diff options
author | Ian Moffett <ian@osmora.org> | 2025-09-21 01:23:02 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-09-21 01:23:02 -0400 |
commit | 5e60c3277f04275bd5c6c68d42f390b82f0725bb (patch) | |
tree | 6d464282a6837f71173e183899d12ee23208d353 /src/sys/io/ic | |
parent | 111bad63b3eae1d7fdd748fd57fe7742c93658a0 (diff) |
kern: ahci: Add AHCI host bus adapter reset impl
This commit introduces this initial ahci_init() logic by resetting HBA
state on startup.
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'src/sys/io/ic')
-rw-r--r-- | src/sys/io/ic/ahci.c | 137 |
1 files changed, 136 insertions, 1 deletions
diff --git a/src/sys/io/ic/ahci.c b/src/sys/io/ic/ahci.c index 4515b9d..3d0e890 100644 --- a/src/sys/io/ic/ahci.c +++ b/src/sys/io/ic/ahci.c @@ -35,8 +35,14 @@ #include <sys/types.h> #include <sys/syslog.h> #include <sys/cdefs.h> +#include <sys/errno.h> #include <io/pci/pci.h> +#include <io/pci/bar.h> +#include <io/ic/ahciregs.h> +#include <io/ic/ahcivar.h> #include <os/module.h> +#include <os/clkdev.h> +#include <os/mmio.h> /* * Represents the PCI advocation descriptor for this @@ -44,6 +50,107 @@ */ static struct pci_adv driver; +/* Various other state */ +static struct pci_device dev; +static struct clkdev *clkdev; +static struct ahci_hba root_hba; + +/* + * Poll register to have 'bits' set/unset. + * + * @reg: Register to poll. + * @bits: Bits to be checked. + * @pollset: True to poll as set. + * + * Returns zero if the register updated in time, otherwise a less + * than zero value upon failure. + */ +static int +ahci_poll32(volatile uint32_t *reg, uint32_t bits, bool pollset) +{ + size_t usec_start, usec; + size_t elapsed_msec; + uint32_t val; + bool tmp; + + usec_start = clkdev->get_time_usec(); + for (;;) { + val = mmio_read32(reg); + tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits); + + usec = clkdev->get_time_usec(); + elapsed_msec = (usec - usec_start) / 1000; + + /* If tmp is set, the register updated in time */ + if (tmp) { + break; + } + + /* Exit with an error if we timeout */ + if (elapsed_msec > AHCI_TIMEOUT) { + return -ETIME; + } + } + + return 0; +} + +/* + * Perform a full HBA reset by using the HR bit + * within the GHC register. + * + * See section 10.4.3 of the AHCI spec for more + * information. + */ +static int +ahci_hba_reset(struct ahci_hba *hba) +{ + volatile struct hba_memspace *io = hba->io; + uint32_t ghc; + int error; + + /* Begin the reset */ + ghc = mmio_read32(&io->ghc); + ghc |= AHCI_GHC_HR; + mmio_write32(&io->ghc, ghc); + + /* Wait until it is done */ + error = ahci_poll32(&io->ghc, AHCI_GHC_HR, false); + if (error < 0) { + printf("ahci: HBA reset timed out\n"); + return error; + } + + return 0; +} + +/* + * Put the HBA as well as its devices in an initialized + * state so that they may be used for operation. + */ +static int +ahci_hba_init(struct ahci_hba *hba) +{ + volatile struct hba_memspace *io = hba->io; + uint32_t ghc; + int error; + + /* Ensure the host controller is AHCI aware */ + ghc = mmio_read32(&io->ghc); + ghc |= AHCI_GHC_AE; + mmio_write32(&io->ghc, ghc); + + /* + * We cannot be so certain what state the BIOS or whatever + * firmware left the host controller in, therefore the HBA + * will require a reset so we can have it in a known state. + */ + if ((error = ahci_hba_reset(hba)) < 0) { + return error; + } + return 0; +} + /* * Initialize only the AHCI driver's state rather than * the hardware it covers. This is used so we can advertise @@ -52,12 +159,22 @@ static struct pci_adv driver; static int ahci_init(struct module *modp) { + uint16_t clkmask; int error; + clkmask = CLKDEV_MSLEEP | CLKDEV_GET_USEC; + error = clkdev_get(clkmask, &clkdev); + if (error < 0) { + printf("ahci_init: could not get clkdev\n"); + return error; + } + if ((error = pci_advoc(&driver)) < 0) { printf("ahci_init: failed to advocate for HBA\n"); return error; } + + root_hba.io = NULL; return 0; } @@ -68,8 +185,26 @@ ahci_init(struct module *modp) static int ahci_attach(struct pci_adv *adv) { + struct bus_space bs; + int error; + + /* Only call once */ + if (root_hba.io != NULL) { + return -EIO; + } + + dev = adv->lookup; printf("ahci: detected AHCI controller\n"); - return 0; + + /* Map ABAR */ + error = pci_map_bar(&dev, 5, &bs); + if (error < 0) { + printf("ahci: failed to map bar 5 (error=%d)\n", error); + return error; + } + + root_hba.io = (void *)bs.va_base; + return ahci_hba_init(&root_hba); } static struct pci_adv driver = { |