1 /*
2  * Copyright (c) 2022 Intel Corporation
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Derived from FreeBSD original driver made by Jim Harris
6  */
7 
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(nvme, CONFIG_NVME_LOG_LEVEL);
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/byteorder.h>
13 
14 #include <stdio.h>
15 
16 #include "nvme.h"
17 
nvme_namespace_get_sector_size(struct nvme_namespace * ns)18 uint32_t nvme_namespace_get_sector_size(struct nvme_namespace *ns)
19 {
20 	uint8_t flbas_fmt, lbads;
21 
22 	flbas_fmt = (ns->data.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) &
23 		NVME_NS_DATA_FLBAS_FORMAT_MASK;
24 	lbads = (ns->data.lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) &
25 		NVME_NS_DATA_LBAF_LBADS_MASK;
26 
27 	return 1 << lbads;
28 }
29 
nvme_namespace_get_num_sectors(struct nvme_namespace * ns)30 uint64_t nvme_namespace_get_num_sectors(struct nvme_namespace *ns)
31 {
32 	return ns->data.nsze;
33 }
34 
nvme_namespace_get_size(struct nvme_namespace * ns)35 uint64_t nvme_namespace_get_size(struct nvme_namespace *ns)
36 {
37 	return nvme_namespace_get_num_sectors(ns) *
38 		nvme_namespace_get_sector_size(ns);
39 }
40 
nvme_namespace_get_flags(struct nvme_namespace * ns)41 uint32_t nvme_namespace_get_flags(struct nvme_namespace *ns)
42 {
43 	return ns->flags;
44 }
45 
nvme_namespace_get_serial_number(struct nvme_namespace * ns)46 const char *nvme_namespace_get_serial_number(struct nvme_namespace *ns)
47 {
48 	return (const char *)ns->ctrlr->cdata.sn;
49 }
50 
nvme_namespace_get_model_number(struct nvme_namespace * ns)51 const char *nvme_namespace_get_model_number(struct nvme_namespace *ns)
52 {
53 	return (const char *)ns->ctrlr->cdata.mn;
54 }
55 
56 const struct nvme_namespace_data *
nvme_namespace_get_data(struct nvme_namespace * ns)57 nvme_namespace_get_data(struct nvme_namespace *ns)
58 {
59 	return &ns->data;
60 }
61 
nvme_namespace_get_stripesize(struct nvme_namespace * ns)62 uint32_t nvme_namespace_get_stripesize(struct nvme_namespace *ns)
63 {
64 	if (((ns->data.nsfeat >> NVME_NS_DATA_NSFEAT_NPVALID_SHIFT) &
65 	     NVME_NS_DATA_NSFEAT_NPVALID_MASK) != 0) {
66 		uint32_t ss = nvme_namespace_get_sector_size(ns);
67 
68 		if (ns->data.npwa != 0) {
69 			return (ns->data.npwa + 1) * ss;
70 		} else if (ns->data.npwg != 0) {
71 			return (ns->data.npwg + 1) * ss;
72 		}
73 	}
74 
75 	return ns->boundary;
76 }
77 
nvme_namespace_construct(struct nvme_namespace * ns,uint32_t id,struct nvme_controller * ctrlr)78 int nvme_namespace_construct(struct nvme_namespace *ns,
79 			     uint32_t id,
80 			     struct nvme_controller *ctrlr)
81 {
82 	struct nvme_completion_poll_status status =
83 		NVME_CPL_STATUS_POLL_INIT(status);
84 	uint8_t flbas_fmt;
85 	uint8_t vwc_present;
86 
87 	ns->ctrlr = ctrlr;
88 	ns->id = id;
89 
90 	nvme_ctrlr_cmd_identify_namespace(ctrlr, id, &ns->data,
91 					  nvme_completion_poll_cb,
92 					  &status);
93 	nvme_completion_poll(&status);
94 
95 	if (nvme_cpl_status_is_error(&status)) {
96 		LOG_DBG("Identifying NS id %d failed", id);
97 		return -EIO;
98 	}
99 
100 	nvme_namespace_data_swapbytes(&ns->data);
101 
102 	if (nvme_namespace_get_num_sectors(ns) == 0) {
103 		LOG_DBG("Namespace %d not present", id);
104 		return -ENODEV;
105 	}
106 
107 	flbas_fmt = (ns->data.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) &
108 		NVME_NS_DATA_FLBAS_FORMAT_MASK;
109 
110 	/* Note: format is a 0-based value, so > is appropriate here not >=. */
111 	if (flbas_fmt > ns->data.nlbaf) {
112 		LOG_DBG("NS id %d: lba format %d exceeds number supported (%d)",
113 			id, flbas_fmt, ns->data.nlbaf + 1);
114 		return -EIO;
115 	}
116 
117 	ns->boundary = ns->data.noiob * nvme_namespace_get_sector_size(ns);
118 
119 	if (nvme_controller_has_dataset_mgmt(ctrlr)) {
120 		ns->flags |= NVME_NS_DEALLOCATE_SUPPORTED;
121 	}
122 
123 	vwc_present = (ctrlr->cdata.vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) &
124 		NVME_CTRLR_DATA_VWC_PRESENT_MASK;
125 	if (vwc_present) {
126 		ns->flags |= NVME_NS_FLUSH_SUPPORTED;
127 	}
128 
129 	snprintf(ns->name, NVME_NAMESPACE_NAME_MAX_LENGTH, "nvme%dn%d",
130 		 ctrlr->id, ns->id-1);
131 
132 	if (nvme_namespace_disk_setup(ns, &ns->disk) != 0) {
133 		LOG_ERR("Could not register no disk subsystem");
134 	}
135 
136 	return 0;
137 }
138