From 52cc4f2f6283b3b9a28ea81df1d127190fa28db8 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Mon, 22 Sep 2025 19:35:50 -0400 Subject: kern: ahci: Bring up ports via COMRESET As we rely on resetting the controller via GHC.HR, we'll need to re-establish a link with each implemented port to actually bring them online. Signed-off-by: Ian Moffett --- src/sys/io/ic/ahci.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/src/sys/io/ic/ahci.c b/src/sys/io/ic/ahci.c index 05d59f6..725fbcf 100644 --- a/src/sys/io/ic/ahci.c +++ b/src/sys/io/ic/ahci.c @@ -247,6 +247,75 @@ ahci_port_detach(struct ahci_port *port) kfree(port); } +/* + * Send a COMRESET over the interface to reset a + * specific port. This is typically used after resetting + * the controller. + * + * @port: The target port to reset + */ +static int +ahci_reset_port(struct ahci_port *port) +{ + const char *spdtab[5] = { + "0 Gbit/s (inactive)", + "1.5 Gbit/s", /* SATA gen 1 */ + "3 Gbit/s", /* SATA gen 2 */ + "6 Gbit/s", /* SATA gen 3 */ + "bad" /* Reserved */ + }; + volatile struct hba_port *io; + uint32_t sctl, ssts; + uint8_t ipm, spd; + + if (port == NULL) { + return -EINVAL; + } + + io = port->io; + + /* + * Section 3.3.11 of the AHCI spec states that to transmit a + * COMRESET on the interface, we'll need to hold a value of 1 + * at PxSCTL.DET for a minimum of 1ms. + */ + sctl = mmio_read32(&io->sctl); + sctl &= ~0xF; + sctl |= 1; + mmio_write32(&io->sctl, sctl); + + /* Be generous and give it 3ms */ + clkdev->msleep(3); + + /* Stop COMRESET TX */ + sctl &= ~0xF; + mmio_write32(&io->sctl, sctl); + ahci_port_start(port); + + /* Give it some time to come up */ + for (int i = 0; i < AHCI_TIMEOUT; ++i) { + clkdev->msleep(1); + + /* Get the current port status */ + ssts = mmio_read32(&io->ssts); + ipm = AHCI_PXSSTS_IPM(ssts); + if (ipm == AHCI_IPM_ACTIVE) { + break; + } + } + + if (ipm != AHCI_IPM_ACTIVE) { + dtrace("port %d not active after reset\n", port->portno); + return -EIO; + } + + spd = AHCI_PXSSTS_SPD(ssts); + pr_trace("port %d interface online\n", port->portno); + pr_trace("port %d clocked @ %s\n", port->portno, spdtab[spd]); + ahci_port_stop(port); + return 0; +} + /* * Initialize a specific port on the HBA as per section * 10.1.2 of the AHCI specification @@ -263,7 +332,6 @@ ahci_init_port(struct ahci_hba *hba, struct ahci_port *port) volatile struct hba_port *regs; struct ahci_cmd_hdr *cmdlist; uint32_t cmd, lo, hi; - uint32_t sig; void *va; int error; @@ -271,14 +339,9 @@ ahci_init_port(struct ahci_hba *hba, struct ahci_port *port) return -EINVAL; } - if ((error = ahci_port_stop(port)) < 0) { - return error; - } - regs = port->io; - sig = mmio_read32(®s->sig); - if (sig == ATAPI_SIG) { - return -ENOTSUP; + if ((error = ahci_reset_port(port)) < 0) { + return error; } va = dma_alloc_pg(1); @@ -336,6 +399,7 @@ ahci_init_ports(struct ahci_hba *hba) uint32_t pi, nbits; int error; + pr_trace("bringing up ports...\n"); pi = hba->pi; for (int i = 0; i < hba->nport; ++i) { if (!ISSET(pi, BIT(i))) { -- cgit v1.2.3