diff options
author | Ian Moffett <ian@osmora.org> | 2025-09-22 13:14:11 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-09-22 13:21:27 -0400 |
commit | 43a29889202e63369e52e13939c59dae28bf199a (patch) | |
tree | 4a71743dc9604dbdcc40e141da47be302e8297ae | |
parent | 1e3f7e5ac3beb6f5952a561563e62cb34207eae1 (diff) |
kern: ahci: Add initial port bring-up logic
This commit introduces this initial bring-up logic for ports on the HBA.
We have allocated the command headers and each FIS RX area for each
respective command slot. More work to be done but this lays the
groundwork
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r-- | src/sys/include/io/ic/ahcivar.h | 4 | ||||
-rw-r--r-- | src/sys/io/ic/ahci.c | 100 |
2 files changed, 102 insertions, 2 deletions
diff --git a/src/sys/include/io/ic/ahcivar.h b/src/sys/include/io/ic/ahcivar.h index c3cb70d..b13b6a3 100644 --- a/src/sys/include/io/ic/ahcivar.h +++ b/src/sys/include/io/ic/ahcivar.h @@ -56,11 +56,15 @@ struct ahci_hba { * * @parent: Parent HBA this port belongs to * @io: Port register space + * @cmdlist: Command list base + * @fis_rx: FIS recieve area * @portno: Port number */ struct ahci_port { volatile struct ahci_hba *parent; volatile struct hba_port *io; + dma_addr_t cmdlist; + dma_addr_t fis_rx; uint32_t portno; TAILQ_ENTRY(ahci_port) link; }; diff --git a/src/sys/io/ic/ahci.c b/src/sys/io/ic/ahci.c index 6cc7104..aecef80 100644 --- a/src/sys/io/ic/ahci.c +++ b/src/sys/io/ic/ahci.c @@ -41,6 +41,7 @@ #include <io/pci/bar.h> #include <io/ic/ahciregs.h> #include <io/ic/ahcivar.h> +#include <io/dma/alloc.h> #include <os/kalloc.h> #include <os/module.h> #include <os/clkdev.h> @@ -148,6 +149,48 @@ ahci_port_stop(struct ahci_port *port) } /* + * Bring up an AHCI port, start the command list engine and + * FIS receive engine. + * + * @port: Port to bring up + * + * Returns zero if the port has been brought up successfully, + * otherwise a less than zero value + */ +static int +ahci_port_start(struct ahci_port *port) +{ + volatile struct hba_port *io = port->io; + uint32_t cmd, mask; + int error; + + if (port == NULL) { + return -EINVAL; + } + + /* Don't start if already started */ + mask = AHCI_PXCMD_FR | AHCI_PXCMD_CR; + cmd = mmio_read32(&io->cmd); + if (ISSET(cmd, mask)) { + dtrace("port %d already started\n", port->portno); + return 0; + } + + /* Bring the port up */ + cmd |= (AHCI_PXCMD_FRE | AHCI_PXCMD_ST); + mmio_write32(&io->cmd, cmd); + + /* Wait until everything is up and running */ + error = ahci_poll32(&io->cmd, mask, true); + if (error < 0) { + pr_trace("timed out starting port %d\n", port->portno); + return error; + } + + return 0; +} + +/* * Perform a full HBA reset by using the HR bit * within the GHC register. * @@ -218,13 +261,66 @@ static int ahci_init_port(struct ahci_hba *hba, struct ahci_port *port) { volatile struct hba_port *regs; - uint32_t cmd; + struct ahci_cmd_hdr *cmdlist; + uint32_t cmd, lo, hi; + uint32_t sig; + void *va; + int error; if (hba == NULL || port == NULL) { return -EINVAL; } - ahci_port_stop(port); + if ((error = ahci_port_stop(port)) < 0) { + return error; + } + + regs = port->io; + sig = mmio_read32(®s->sig); + if (sig == ATAPI_SIG) { + return -ENOTSUP; + } + + va = dma_alloc_pg(1); + port->cmdlist = dma_get_pa(va); + + /* Program the command list in */ + lo = port->cmdlist & 0xFFFFFFFF; + hi = (port->cmdlist >> 32) & 0xFFFFFFFF; + mmio_write32(®s->clb, lo); + mmio_write32(®s->clbu, hi); + + /* Set up each command slot */ + cmdlist = dma_get_va(port->cmdlist); + for (int i = 0; i < hba->nslots; ++i) { + va = dma_alloc_pg(1); + if (va == 0) { + cmdlist[i].prdtl = 0; + continue; + } + + /* Allocate H2D FIS area */ + cmdlist[i].prdtl = 1; + cmdlist[i].ctba = dma_get_pa(va); + } + + /* Allocate FIS recieve area */ + va = dma_alloc_pg(1); + port->fis_rx = dma_get_pa(va); + + /* Program FIS recieve area for port */ + lo = port->fis_rx & 0xFFFFFFFF; + hi = (port->fis_rx >> 32) & 0xFFFFFFFF; + mmio_write32(®s->fb, lo); + mmio_write32(®s->fbu, hi); + + /* Clear errors and bring up the port */ + mmio_write32(®s->serr, 0xFFFFFFFF); + error = ahci_port_start(port); + if (error < 0) { + return error; + } + TAILQ_INSERT_TAIL(&portlist, port, link); return 0; } |