xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_sandisk.c (revision fe5224a3e61e4d791ea239810e79697fc96f7b16)
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