summaryrefslogtreecommitdiff
path: root/sys/dev/ic
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ic')
-rw-r--r--sys/dev/ic/ahci.c184
-rw-r--r--sys/dev/ic/nvme.c2
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);