diff options
author | Ian Moffett <ian@osmora.org> | 2025-09-22 19:35:50 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-09-22 19:40:01 -0400 |
commit | 52cc4f2f6283b3b9a28ea81df1d127190fa28db8 (patch) | |
tree | 21cae82e2943f4ad3e06ef2d59e2c474a771d4b2 /src/sys/io | |
parent | fa93409eebef651e7c1a79629751d8625aff1493 (diff) |
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 <ian@osmora.org>
Diffstat (limited to 'src/sys/io')
-rw-r--r-- | src/sys/io/ic/ahci.c | 80 |
1 files 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 @@ -248,6 +248,75 @@ ahci_port_detach(struct ahci_port *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))) { |