From 3995e22535551eda05c64872a5c6e04c7581adb7 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Wed, 7 May 2025 17:51:56 -0400 Subject: kernel: ahci: Implement initial HBA port logic - Implement logic to scan the HBA for ports - Implement logic to stop HBA ports - Add ahci_init_port() stub Signed-off-by: Ian Moffett --- sys/dev/ic/ahci.c | 100 ++++++++++++++++++++++++++++++++++++++++++ sys/include/dev/ic/ahciregs.h | 2 + sys/include/dev/ic/ahcivar.h | 30 +++++++++++++ 3 files changed, 132 insertions(+) diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c index ec8b8fa..f1603c0 100644 --- a/sys/dev/ic/ahci.c +++ b/sys/dev/ic/ahci.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -114,12 +115,86 @@ ahci_hba_reset(struct ahci_hba *hba) return 0; } +/* + * Stop an HBA port's command list and FIS + * engine. + */ +static int +hba_port_stop(struct hba_port *port) +{ + uint32_t cmd, tmp; + + /* Stop the port */ + cmd = mmio_read32(&port->cmd); + cmd &= ~(AHCI_PXCMD_ST | AHCI_PXCMD_FRE); + mmio_write32(&port->cmd, cmd); + + /* + * The spec states that once the port is stopped, + * PxCMD.CR and PxCMD.FR become unset + */ + tmp = AHCI_PXCMD_FR | AHCI_PXCMD_CR; + if (ahci_poll_reg(&port->cmd, tmp, false) < 0) { + return -EAGAIN; + } + + return 0; +} + +/* + * Initialize a drive on an HBA port + * + * @hba: HBA descriptor + * @portno: Port number + */ +static int +ahci_init_port(struct ahci_hba *hba, uint32_t portno) +{ + struct hba_memspace *abar = hba->io; + struct hba_port *port; + int error; + + pr_trace("found device @ port %d\n", portno); + port = &abar->ports[portno]; + + if ((error = hba_port_stop(port)) < 0) { + pr_trace("failed to stop port %d\n", portno); + return error; + } + + /* TODO */ + return 0; +} + +/* + * Scan the HBA for implemented ports + */ +static int +ahci_hba_scan(struct ahci_hba *hba) +{ + struct hba_memspace *abar = hba->io; + uint32_t pi, i = 0; + + pi = mmio_read32(&abar->pi); + while (pi != 0) { + if ((pi & 1) != 0) { + ahci_init_port(hba, i); + } + + ++i; + pi >>= 1; + } + + return 0; +} + static int ahci_hba_init(struct ahci_hba *hba) { struct hba_memspace *abar = hba->io; int error; uint32_t tmp; + uint32_t cap, pi; /* * God knows what state the HBA is in by the time @@ -132,6 +207,11 @@ ahci_hba_init(struct ahci_hba *hba) } pr_trace("successfully performed a hard reset\n"); + cap = mmio_read32(&abar->cap); + hba->maxports = AHCI_CAP_NP(cap); + hba->nslots = AHCI_CAP_NCS(cap); + hba->ems = AHCI_CAP_EMS(cap); + hba->sal = AHCI_CAP_SAL(cap); /* * The HBA provides backwards compatibility with @@ -142,6 +222,26 @@ ahci_hba_init(struct ahci_hba *hba) tmp = mmio_read32(&abar->ghc); tmp |= AHCI_GHC_AE; mmio_write32(&abar->ghc, tmp); + + /* + * CAP.NCS reports the maximum number of ports the + * HBA silicon supports but a lot of hardware will + * not implement the full number of ports supported. + * + * the `PI' register is a bit-significant register + * used to determine which ports are implemented, + * therefore we can just count how many bits are + * set in this register and that would be how many + * ports are implemented total. + */ + pi = mmio_read32(&abar->pi); + hba->nports = popcnt(pi); + pr_trace("hba implements %d ports\n", hba->nports); + + if ((error = ahci_hba_scan(hba)) != 0) { + return error; + } + return 0; } diff --git a/sys/include/dev/ic/ahciregs.h b/sys/include/dev/ic/ahciregs.h index 4a4dc65..973c761 100644 --- a/sys/include/dev/ic/ahciregs.h +++ b/sys/include/dev/ic/ahciregs.h @@ -122,6 +122,8 @@ struct hba_memspace { */ #define AHCI_CAP_NP(CAP) (CAP & 0x1F) /* Number of ports */ #define AHCI_CAP_NCS(CAP) ((CAP >> 8) & 0x1F) /* Number of command slots */ +#define AHCI_CAP_EMS(CAP) ((CAP >> 6) & 1) /* Enclosure management support */ +#define AHCI_CAP_SAL(CAP) ((CAP >> 25) & 1) /* Supports activity LED */ /* * Device detection (DET) and Interface power diff --git a/sys/include/dev/ic/ahcivar.h b/sys/include/dev/ic/ahcivar.h index 0d307cd..cc6d346 100644 --- a/sys/include/dev/ic/ahcivar.h +++ b/sys/include/dev/ic/ahcivar.h @@ -30,10 +30,40 @@ #ifndef _IC_AHCIVAR_H_ #define _IC_AHCIVAR_H_ +#include +#include #include +/* + * AHCI Host Bus Adapter + * + * @io: HBA MMIO + * @maxports: Max number of HBA ports + * @nports: Number of implemented HBA ports. + * @nslots: Number of command slots + * @ems: Enclosure management support + * @sal: Supports activity LED + */ struct ahci_hba { struct hba_memspace *io; + uint32_t maxports; + uint32_t nports; + uint32_t nslots; + uint8_t ems : 1; + uint8_t sal : 1; +}; + +/* + * A device attached to a physical HBA port. + * + * @io: Memory mapped port registers + * @hba: HBA descriptor + * @dev: Device minor number. + */ +struct hba_device { + struct hba_port *io; + struct ahci_hba *hba; + dev_t dev; }; #define AHCI_TIMEOUT 500 /* In ms */ -- cgit v1.2.3