1*fe5224a3SRobert Mustacchi /*
2*fe5224a3SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*fe5224a3SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*fe5224a3SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*fe5224a3SRobert Mustacchi * 1.0 of the CDDL.
6*fe5224a3SRobert Mustacchi *
7*fe5224a3SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*fe5224a3SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*fe5224a3SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*fe5224a3SRobert Mustacchi */
11*fe5224a3SRobert Mustacchi
12*fe5224a3SRobert Mustacchi /*
13*fe5224a3SRobert Mustacchi * Copyright 2026 Oxide Computer Company
14*fe5224a3SRobert Mustacchi */
15*fe5224a3SRobert Mustacchi
16*fe5224a3SRobert Mustacchi /*
17*fe5224a3SRobert Mustacchi * libnvme pieces specific to Sandisk.
18*fe5224a3SRobert Mustacchi *
19*fe5224a3SRobert Mustacchi * Sandisk was spun out of WDC. The controller family changed in approximately
20*fe5224a3SRobert Mustacchi * the SN861 family versus the prior SNx40 and SNx50 parts. We use this as a
21*fe5224a3SRobert Mustacchi * somewhat arbitrary delimiter between the two devices and thus add
22*fe5224a3SRobert Mustacchi * vendor-specific commands starting with this family with the 'sandisk' name.
23*fe5224a3SRobert Mustacchi */
24*fe5224a3SRobert Mustacchi
25*fe5224a3SRobert Mustacchi #include <sys/sysmacros.h>
26*fe5224a3SRobert Mustacchi #include <sys/nvme/wdc.h>
27*fe5224a3SRobert Mustacchi
28*fe5224a3SRobert Mustacchi #include "libnvme_impl.h"
29*fe5224a3SRobert Mustacchi
30*fe5224a3SRobert Mustacchi /*
31*fe5224a3SRobert Mustacchi * This is a default timeout (seconds) that we think should be good enough for
32*fe5224a3SRobert Mustacchi * most of these operations. As we expand this, this should become more fine
33*fe5224a3SRobert Mustacchi * grained.
34*fe5224a3SRobert Mustacchi */
35*fe5224a3SRobert Mustacchi static const uint32_t nvme_sndk_timeout = 20;
36*fe5224a3SRobert Mustacchi
37*fe5224a3SRobert Mustacchi static const nvme_vsd_ident_t sandisk_sn861_idents[] = {
38*fe5224a3SRobert Mustacchi {
39*fe5224a3SRobert Mustacchi .nvdi_vid = WDC_PCI_VID,
40*fe5224a3SRobert Mustacchi .nvdi_did = WDC_SN861_DID_U2,
41*fe5224a3SRobert Mustacchi .nvdi_human = "SanDisk DC SN861 U.2",
42*fe5224a3SRobert Mustacchi }, {
43*fe5224a3SRobert Mustacchi .nvdi_vid = WDC_PCI_VID,
44*fe5224a3SRobert Mustacchi .nvdi_did = WDC_SN861_DID_E3,
45*fe5224a3SRobert Mustacchi .nvdi_human = "SanDisk DC SN861 E3.S",
46*fe5224a3SRobert Mustacchi }, {
47*fe5224a3SRobert Mustacchi .nvdi_vid = WDC_PCI_VID,
48*fe5224a3SRobert Mustacchi .nvdi_did = WDC_SN861_DID_E1,
49*fe5224a3SRobert Mustacchi .nvdi_human = "SanDisk DC SN861 E1.S",
50*fe5224a3SRobert Mustacchi }
51*fe5224a3SRobert Mustacchi };
52*fe5224a3SRobert Mustacchi
53*fe5224a3SRobert Mustacchi static const nvme_log_page_info_t *sandisk_sn861_log_pages[] = {
54*fe5224a3SRobert Mustacchi &ocp_log_smart, &ocp_log_errrec, &ocp_log_fwact, &ocp_log_lat,
55*fe5224a3SRobert Mustacchi &ocp_log_devcap, &ocp_log_unsup
56*fe5224a3SRobert Mustacchi };
57*fe5224a3SRobert Mustacchi
58*fe5224a3SRobert Mustacchi static const nvme_vuc_disc_t sndk_sn861_vuc[] = { {
59*fe5224a3SRobert Mustacchi .nvd_short = "sandisk/pci-eye",
60*fe5224a3SRobert Mustacchi .nvd_desc = "per-lane PCI eye diagram",
61*fe5224a3SRobert Mustacchi .nvd_opc = WDC_SN861_VUC_EYE_OPC,
62*fe5224a3SRobert Mustacchi .nvd_dt = NVME_VUC_DISC_IO_OUTPUT,
63*fe5224a3SRobert Mustacchi .nvd_lock = NVME_VUC_DISC_LOCK_NONE
64*fe5224a3SRobert Mustacchi }, {
65*fe5224a3SRobert Mustacchi .nvd_short = "sandisk/hwrev",
66*fe5224a3SRobert Mustacchi .nvd_desc = "print hardware revision",
67*fe5224a3SRobert Mustacchi .nvd_opc = WDC_SN861_VUC_HWREV_OPC,
68*fe5224a3SRobert Mustacchi .nvd_dt = NVME_VUC_DISC_IO_OUTPUT,
69*fe5224a3SRobert Mustacchi .nvd_lock = NVME_VUC_DISC_LOCK_NONE
70*fe5224a3SRobert Mustacchi } };
71*fe5224a3SRobert Mustacchi
72*fe5224a3SRobert Mustacchi const nvme_vsd_t sandisk_sn861 = {
73*fe5224a3SRobert Mustacchi .nvd_ident = sandisk_sn861_idents,
74*fe5224a3SRobert Mustacchi .nvd_nident = ARRAY_SIZE(sandisk_sn861_idents),
75*fe5224a3SRobert Mustacchi .nvd_logs = sandisk_sn861_log_pages,
76*fe5224a3SRobert Mustacchi .nvd_nlogs = ARRAY_SIZE(sandisk_sn861_log_pages),
77*fe5224a3SRobert Mustacchi .nvd_vuc = sndk_sn861_vuc,
78*fe5224a3SRobert Mustacchi .nvd_nvuc = ARRAY_SIZE(sndk_sn861_vuc)
79*fe5224a3SRobert Mustacchi };
80*fe5224a3SRobert Mustacchi
81*fe5224a3SRobert Mustacchi bool
nvme_sndk_pci_eye(nvme_ctrl_t * ctrl,uint8_t lane,void * buf,size_t len)82*fe5224a3SRobert Mustacchi nvme_sndk_pci_eye(nvme_ctrl_t *ctrl, uint8_t lane, void *buf, size_t len)
83*fe5224a3SRobert Mustacchi {
84*fe5224a3SRobert Mustacchi nvme_vuc_req_t *req = NULL;
85*fe5224a3SRobert Mustacchi
86*fe5224a3SRobert Mustacchi if (buf == NULL) {
87*fe5224a3SRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
88*fe5224a3SRobert Mustacchi "encountered invalid eye diagram buffer output pointer: %p",
89*fe5224a3SRobert Mustacchi buf));
90*fe5224a3SRobert Mustacchi }
91*fe5224a3SRobert Mustacchi
92*fe5224a3SRobert Mustacchi if (len < WDC_SN861_VUC_EYE_LEN) {
93*fe5224a3SRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_PCIE_EYE_BUF_RANGE, 0,
94*fe5224a3SRobert Mustacchi "eye diagram buffer output size is too small: found 0x%zx "
95*fe5224a3SRobert Mustacchi "bytes, but need at least 0x%x", len,
96*fe5224a3SRobert Mustacchi WDC_SN861_VUC_EYE_LEN));
97*fe5224a3SRobert Mustacchi }
98*fe5224a3SRobert Mustacchi
99*fe5224a3SRobert Mustacchi if (lane >= 4) {
100*fe5224a3SRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_PCIE_LANE_RANGE, 0,
101*fe5224a3SRobert Mustacchi "invalid PCIe lane %u: must be between 0-3", lane));
102*fe5224a3SRobert Mustacchi }
103*fe5224a3SRobert Mustacchi
104*fe5224a3SRobert Mustacchi if (!nvme_vendor_vuc_supported(ctrl, "sandisk/pci-eye")) {
105*fe5224a3SRobert Mustacchi return (false);
106*fe5224a3SRobert Mustacchi }
107*fe5224a3SRobert Mustacchi
108*fe5224a3SRobert Mustacchi if (!nvme_vuc_req_init(ctrl, &req)) {
109*fe5224a3SRobert Mustacchi return (false);
110*fe5224a3SRobert Mustacchi }
111*fe5224a3SRobert Mustacchi
112*fe5224a3SRobert Mustacchi
113*fe5224a3SRobert Mustacchi if (!nvme_vuc_req_set_opcode(req, WDC_SN861_VUC_EYE_OPC) ||
114*fe5224a3SRobert Mustacchi !nvme_vuc_req_set_cdw12(req, WDC_SN861_VUC_EYE_CDW12) ||
115*fe5224a3SRobert Mustacchi !nvme_vuc_req_set_cdw13(req, lane) ||
116*fe5224a3SRobert Mustacchi !nvme_vuc_req_set_timeout(req, nvme_sndk_timeout) ||
117*fe5224a3SRobert Mustacchi !nvme_vuc_req_set_output(req, buf, len) ||
118*fe5224a3SRobert Mustacchi !nvme_vuc_req_exec(req)) {
119*fe5224a3SRobert Mustacchi nvme_vuc_req_fini(req);
120*fe5224a3SRobert Mustacchi return (false);
121*fe5224a3SRobert Mustacchi }
122*fe5224a3SRobert Mustacchi
123*fe5224a3SRobert Mustacchi nvme_vuc_req_fini(req);
124*fe5224a3SRobert Mustacchi return (nvme_ctrl_success(ctrl));
125*fe5224a3SRobert Mustacchi }
126*fe5224a3SRobert Mustacchi
127*fe5224a3SRobert Mustacchi bool
nvme_sndk_hw_rev(nvme_ctrl_t * ctrl,uint8_t * majorp,uint8_t * minorp)128*fe5224a3SRobert Mustacchi nvme_sndk_hw_rev(nvme_ctrl_t *ctrl, uint8_t *majorp, uint8_t *minorp)
129*fe5224a3SRobert Mustacchi {
130*fe5224a3SRobert Mustacchi uint32_t vers;
131*fe5224a3SRobert Mustacchi nvme_vuc_req_t *req = NULL;
132*fe5224a3SRobert Mustacchi
133*fe5224a3SRobert Mustacchi if (majorp == NULL) {
134*fe5224a3SRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
135*fe5224a3SRobert Mustacchi "encountered invalid major number output pointer: %p",
136*fe5224a3SRobert Mustacchi majorp));
137*fe5224a3SRobert Mustacchi }
138*fe5224a3SRobert Mustacchi
139*fe5224a3SRobert Mustacchi if (minorp == NULL) {
140*fe5224a3SRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
141*fe5224a3SRobert Mustacchi "encountered invalid minor number output pointer: %p",
142*fe5224a3SRobert Mustacchi majorp));
143*fe5224a3SRobert Mustacchi }
144*fe5224a3SRobert Mustacchi
145*fe5224a3SRobert Mustacchi if (!nvme_vendor_vuc_supported(ctrl, "sandisk/hwrev")) {
146*fe5224a3SRobert Mustacchi return (false);
147*fe5224a3SRobert Mustacchi }
148*fe5224a3SRobert Mustacchi
149*fe5224a3SRobert Mustacchi if (!nvme_vuc_req_init(ctrl, &req)) {
150*fe5224a3SRobert Mustacchi return (false);
151*fe5224a3SRobert Mustacchi }
152*fe5224a3SRobert Mustacchi
153*fe5224a3SRobert Mustacchi if (!nvme_vuc_req_set_opcode(req, WDC_SN861_VUC_HWREV_OPC) ||
154*fe5224a3SRobert Mustacchi !nvme_vuc_req_set_cdw12(req, WDC_SN861_VUC_HWREV_CDW12) ||
155*fe5224a3SRobert Mustacchi !nvme_vuc_req_set_timeout(req, nvme_sndk_timeout) ||
156*fe5224a3SRobert Mustacchi !nvme_vuc_req_set_output(req, &vers, sizeof (vers)) ||
157*fe5224a3SRobert Mustacchi !nvme_vuc_req_exec(req)) {
158*fe5224a3SRobert Mustacchi nvme_vuc_req_fini(req);
159*fe5224a3SRobert Mustacchi return (false);
160*fe5224a3SRobert Mustacchi }
161*fe5224a3SRobert Mustacchi nvme_vuc_req_fini(req);
162*fe5224a3SRobert Mustacchi
163*fe5224a3SRobert Mustacchi /*
164*fe5224a3SRobert Mustacchi * The major and minor version are the first and second 10s digit of
165*fe5224a3SRobert Mustacchi * this value. If we have something that is larger than that then we are
166*fe5224a3SRobert Mustacchi * going to treat this as an error.
167*fe5224a3SRobert Mustacchi */
168*fe5224a3SRobert Mustacchi if (vers > 100) {
169*fe5224a3SRobert Mustacchi return (nvme_ctrl_error(ctrl, NVME_ERR_INTERNAL, 0, "returned "
170*fe5224a3SRobert Mustacchi "version is in an unexpected format and cannot be parsed "
171*fe5224a3SRobert Mustacchi "into its major and minor number: 0x%x", vers));
172*fe5224a3SRobert Mustacchi }
173*fe5224a3SRobert Mustacchi
174*fe5224a3SRobert Mustacchi *minorp = vers % 10;
175*fe5224a3SRobert Mustacchi *majorp = (vers / 10) % 10;
176*fe5224a3SRobert Mustacchi
177*fe5224a3SRobert Mustacchi return (nvme_ctrl_success(ctrl));
178*fe5224a3SRobert Mustacchi }
179