summaryrefslogtreecommitdiff
path: root/sys/dev/ic/nvme.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ic/nvme.c')
-rw-r--r--sys/dev/ic/nvme.c88
1 files changed, 74 insertions, 14 deletions
diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c
index ac6fb74..c65d7e0 100644
--- a/sys/dev/ic/nvme.c
+++ b/sys/dev/ic/nvme.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team.
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -219,7 +219,7 @@ nvme_stop_ctrl(struct nvme_bar *bar)
mmio_write32(&bar->config, config);
if (nvme_poll_reg(bar, &bar->status, STATUS_RDY, false) < 0) {
- pr_error("Controller reset timeout\n");
+ pr_error("controller reset timeout\n");
return -ETIME;
}
@@ -246,7 +246,7 @@ nvme_start_ctrl(struct nvme_bar *bar)
mmio_write32(&bar->config, config);
if (nvme_poll_reg(bar, &bar->status, STATUS_RDY, true) < 0) {
- pr_error("Controller startup timeout\n");
+ pr_error("controller startup timeout\n");
return -ETIME;
}
@@ -292,7 +292,7 @@ nvme_poll_submit_cmd(struct nvme_queue *q, struct nvme_cmd cmd)
/* Check for timeout */
if (spins > 5) {
- pr_error("Hang while polling phase bit, giving up\n");
+ pr_error("hang while polling phase bit, giving up\n");
return -ETIME;
}
@@ -309,6 +309,35 @@ nvme_poll_submit_cmd(struct nvme_queue *q, struct nvme_cmd cmd)
return 0;
}
+/*
+ * Get NVMe log page
+ *
+ * @ctrl: NVMe controller to target
+ * @buf: Data buffer
+ * @lid: Log identifier
+ * @len: Length (in bytes)
+ */
+static int
+nvme_get_logpage(struct nvme_ctrl *ctrl, void *buf, uint8_t lid, uint32_t len)
+{
+ struct nvme_cmd cmd = {0};
+ struct nvme_get_logpage_cmd *cmdp;
+
+ if (!is_4k_aligned(buf)) {
+ return -1;
+ }
+
+ cmdp = &cmd.get_logpage;
+ cmdp->opcode = NVME_OP_GET_LOGPAGE;
+ cmdp->nsid = 0xFFFFFFFF;
+ cmdp->lid = lid;
+ cmdp->numdl = len / 4;
+ cmdp->numdu = 0;
+ cmdp->prp1 = VIRT_TO_PHYS(buf);
+ cmdp->prp2 = 0;
+ return nvme_poll_submit_cmd(&ctrl->adminq, cmd);
+}
+
static int
nvme_identify(struct nvme_ctrl *ctrl, void *buf, uint32_t nsid, uint8_t cns)
{
@@ -350,9 +379,9 @@ nvme_log_ctrl_id(struct nvme_id *id)
sn[i] = id->sn[i];
}
- pr_trace("Model number: %s\n", mn);
- pr_trace("Serial number: %s\n", sn);
- pr_trace("Firmware revision: %s\n", fr);
+ pr_trace("model number: %s\n", mn);
+ pr_trace("serial number: %s\n", sn);
+ pr_trace("firmware revision: %s\n", fr);
}
/*
@@ -425,7 +454,7 @@ nvme_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
*/
ns = nvme_get_ns(dev);
if (__unlikely(ns == NULL))
- return -EIO;
+ return -ENODEV;
/* Calculate the block count and offset */
block_count = ALIGN_UP(sio->len, ns->lba_bsize);
@@ -470,6 +499,12 @@ nvme_dev_read(dev_t dev, struct sio_txn *sio, int flags)
return nvme_dev_rw(dev, sio, false);
}
+static int
+nvme_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ return nvme_dev_rw(dev, sio, true);
+}
+
/*
* Initializes an NVMe namespace.
*
@@ -543,6 +578,7 @@ nvme_init_ctrl(struct nvme_bar *bar)
uint16_t mqes;
uint8_t *nsids;
struct nvme_ctrl ctrl = { .bar = bar };
+ struct nvme_smart_data *smart;
struct nvme_queue *adminq;
struct nvme_id *id;
@@ -566,14 +602,21 @@ nvme_init_ctrl(struct nvme_bar *bar)
return error;
}
+ smart = dynalloc_memalign(sizeof(*smart), 0x1000);
+ if (smart == NULL) {
+ return -ENOMEM;
+ }
+
id = dynalloc_memalign(sizeof(*id), 0x1000);
if (id == NULL) {
+ dynfree(smart);
return -ENOMEM;
}
nsids = dynalloc_memalign(0x1000, 0x1000);
if (nsids == NULL) {
dynfree(id);
+ dynfree(smart);
return -ENOMEM;
}
@@ -581,6 +624,18 @@ nvme_init_ctrl(struct nvme_bar *bar)
nvme_log_ctrl_id(id);
nvme_identify(&ctrl, nsids, 0, ID_CNS_NSID_LIST);
+ /*
+ * Attempt to read some SMART data but don't bother
+ * if it fails in any way.
+ */
+ error = nvme_get_logpage(&ctrl, smart, NVME_LOGPAGE_SMART, sizeof(*smart));
+ if (error == 0) {
+ if (smart->temp != 0 && smart->temp > 283)
+ pr_trace("temp: %d K\n", smart->temp);
+
+ pr_trace("%d%% used\n", smart->percent_used);
+ }
+
ctrl.sqes = id->sqes >> 4;
ctrl.cqes = id->cqes >> 4;
@@ -601,12 +656,13 @@ nvme_init_ctrl(struct nvme_bar *bar)
}
if (nvme_init_ns(&ctrl, nsids[i]) != 0) {
- pr_error("Failed to initialize NSID %d\n", nsids[i]);
+ pr_error("failed to initialize NSID %d\n", nsids[i]);
}
}
dynfree(id);
dynfree(nsids);
+ dynfree(smart);
return 0;
}
@@ -625,21 +681,25 @@ nvme_init(void)
return -ENODEV;
}
+ pr_trace("NVMe storage ctrl <hba? at pci%d:%x.%x.%d>\n",
+ nvme_dev->bus, nvme_dev->device_id, nvme_dev->func,
+ nvme_dev->slot);
+
/* Try to request a general purpose timer */
if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
- pr_error("Failed to fetch general purpose timer\n");
+ pr_error("failed to fetch general purpose timer\n");
return -ENODEV;
}
/* Ensure it has get_time_usec() */
if (tmr.get_time_usec == NULL) {
- pr_error("General purpose timer has no get_time_usec()\n");
+ pr_error("general purpose timer has no get_time_usec()\n");
return -ENODEV;
}
/* We also need msleep() */
if (tmr.msleep == NULL) {
- pr_error("General purpose timer has no msleep()\n");
+ pr_error("general purpose timer has no msleep()\n");
return -ENODEV;
}
@@ -655,7 +715,7 @@ nvme_init(void)
static struct bdevsw nvme_bdevsw = {
.read = nvme_dev_read,
- .write = nowrite
+ .write = nvme_dev_write
};
-DRIVER_EXPORT(nvme_init);
+DRIVER_EXPORT(nvme_init, "nvme");