summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-05-07 17:51:56 -0400
committerIan Moffett <ian@osmora.org>2025-05-07 17:55:22 -0400
commit3995e22535551eda05c64872a5c6e04c7581adb7 (patch)
treeded627ef95d528c2fddbf1d8bffe0251a05a8b2e
parent256407ae283fa132873de685731070b44794c527 (diff)
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 <ian@osmora.org>
-rw-r--r--sys/dev/ic/ahci.c100
-rw-r--r--sys/include/dev/ic/ahciregs.h2
-rw-r--r--sys/include/dev/ic/ahcivar.h30
3 files changed, 132 insertions, 0 deletions
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 <sys/driver.h>
#include <sys/errno.h>
#include <sys/syslog.h>
+#include <sys/bitops.h>
#include <sys/mmio.h>
#include <dev/pci/pci.h>
#include <dev/timer.h>
@@ -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 <sys/param.h>
+#include <sys/types.h>
#include <dev/ic/ahciregs.h>
+/*
+ * 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 */