1/*-
2 *   BSD LICENSE
3 *
4 *   Copyright (c) Intel Corporation. All rights reserved.
5 *   Copyright (c) 2017, Western Digital Corporation or its affiliates.
6 *
7 *   Redistribution and use in sourete and binary forms, with or without
8 *   modification, are permitted provided that the following conditions
9 *   are met:
10 *
11 *     * Redistributions of sourete code must retain the above copyright
12 *       notice, this list of conditions and the following disclaimer.
13 *     * Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *     * Neither the name of Intel Corporation nor the names of its
18 *       contributors may be used to endorse or promote products derived
19 *       from this software without specific prior written permission.
20 *
21 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "nvme_internal.h"
35
36/*
37 * Allocate a request, set its command and submit it
38 * to the controller admin queue.
39 */
40static int nvme_admin_submit_cmd(struct nvme_ctrlr *ctrlr,
41				 struct nvme_cmd *cmd,
42				 void *buf, uint32_t len,
43				 nvme_cmd_cb cb_fn, void *cb_arg)
44{
45	struct nvme_request *req;
46
47	if (buf)
48		req = nvme_request_allocate_contig(&ctrlr->adminq, buf, len,
49						   cb_fn, cb_arg);
50	else
51		req = nvme_request_allocate_null(&ctrlr->adminq, cb_fn, cb_arg);
52	if (!req)
53		return ENOMEM;
54
55	memcpy(&req->cmd, cmd, sizeof(req->cmd));
56
57	return nvme_qpair_submit_request(&ctrlr->adminq, req);
58}
59
60/*
61 * Poll the controller admin queue waiting for a
62 * command completion.
63 */
64static int nvme_admin_wait_cmd(struct nvme_ctrlr *ctrlr,
65			       struct nvme_completion_poll_status *status)
66{
67
68	/* Wait for completion and check result */
69	while (status->done == false)
70		nvme_qpair_poll(&ctrlr->adminq, 0);
71
72	if (nvme_cpl_is_error(&status->cpl)) {
73		nvme_notice("Admin command failed\n");
74		return ENXIO;
75	}
76
77	return 0;
78}
79
80/*
81 * Execute an admin command.
82 */
83static int nvme_admin_exec_cmd(struct nvme_ctrlr *ctrlr,
84			       struct nvme_cmd *cmd,
85			       void *buf, uint32_t len)
86{
87	struct nvme_completion_poll_status status;
88	int ret;
89
90	/* Submit the command */
91	status.done = false;
92	ret = nvme_admin_submit_cmd(ctrlr, cmd, buf, len,
93				    nvme_request_completion_poll_cb,
94				    &status);
95	if (ret != 0)
96		return ret;
97
98	/* Wait for the command completion and check result */
99	return nvme_admin_wait_cmd(ctrlr, &status);
100}
101
102/*
103 * Get a controller information.
104 */
105int nvme_admin_identify_ctrlr(struct nvme_ctrlr *ctrlr,
106			      struct nvme_ctrlr_data *cdata)
107{
108	struct nvme_cmd cmd;
109
110	/* Setup the command */
111	memset(&cmd, 0, sizeof(struct nvme_cmd));
112	cmd.opc = NVME_OPC_IDENTIFY;
113	cmd.cdw10 = NVME_IDENTIFY_CTRLR;
114
115	/* Execute the command */
116	return nvme_admin_exec_cmd(ctrlr, &cmd,
117				   cdata, sizeof(struct nvme_ctrlr_data));
118}
119
120/*
121 * Get a controller feature.
122 */
123int nvme_admin_get_feature(struct nvme_ctrlr *ctrlr,
124			   enum nvme_feat_sel sel,
125			   enum nvme_feat feature,
126			   uint32_t cdw11,
127			   uint32_t *attributes)
128{
129	struct nvme_completion_poll_status status;
130	struct nvme_cmd cmd;
131	int ret;
132
133	/* Setup the command */
134	memset(&cmd, 0, sizeof(struct nvme_cmd));
135	cmd.opc = NVME_OPC_GET_FEATURES;
136	cmd.cdw10 = (sel << 8) | feature;
137	cmd.cdw11 = cdw11;
138
139	/* Submit the command */
140	status.done = false;
141	ret = nvme_admin_submit_cmd(ctrlr, &cmd, NULL, 0,
142				    nvme_request_completion_poll_cb,
143				    &status);
144	if (ret == 0) {
145		/* Wait for the command completion and check result */
146		ret = nvme_admin_wait_cmd(ctrlr, &status);
147		if (ret == 0 && attributes)
148			*attributes = status.cpl.cdw0;
149	}
150
151	return ret;
152}
153
154/*
155 * Set a feature.
156 */
157int nvme_admin_set_feature(struct nvme_ctrlr *ctrlr,
158			   bool save,
159			   enum nvme_feat feature,
160			   uint32_t cdw11,
161			   uint32_t cdw12,
162			   uint32_t *attributes)
163{
164	struct nvme_completion_poll_status status;
165	struct nvme_cmd cmd;
166	int ret;
167
168	/* Setup the command */
169	memset(&cmd, 0, sizeof(struct nvme_cmd));
170	cmd.opc = NVME_OPC_SET_FEATURES;
171	cmd.cdw10 = feature;
172	if (save)
173		cmd.cdw10 |= (1 << 31);
174	cmd.cdw11 = cdw11;
175	cmd.cdw12 = cdw12;
176
177	/* Submit the command */
178	status.done = false;
179	ret = nvme_admin_submit_cmd(ctrlr, &cmd, NULL, 0,
180				    nvme_request_completion_poll_cb,
181				    &status);
182	if (ret == 0) {
183		/* Wait for the command completion and check result */
184		ret = nvme_admin_wait_cmd(ctrlr, &status);
185		if (ret == 0 && attributes)
186			*attributes = status.cpl.cdw0;
187	}
188
189	return ret;
190}
191
192/*
193 * Create an I/O queue.
194 */
195int nvme_admin_create_ioq(struct nvme_ctrlr *ctrlr,
196			  struct nvme_qpair *qpair,
197			  enum nvme_io_queue_type io_qtype)
198{
199	struct nvme_cmd cmd;
200
201	/* Setup the command */
202	memset(&cmd, 0, sizeof(struct nvme_cmd));
203	switch(io_qtype) {
204	case NVME_IO_SUBMISSION_QUEUE:
205		cmd.opc = NVME_OPC_CREATE_IO_SQ;
206		cmd.cdw11 = (qpair->id << 16) | (qpair->qprio << 1) | 0x1;
207		cmd.dptr.prp.prp1 = qpair->cmd_bus_addr;
208		break;
209	case NVME_IO_COMPLETION_QUEUE:
210		cmd.opc = NVME_OPC_CREATE_IO_CQ;
211#ifdef __HAIKU__ // TODO: Option!
212		cmd.cdw11 = 0x1 | 0x2; /* enable interrupts */
213#else
214		cmd.cdw11 = 0x1;
215#endif
216		cmd.dptr.prp.prp1 = qpair->cpl_bus_addr;
217		break;
218	default:
219		return EINVAL;
220	}
221
222	cmd.cdw10 = ((qpair->entries - 1) << 16) | qpair->id;
223
224	/* Execute the command */
225	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
226}
227
228/*
229 * Delete an I/O queue.
230 */
231int nvme_admin_delete_ioq(struct nvme_ctrlr *ctrlr,
232			  struct nvme_qpair *qpair,
233			  enum nvme_io_queue_type io_qtype)
234{
235	struct nvme_cmd cmd;
236
237	/* Setup the command */
238	memset(&cmd, 0, sizeof(struct nvme_cmd));
239	switch(io_qtype) {
240	case NVME_IO_SUBMISSION_QUEUE:
241		cmd.opc = NVME_OPC_DELETE_IO_SQ;
242		break;
243	case NVME_IO_COMPLETION_QUEUE:
244		cmd.opc = NVME_OPC_DELETE_IO_CQ;
245		break;
246	default:
247		return EINVAL;
248	}
249	cmd.cdw10 = qpair->id;
250
251	/* Execute the command */
252	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
253}
254
255/*
256 * Get a namespace information.
257 */
258int nvme_admin_identify_ns(struct nvme_ctrlr *ctrlr,
259			   uint16_t nsid,
260			   struct nvme_ns_data *nsdata)
261{
262	struct nvme_cmd cmd;
263
264	/* Setup the command */
265	memset(&cmd, 0, sizeof(struct nvme_cmd));
266	cmd.opc = NVME_OPC_IDENTIFY;
267	cmd.cdw10 = NVME_IDENTIFY_NS;
268	cmd.nsid = nsid;
269
270	/* Execute the command */
271	return nvme_admin_exec_cmd(ctrlr, &cmd,
272				   nsdata, sizeof(struct nvme_ns_data));
273}
274
275/*
276 * Attach a namespace.
277 */
278int nvme_admin_attach_ns(struct nvme_ctrlr *ctrlr,
279			 uint32_t nsid,
280			 struct nvme_ctrlr_list *clist)
281{
282	struct nvme_cmd cmd;
283
284	/* Setup the command */
285	memset(&cmd, 0, sizeof(struct nvme_cmd));
286	cmd.opc = NVME_OPC_NS_ATTACHMENT;
287	cmd.nsid = nsid;
288	cmd.cdw10 = NVME_NS_CTRLR_ATTACH;
289
290	/* Execute the command */
291	return nvme_admin_exec_cmd(ctrlr, &cmd,
292				   clist, sizeof(struct nvme_ctrlr_list));
293}
294
295/*
296 * Detach a namespace.
297 */
298int nvme_admin_detach_ns(struct nvme_ctrlr *ctrlr,
299			 uint32_t nsid,
300			 struct nvme_ctrlr_list *clist)
301{
302	struct nvme_cmd cmd;
303
304	/* Setup the command */
305	memset(&cmd, 0, sizeof(struct nvme_cmd));
306	cmd.opc = NVME_OPC_NS_ATTACHMENT;
307	cmd.nsid = nsid;
308	cmd.cdw10 = NVME_NS_CTRLR_DETACH;
309
310	/* Execute the command */
311	return nvme_admin_exec_cmd(ctrlr, &cmd,
312				   clist, sizeof(struct nvme_ctrlr_list));
313}
314
315/*
316 * Create a namespace.
317 */
318int nvme_admin_create_ns(struct nvme_ctrlr *ctrlr,
319			 struct nvme_ns_data *nsdata,
320			 unsigned int *nsid)
321{
322	struct nvme_completion_poll_status status;
323	struct nvme_cmd cmd;
324	int ret;
325
326	/* Setup the command */
327	memset(&cmd, 0, sizeof(struct nvme_cmd));
328	cmd.opc = NVME_OPC_NS_MANAGEMENT;
329	cmd.cdw10 = NVME_NS_MANAGEMENT_CREATE;
330
331	/* Submit the command */
332	status.done = false;
333	ret = nvme_admin_submit_cmd(ctrlr, &cmd,
334				    nsdata, sizeof(struct nvme_ns_data),
335				    nvme_request_completion_poll_cb,
336				    &status);
337	if (ret == 0)
338		/* Wait for the command completion and check result */
339		ret = nvme_admin_wait_cmd(ctrlr, &status);
340
341	if (ret != 0)
342		return ret;
343
344	*nsid = status.cpl.cdw0;
345
346	return 0;
347}
348
349/*
350 * Delete a namespace.
351 */
352int nvme_admin_delete_ns(struct nvme_ctrlr *ctrlr,
353			 unsigned int nsid)
354{
355	struct nvme_cmd cmd;
356
357	/* Setup the command */
358	memset(&cmd, 0, sizeof(struct nvme_cmd));
359	cmd.opc = NVME_OPC_NS_MANAGEMENT;
360	cmd.cdw10 = NVME_NS_MANAGEMENT_DELETE;
361	cmd.nsid = nsid;
362
363	/* Execute the command */
364	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
365}
366
367/*
368 * Format media.
369 * (entire device or just the specified namespace)
370 */
371int nvme_admin_format_nvm(struct nvme_ctrlr *ctrlr,
372			  unsigned int nsid,
373			  struct nvme_format *format)
374{
375	struct nvme_cmd cmd;
376
377	/* Setup the command */
378	memset(&cmd, 0, sizeof(struct nvme_cmd));
379	cmd.opc = NVME_OPC_FORMAT_NVM;
380	cmd.nsid = nsid;
381	memcpy(&cmd.cdw10, format, sizeof(uint32_t));
382
383	/* Execute the command */
384	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
385}
386
387/*
388 * Get a log page.
389 */
390int nvme_admin_get_log_page(struct nvme_ctrlr *ctrlr,
391			    uint8_t log_page,
392			    uint32_t nsid,
393			    void *payload,
394			    uint32_t payload_size)
395{
396	struct nvme_cmd cmd;
397
398	/* Setup the command */
399	memset(&cmd, 0, sizeof(struct nvme_cmd));
400	cmd.opc = NVME_OPC_GET_LOG_PAGE;
401	cmd.nsid = nsid;
402	cmd.cdw10 = ((payload_size / sizeof(uint32_t)) - 1) << 16;
403	cmd.cdw10 |= log_page;
404
405	/* Execute the command */
406	return nvme_admin_exec_cmd(ctrlr, &cmd, payload, payload_size);
407}
408
409/*
410 * Abort an admin or an I/O command.
411 */
412int nvme_admin_abort_cmd(struct nvme_ctrlr *ctrlr,
413			 uint16_t cid, uint16_t sqid)
414{
415	struct nvme_cmd cmd;
416
417	/* Setup the command */
418	memset(&cmd, 0, sizeof(struct nvme_cmd));
419	cmd.opc = NVME_OPC_ABORT;
420	cmd.cdw10 = (cid << 16) | sqid;
421
422	/* Execute the command */
423	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
424}
425
426/*
427 * Validate a FW.
428 */
429int nvme_admin_fw_commit(struct nvme_ctrlr *ctrlr,
430			 const struct nvme_fw_commit *fw_commit)
431{
432	struct nvme_cmd cmd;
433
434	/* Setup the command */
435	memset(&cmd, 0, sizeof(struct nvme_cmd));
436	cmd.opc = NVME_OPC_FIRMWARE_COMMIT;
437	memcpy(&cmd.cdw10, fw_commit, sizeof(uint32_t));
438
439	/* Execute the command */
440	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
441}
442
443/*
444 * Download to the device a firmware.
445 */
446int nvme_admin_fw_image_dl(struct nvme_ctrlr *ctrlr,
447			   void *fw, uint32_t size,
448			   uint32_t offset)
449{
450	struct nvme_cmd cmd;
451
452	/* Setup the command */
453	memset(&cmd, 0, sizeof(struct nvme_cmd));
454	cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
455	cmd.cdw10 = (size >> 2) - 1;
456	cmd.cdw11 = offset >> 2;
457
458	/* Execute the command */
459	return nvme_admin_exec_cmd(ctrlr, &cmd, fw, size);
460}
461