diff options
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/ahci.c | 184 | ||||
-rw-r--r-- | sys/dev/ic/nvme.c | 2 |
2 files changed, 124 insertions, 62 deletions
diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c index 23c68a2..5dbf4a7 100644 --- a/sys/dev/ic/ahci.c +++ b/sys/dev/ic/ahci.c @@ -46,6 +46,7 @@ #include <fs/ctlfs.h> #include <vm/dynalloc.h> #include <vm/physmem.h> +#include <machine/cdefs.h> #include <string.h> #define pr_trace(fmt, ...) kprintf("ahci: " fmt, ##__VA_ARGS__) @@ -56,6 +57,8 @@ static struct bdevsw ahci_bdevsw; static struct hba_device *devs; static struct pci_device *ahci_dev; static struct timer tmr; +static struct ahci_hba g_hba; +static struct driver_var __driver_var; /* * Poll register to have 'bits' set/unset. @@ -95,6 +98,18 @@ ahci_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset) return 0; } +static struct hba_device * +ahci_get_dev(dev_t dev) +{ + for (int i = 0; i < devs_max; ++i) { + if (devs[i].dev == dev) { + return &devs[i]; + } + } + + return NULL; +} + /* * Allocate a command slot for a port on * the HBA. @@ -104,7 +119,6 @@ ahci_alloc_cmdslot(struct ahci_hba *hba, struct hba_port *port) { uint32_t slotlist; - slotlist = port->ci | port->sact; slotlist = mmio_read32(&port->ci); slotlist |= mmio_read32(&port->sact); @@ -316,22 +330,24 @@ hba_port_chkerr(struct hba_port *port) static int hba_port_reset(struct ahci_hba *hba, struct hba_port *port) { - uint32_t sctl, ssts; - uint8_t det, ipm; - int error; + uint32_t sctl, ssts, cmd; + uint8_t det, ipm, spd; + uint32_t elapsed = 0; + + sctl = mmio_read32(&port->sctl); /* - * The port must not be in an idle state when a - * COMRESET is sent over the interface as some - * chipsets do not know how to handle this... - * - * After bringing up the port, send a COMRESET - * over the interface for roughly ~2ms. + * Transmit a COMRESET to the device. If the HBA + * supports staggered spin-up, we'll need to set + * the PxCMD.SUD bit as well. */ - hba_port_start(port); - sctl = mmio_read32(&port->sctl); sctl = (sctl & ~0x0F) | AHCI_DET_COMRESET; mmio_write32(&port->sctl, sctl); + if (hba->sss) { + cmd = mmio_read32(&port->cmd); + cmd |= AHCI_PXCMD_SUD; + mmio_write32(&port->cmd, cmd); + } /* * Wait for the link to become reestablished @@ -341,34 +357,50 @@ hba_port_reset(struct ahci_hba *hba, struct hba_port *port) sctl &= ~AHCI_DET_COMRESET; mmio_write32(&port->sctl, sctl); - /* - * Now we'll need to grab some power management - * and detection flags as the port must have - * a device present along with an active - * interface. - */ - ssts = mmio_read32(&port->ssts); - det = AHCI_PXSCTL_DET(ssts); - ipm = AHCI_PXSSTS_IPM(ssts); + for (;;) { + if (elapsed >= AHCI_TIMEOUT) { + break; + } + ssts = mmio_read32(&port->ssts); + det = AHCI_PXSSTS_DET(ssts); + if (det == AHCI_DET_COMM) { + break; + } - /* If there is no device, fake success */ - if (det == AHCI_DET_NULL) { - return 0; + tmr.msleep(10); + elapsed += 10; } + ipm = AHCI_PXSSTS_IPM(ssts); + spd = AHCI_PXSSTS_SPD(ssts); + + if (det == AHCI_DET_PRESENT) { + pr_error("SATA link timeout\n"); + return -EAGAIN; + } if (det != AHCI_DET_COMM) { - pr_trace("failed to establish link\n"); return -EAGAIN; } + /* + * Ensure the interface is in an active + * state. + */ if (ipm != AHCI_IPM_ACTIVE) { - pr_trace("device interface not active\n"); + pr_error("device interface not active\n"); return -EAGAIN; } - if ((error = hba_port_stop(port)) < 0) { - pr_trace("failed to stop port\n"); - return error; + switch (spd) { + case AHCI_SPD_GEN1: + pr_trace("SATA link rate @ ~1.5 Gb/s\n"); + break; + case AHCI_SPD_GEN2: + pr_trace("SATA link rate @ ~3 Gb/s\n"); + break; + case AHCI_SPD_GEN3: + pr_trace("SATA link rate @ ~6 Gb/s\n"); + break; } return 0; @@ -417,12 +449,14 @@ ahci_submit_cmd(struct ahci_hba *hba, struct hba_port *port, uint8_t slot) * SATA device. */ static int -ahci_identify(struct ahci_hba *hba, struct hba_port *port) +ahci_identify(struct ahci_hba *hba, struct hba_device *dp) { paddr_t base, buf; + struct hba_port *port; struct ahci_cmd_hdr *cmdhdr; struct ahci_cmdtab *cmdtbl; struct ahci_fis_h2d *fis; + uint16_t *p; int cmdslot, status; buf = vm_alloc_frame(1); @@ -431,6 +465,7 @@ ahci_identify(struct ahci_hba *hba, struct hba_port *port) return -ENOMEM; } + port = dp->io; cmdslot = ahci_alloc_cmdslot(hba, port); if (cmdslot < 0) { pr_trace("failed to alloc cmdslot\n"); @@ -462,6 +497,9 @@ ahci_identify(struct ahci_hba *hba, struct hba_port *port) } ahci_dump_identity(PHYS_TO_VIRT(buf)); + p = (uint16_t *)PHYS_TO_VIRT(buf); + dp->nlba = (p[61] << 16) | p[60]; + pr_trace("max block size: %d\n", dp->nlba); done: vm_free_frame(buf, 1); return status; @@ -621,7 +659,6 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write) const size_t BSIZE = 512; struct sio_txn wr_sio; struct hba_device *devp; - struct ahci_hba *hba; size_t block_count, len; off_t block_off, read_off; char *buf; @@ -633,11 +670,11 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write) if (sio->len == 0 || sio->buf == NULL) { return -EINVAL; } - if (dev >= devs_max) { + if (dev > devs_max) { return -ENODEV; } - devp = &devs[dev]; + devp = ahci_get_dev(dev); if (__unlikely(devp == NULL)) { return -ENODEV; } @@ -667,14 +704,14 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write) wr_sio.buf = buf; wr_sio.len = block_count; wr_sio.offset = block_off; - status = ahci_sata_rw(hba, devp, &wr_sio, write); + status = ahci_sata_rw(&g_hba, devp, &wr_sio, write); if (status == 0 && !write) { read_off = sio->offset & (BSIZE - 1); memcpy(sio->buf, buf + read_off, sio->len); } dynfree(buf); - return status; + return sio->len; } /* @@ -683,10 +720,46 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write) static int ahci_dev_read(dev_t dev, struct sio_txn *sio, int flags) { + while (DRIVER_DEFERRED()) { + md_pause(); + } + return sata_dev_rw(dev, sio, false); } /* + * Device interface write + */ +static int +ahci_dev_write(dev_t dev, struct sio_txn *sio, int flags) +{ + while (DRIVER_DEFERRED()) { + md_pause(); + } + + return sata_dev_rw(dev, sio, true); +} + +/* + * Device interface number of blocks + */ +static int +ahci_dev_bsize(dev_t dev) +{ + struct hba_device *dp; + + while (DRIVER_DEFERRED()) { + md_pause(); + } + + if ((dp = ahci_get_dev(dev)) == NULL) { + return -ENODEV; + } + + return dp->nlba; +} + +/* * Initialize a drive on an HBA port * * @hba: HBA descriptor @@ -701,27 +774,19 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) struct hba_device *dp; struct ctlfs_dev dev; size_t clen, pagesz; - uint32_t lo, hi, ssts; - uint8_t det; + uint32_t lo, hi, sig; paddr_t fra, cmdlist, tmp; int error; pagesz = DEFAULT_PAGESIZE; port = &abar->ports[portno]; - /* Is anything on the port? */ - ssts = mmio_read32(&port->ssts); - det = AHCI_PXSCTL_DET(ssts); - switch (det) { - case AHCI_DET_NULL: - /* No device attached */ - return 0; - case AHCI_DET_PRESENT: - if ((error = hba_port_reset(hba, port)) < 0) { - pr_trace("failed to reset port %d\n", portno); - return error; - } - break; + if ((error = hba_port_reset(hba, port)) < 0) { + return error; + } + sig = mmio_read32(&port->sig); + if (sig == ATAPI_SIG) { + return -ENOTSUP; } pr_trace("found device @ port %d\n", portno); @@ -755,8 +820,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) } dp->fra = PHYS_TO_VIRT(fra); - memset(dp->cmdlist, 0, clen * pagesz); - memset(dp->fra, 0, pagesz); /* Write the command list */ lo = cmdlist & 0xFFFFFFFF; @@ -775,7 +838,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) tmp = vm_alloc_frame(1); dp->cmdlist[i].prdtl = 1; dp->cmdlist[i].ctba = tmp; - memset(PHYS_TO_VIRT(tmp), 0, pagesz); } mmio_write32(&port->serr, 0xFFFFFFFF); @@ -790,7 +852,7 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) return error; } - ahci_identify(hba, port); + ahci_identify(hba, dp); if (hba->major == 0) { hba->major = dev_alloc_major(); @@ -811,7 +873,7 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) dev.devname = devname; dev.ops = &g_sata_bsize_ops; ctlfs_create_entry("bsize", &dev); - return devfs_create_entry(devname, hba->major, dp->dev, 0444); + return devfs_create_entry(devname, hba->major, dp->dev, 060444); } /* @@ -919,10 +981,9 @@ ahci_init(void) { struct pci_lookup lookup; int status; - struct ahci_hba hba; void *abar_vap = NULL; - hba.major = 0; + g_hba.major = 0; lookup.pci_class = 0x01; lookup.pci_subclass = 0x06; @@ -968,14 +1029,15 @@ ahci_init(void) } ahci_init_pci(); - hba.io = (struct hba_memspace*)abar_vap; - ahci_hba_init(&hba); + g_hba.io = (struct hba_memspace*)abar_vap; + ahci_hba_init(&g_hba); return 0; } static struct bdevsw ahci_bdevsw = { .read = ahci_dev_read, - .write = nowrite + .write = ahci_dev_write, + .bsize = ahci_dev_bsize }; -DRIVER_EXPORT(ahci_init); +DRIVER_DEFER(ahci_init); diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c index 704fdec..822b085 100644 --- a/sys/dev/ic/nvme.c +++ b/sys/dev/ic/nvme.c @@ -662,4 +662,4 @@ static struct bdevsw nvme_bdevsw = { .write = nowrite }; -DRIVER_EXPORT(nvme_init); +DRIVER_DEFER(nvme_init); |