| /* |
| * Copyright (c) 2022 Intel Corporation |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Derived from FreeBSD original driver made by Jim Harris |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_DECLARE(nvme, CONFIG_NVME_LOG_LEVEL); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/sys/byteorder.h> |
| |
| #include <stdio.h> |
| |
| #include "nvme.h" |
| |
| uint32_t nvme_namespace_get_sector_size(struct nvme_namespace *ns) |
| { |
| uint8_t flbas_fmt, lbads; |
| |
| flbas_fmt = (ns->data.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & |
| NVME_NS_DATA_FLBAS_FORMAT_MASK; |
| lbads = (ns->data.lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & |
| NVME_NS_DATA_LBAF_LBADS_MASK; |
| |
| return 1 << lbads; |
| } |
| |
| uint64_t nvme_namespace_get_num_sectors(struct nvme_namespace *ns) |
| { |
| return ns->data.nsze; |
| } |
| |
| uint64_t nvme_namespace_get_size(struct nvme_namespace *ns) |
| { |
| return nvme_namespace_get_num_sectors(ns) * |
| nvme_namespace_get_sector_size(ns); |
| } |
| |
| uint32_t nvme_namespace_get_flags(struct nvme_namespace *ns) |
| { |
| return ns->flags; |
| } |
| |
| const char *nvme_namespace_get_serial_number(struct nvme_namespace *ns) |
| { |
| return (const char *)ns->ctrlr->cdata.sn; |
| } |
| |
| const char *nvme_namespace_get_model_number(struct nvme_namespace *ns) |
| { |
| return (const char *)ns->ctrlr->cdata.mn; |
| } |
| |
| const struct nvme_namespace_data * |
| nvme_namespace_get_data(struct nvme_namespace *ns) |
| { |
| return &ns->data; |
| } |
| |
| uint32_t nvme_namespace_get_stripesize(struct nvme_namespace *ns) |
| { |
| if (((ns->data.nsfeat >> NVME_NS_DATA_NSFEAT_NPVALID_SHIFT) & |
| NVME_NS_DATA_NSFEAT_NPVALID_MASK) != 0) { |
| uint32_t ss = nvme_namespace_get_sector_size(ns); |
| |
| if (ns->data.npwa != 0) { |
| return (ns->data.npwa + 1) * ss; |
| } else if (ns->data.npwg != 0) { |
| return (ns->data.npwg + 1) * ss; |
| } |
| } |
| |
| return ns->boundary; |
| } |
| |
| int nvme_namespace_construct(struct nvme_namespace *ns, |
| uint32_t id, |
| struct nvme_controller *ctrlr) |
| { |
| struct nvme_completion_poll_status status = |
| NVME_CPL_STATUS_POLL_INIT(status); |
| uint8_t flbas_fmt; |
| uint8_t vwc_present; |
| |
| ns->ctrlr = ctrlr; |
| ns->id = id; |
| |
| nvme_ctrlr_cmd_identify_namespace(ctrlr, id, &ns->data, |
| nvme_completion_poll_cb, |
| &status); |
| nvme_completion_poll(&status); |
| |
| if (nvme_cpl_status_is_error(&status)) { |
| LOG_DBG("Identifying NS id %d failed", id); |
| return -EIO; |
| } |
| |
| nvme_namespace_data_swapbytes(&ns->data); |
| |
| if (nvme_namespace_get_num_sectors(ns) == 0) { |
| LOG_DBG("Namespace %d not present", id); |
| return -ENODEV; |
| } |
| |
| flbas_fmt = (ns->data.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & |
| NVME_NS_DATA_FLBAS_FORMAT_MASK; |
| |
| /* Note: format is a 0-based value, so > is appropriate here not >=. */ |
| if (flbas_fmt > ns->data.nlbaf) { |
| LOG_DBG("NS id %d: lba format %d exceeds number supported (%d)", |
| id, flbas_fmt, ns->data.nlbaf + 1); |
| return -EIO; |
| } |
| |
| ns->boundary = ns->data.noiob * nvme_namespace_get_sector_size(ns); |
| |
| if (nvme_controller_has_dataset_mgmt(ctrlr)) { |
| ns->flags |= NVME_NS_DEALLOCATE_SUPPORTED; |
| } |
| |
| vwc_present = (ctrlr->cdata.vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) & |
| NVME_CTRLR_DATA_VWC_PRESENT_MASK; |
| if (vwc_present) { |
| ns->flags |= NVME_NS_FLUSH_SUPPORTED; |
| } |
| |
| snprintf(ns->name, NVME_NAMESPACE_NAME_MAX_LENGTH, "nvme%dn%d", |
| ctrlr->id, ns->id-1); |
| |
| if (nvme_namespace_disk_setup(ns, &ns->disk) != 0) { |
| LOG_ERR("Could not register no disk subsystem"); |
| } |
| |
| return 0; |
| } |