1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (C) 2012-2013 Intel Corporation 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include "nvme_private.h" 31 32 void 33 nvme_ctrlr_cmd_identify_controller(struct nvme_controller *ctrlr, void *payload, 34 nvme_cb_fn_t cb_fn, void *cb_arg) 35 { 36 struct nvme_request *req; 37 struct nvme_command *cmd; 38 39 req = nvme_allocate_request_vaddr(payload, 40 sizeof(struct nvme_controller_data), M_WAITOK, cb_fn, cb_arg); 41 42 cmd = &req->cmd; 43 cmd->opc = NVME_OPC_IDENTIFY; 44 45 /* 46 * TODO: create an identify command data structure, which 47 * includes this CNS bit in cdw10. 48 */ 49 cmd->cdw10 = htole32(1); 50 51 nvme_ctrlr_submit_admin_request(ctrlr, req); 52 } 53 54 void 55 nvme_ctrlr_cmd_identify_namespace(struct nvme_controller *ctrlr, uint32_t nsid, 56 void *payload, nvme_cb_fn_t cb_fn, void *cb_arg) 57 { 58 struct nvme_request *req; 59 struct nvme_command *cmd; 60 61 req = nvme_allocate_request_vaddr(payload, 62 sizeof(struct nvme_namespace_data), M_WAITOK, cb_fn, cb_arg); 63 64 cmd = &req->cmd; 65 cmd->opc = NVME_OPC_IDENTIFY; 66 67 /* 68 * TODO: create an identify command data structure 69 */ 70 cmd->nsid = htole32(nsid); 71 72 nvme_ctrlr_submit_admin_request(ctrlr, req); 73 } 74 75 void 76 nvme_ctrlr_cmd_create_io_cq(struct nvme_controller *ctrlr, 77 struct nvme_qpair *io_que, nvme_cb_fn_t cb_fn, void *cb_arg) 78 { 79 struct nvme_request *req; 80 struct nvme_command *cmd; 81 82 req = nvme_allocate_request_null(M_WAITOK, cb_fn, cb_arg); 83 84 cmd = &req->cmd; 85 cmd->opc = NVME_OPC_CREATE_IO_CQ; 86 87 /* 88 * TODO: create a create io completion queue command data 89 * structure. 90 */ 91 cmd->cdw10 = htole32(((io_que->num_entries-1) << 16) | io_que->id); 92 cmd->cdw11 = htole32((io_que->vector << 16) | 93 NVME_CREATE_IO_CQ_IEN | NVME_CREATE_IO_CQ_PC); 94 cmd->prp1 = htole64(io_que->cpl_bus_addr); 95 96 nvme_ctrlr_submit_admin_request(ctrlr, req); 97 } 98 99 void 100 nvme_ctrlr_cmd_create_io_sq(struct nvme_controller *ctrlr, 101 struct nvme_qpair *io_que, nvme_cb_fn_t cb_fn, void *cb_arg) 102 { 103 struct nvme_request *req; 104 struct nvme_command *cmd; 105 106 req = nvme_allocate_request_null(M_WAITOK, cb_fn, cb_arg); 107 108 cmd = &req->cmd; 109 cmd->opc = NVME_OPC_CREATE_IO_SQ; 110 111 /* 112 * TODO: create a create io submission queue command data 113 * structure. 114 */ 115 cmd->cdw10 = htole32(((io_que->num_entries-1) << 16) | io_que->id); 116 cmd->cdw11 = htole32((io_que->id << 16) | 117 NVMEF(NVME_CREATE_IO_SQ_QPRIO, NVME_QPRIO_MEDIUM) | 118 NVME_CREATE_IO_SQ_PC); 119 cmd->prp1 = htole64(io_que->cmd_bus_addr); 120 121 nvme_ctrlr_submit_admin_request(ctrlr, req); 122 } 123 124 static void 125 nvme_ctrlr_cmd_delete_io_queue(struct nvme_controller *ctrlr, 126 struct nvme_qpair *io_que, uint8_t opc, nvme_cb_fn_t cb_fn, void *cb_arg) 127 { 128 struct nvme_request *req; 129 struct nvme_command *cmd; 130 131 req = nvme_allocate_request_null(M_WAITOK, cb_fn, cb_arg); 132 133 cmd = &req->cmd; 134 cmd->opc = opc; 135 136 /* 137 * TODO: create a delete io queue command data structure. 138 */ 139 cmd->cdw10 = htole32(io_que->id); 140 141 nvme_ctrlr_submit_admin_request(ctrlr, req); 142 } 143 144 void 145 nvme_ctrlr_cmd_delete_io_cq(struct nvme_controller *ctrlr, 146 struct nvme_qpair *io_que, nvme_cb_fn_t cb_fn, void *cb_arg) 147 { 148 nvme_ctrlr_cmd_delete_io_queue(ctrlr, io_que, NVME_OPC_DELETE_IO_CQ, 149 cb_fn, cb_arg); 150 } 151 152 void 153 nvme_ctrlr_cmd_delete_io_sq(struct nvme_controller *ctrlr, 154 struct nvme_qpair *io_que, nvme_cb_fn_t cb_fn, void *cb_arg) 155 { 156 nvme_ctrlr_cmd_delete_io_queue(ctrlr, io_que, NVME_OPC_DELETE_IO_SQ, 157 cb_fn, cb_arg); 158 } 159 160 void 161 nvme_ctrlr_cmd_set_feature(struct nvme_controller *ctrlr, uint8_t feature, 162 uint32_t cdw11, uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, 163 uint32_t cdw15, void *payload, uint32_t payload_size, 164 nvme_cb_fn_t cb_fn, void *cb_arg) 165 { 166 struct nvme_request *req; 167 struct nvme_command *cmd; 168 169 if (payload != NULL) 170 req = nvme_allocate_request_vaddr(payload, payload_size, 171 M_WAITOK, cb_fn, cb_arg); 172 else 173 req = nvme_allocate_request_null(M_WAITOK, cb_fn, cb_arg); 174 175 cmd = &req->cmd; 176 cmd->opc = NVME_OPC_SET_FEATURES; 177 cmd->cdw10 = htole32(feature); 178 cmd->cdw11 = htole32(cdw11); 179 cmd->cdw12 = htole32(cdw12); 180 cmd->cdw13 = htole32(cdw13); 181 cmd->cdw14 = htole32(cdw14); 182 cmd->cdw15 = htole32(cdw15); 183 184 nvme_ctrlr_submit_admin_request(ctrlr, req); 185 } 186 187 void 188 nvme_ctrlr_cmd_get_feature(struct nvme_controller *ctrlr, uint8_t feature, 189 uint32_t cdw11, void *payload, uint32_t payload_size, 190 nvme_cb_fn_t cb_fn, void *cb_arg) 191 { 192 struct nvme_request *req; 193 struct nvme_command *cmd; 194 195 if (payload != NULL) 196 req = nvme_allocate_request_vaddr(payload, payload_size, 197 M_WAITOK, cb_fn, cb_arg); 198 else 199 req = nvme_allocate_request_null(M_WAITOK, cb_fn, cb_arg); 200 201 cmd = &req->cmd; 202 cmd->opc = NVME_OPC_GET_FEATURES; 203 cmd->cdw10 = htole32(feature); 204 cmd->cdw11 = htole32(cdw11); 205 206 nvme_ctrlr_submit_admin_request(ctrlr, req); 207 } 208 209 void 210 nvme_ctrlr_cmd_set_num_queues(struct nvme_controller *ctrlr, 211 uint32_t num_queues, nvme_cb_fn_t cb_fn, void *cb_arg) 212 { 213 uint32_t cdw11; 214 215 cdw11 = ((num_queues - 1) << 16) | (num_queues - 1); 216 nvme_ctrlr_cmd_set_feature(ctrlr, NVME_FEAT_NUMBER_OF_QUEUES, cdw11, 217 0, 0, 0, 0, NULL, 0, cb_fn, cb_arg); 218 } 219 220 void 221 nvme_ctrlr_cmd_set_async_event_config(struct nvme_controller *ctrlr, 222 uint32_t state, nvme_cb_fn_t cb_fn, void *cb_arg) 223 { 224 uint32_t cdw11; 225 226 cdw11 = state; 227 nvme_ctrlr_cmd_set_feature(ctrlr, 228 NVME_FEAT_ASYNC_EVENT_CONFIGURATION, cdw11, 0, 0, 0, 0, NULL, 0, 229 cb_fn, cb_arg); 230 } 231 232 void 233 nvme_ctrlr_cmd_set_interrupt_coalescing(struct nvme_controller *ctrlr, 234 uint32_t microseconds, uint32_t threshold, nvme_cb_fn_t cb_fn, void *cb_arg) 235 { 236 uint32_t cdw11; 237 238 if ((microseconds/100) >= 0x100) { 239 nvme_printf(ctrlr, "invalid coal time %d, disabling\n", 240 microseconds); 241 microseconds = 0; 242 threshold = 0; 243 } 244 245 if (threshold >= 0x100) { 246 nvme_printf(ctrlr, "invalid threshold %d, disabling\n", 247 threshold); 248 threshold = 0; 249 microseconds = 0; 250 } 251 252 cdw11 = ((microseconds/100) << 8) | threshold; 253 nvme_ctrlr_cmd_set_feature(ctrlr, NVME_FEAT_INTERRUPT_COALESCING, cdw11, 254 0, 0, 0, 0, NULL, 0, cb_fn, cb_arg); 255 } 256 257 void 258 nvme_ctrlr_cmd_get_log_page(struct nvme_controller *ctrlr, uint8_t log_page, 259 uint32_t nsid, void *payload, uint32_t payload_size, nvme_cb_fn_t cb_fn, 260 void *cb_arg) 261 { 262 struct nvme_request *req; 263 struct nvme_command *cmd; 264 265 /* 266 * XXX-MJ this should be M_WAITOK but we might be called from AER 267 * completion processing, which is a non-sleepable context. 268 */ 269 req = nvme_allocate_request_vaddr(payload, payload_size, 270 M_NOWAIT, cb_fn, cb_arg); 271 272 cmd = &req->cmd; 273 cmd->opc = NVME_OPC_GET_LOG_PAGE; 274 cmd->nsid = htole32(nsid); 275 cmd->cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 276 cmd->cdw10 |= log_page; 277 cmd->cdw10 = htole32(cmd->cdw10); 278 279 nvme_ctrlr_submit_admin_request(ctrlr, req); 280 } 281 282 void 283 nvme_ctrlr_cmd_get_error_page(struct nvme_controller *ctrlr, 284 struct nvme_error_information_entry *payload, uint32_t num_entries, 285 nvme_cb_fn_t cb_fn, void *cb_arg) 286 { 287 KASSERT(num_entries > 0, ("%s called with num_entries==0\n", __func__)); 288 289 /* Controller's error log page entries is 0-based. */ 290 KASSERT(num_entries <= (ctrlr->cdata.elpe + 1), 291 ("%s called with num_entries=%d but (elpe+1)=%d\n", __func__, 292 num_entries, ctrlr->cdata.elpe + 1)); 293 294 if (num_entries > (ctrlr->cdata.elpe + 1)) 295 num_entries = ctrlr->cdata.elpe + 1; 296 297 nvme_ctrlr_cmd_get_log_page(ctrlr, NVME_LOG_ERROR, 298 NVME_GLOBAL_NAMESPACE_TAG, payload, sizeof(*payload) * num_entries, 299 cb_fn, cb_arg); 300 } 301 302 void 303 nvme_ctrlr_cmd_get_health_information_page(struct nvme_controller *ctrlr, 304 uint32_t nsid, struct nvme_health_information_page *payload, 305 nvme_cb_fn_t cb_fn, void *cb_arg) 306 { 307 nvme_ctrlr_cmd_get_log_page(ctrlr, NVME_LOG_HEALTH_INFORMATION, 308 nsid, payload, sizeof(*payload), cb_fn, cb_arg); 309 } 310 311 void 312 nvme_ctrlr_cmd_get_firmware_page(struct nvme_controller *ctrlr, 313 struct nvme_firmware_page *payload, nvme_cb_fn_t cb_fn, void *cb_arg) 314 { 315 nvme_ctrlr_cmd_get_log_page(ctrlr, NVME_LOG_FIRMWARE_SLOT, 316 NVME_GLOBAL_NAMESPACE_TAG, payload, sizeof(*payload), cb_fn, 317 cb_arg); 318 } 319 320 void 321 nvme_ctrlr_cmd_abort(struct nvme_controller *ctrlr, uint16_t cid, 322 uint16_t sqid, nvme_cb_fn_t cb_fn, void *cb_arg) 323 { 324 struct nvme_request *req; 325 struct nvme_command *cmd; 326 327 /* 328 * XXX-MJ this should be M_WAITOK, we do reset from non-sleepable 329 * context and abort commands as part of that. 330 */ 331 req = nvme_allocate_request_null(M_NOWAIT, cb_fn, cb_arg); 332 333 cmd = &req->cmd; 334 cmd->opc = NVME_OPC_ABORT; 335 cmd->cdw10 = htole32((cid << 16) | sqid); 336 337 nvme_ctrlr_submit_admin_request(ctrlr, req); 338 } 339