1 /*-
2 * Copyright (c) 2025, Samsung Electronics Co., Ltd.
3 * Written by Jaeyoon Choi
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8 #include <sys/param.h>
9 #include <sys/systm.h>
10 #include <sys/bus.h>
11 #include <sys/sysctl.h>
12
13 #include "ufshci_private.h"
14 #include "ufshci_reg.h"
15
16 static int
ufshci_sysctl_timeout_period(SYSCTL_HANDLER_ARGS)17 ufshci_sysctl_timeout_period(SYSCTL_HANDLER_ARGS)
18 {
19 uint32_t *ptr = arg1;
20 uint32_t newval = *ptr;
21 int error = sysctl_handle_int(oidp, &newval, 0, req);
22
23 if (error || (req->newptr == NULL))
24 return (error);
25
26 if (newval > UFSHCI_MAX_TIMEOUT_PERIOD ||
27 newval < UFSHCI_MIN_TIMEOUT_PERIOD) {
28 return (EINVAL);
29 } else {
30 *ptr = newval;
31 }
32
33 return (0);
34 }
35
36 static int
ufshci_sysctl_num_cmds(SYSCTL_HANDLER_ARGS)37 ufshci_sysctl_num_cmds(SYSCTL_HANDLER_ARGS)
38 {
39 struct ufshci_controller *ctrlr = arg1;
40 int64_t num_cmds = 0;
41 int i;
42
43 num_cmds = ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_cmds;
44
45 if (ctrlr->transfer_req_queue.hwq != NULL) {
46 for (i = 0; i < ctrlr->num_io_queues; i++)
47 num_cmds += ctrlr->transfer_req_queue.hwq[i].num_cmds;
48 }
49
50 return (sysctl_handle_64(oidp, &num_cmds, 0, req));
51 }
52
53 static int
ufshci_sysctl_num_intr_handler_calls(SYSCTL_HANDLER_ARGS)54 ufshci_sysctl_num_intr_handler_calls(SYSCTL_HANDLER_ARGS)
55 {
56 struct ufshci_controller *ctrlr = arg1;
57 int64_t num_intr_handler_calls = 0;
58 int i;
59
60 num_intr_handler_calls =
61 ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_intr_handler_calls;
62
63 if (ctrlr->transfer_req_queue.hwq != NULL) {
64 for (i = 0; i < ctrlr->num_io_queues; i++)
65 num_intr_handler_calls += ctrlr->transfer_req_queue
66 .hwq[i]
67 .num_intr_handler_calls;
68 }
69
70 return (sysctl_handle_64(oidp, &num_intr_handler_calls, 0, req));
71 }
72
73 static int
ufshci_sysctl_num_retries(SYSCTL_HANDLER_ARGS)74 ufshci_sysctl_num_retries(SYSCTL_HANDLER_ARGS)
75 {
76 struct ufshci_controller *ctrlr = arg1;
77 int64_t num_retries = 0;
78 int i;
79
80 num_retries = ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_retries;
81
82 if (ctrlr->transfer_req_queue.hwq != NULL) {
83 for (i = 0; i < ctrlr->num_io_queues; i++)
84 num_retries +=
85 ctrlr->transfer_req_queue.hwq[i].num_retries;
86 }
87
88 return (sysctl_handle_64(oidp, &num_retries, 0, req));
89 }
90
91 static int
ufshci_sysctl_num_failures(SYSCTL_HANDLER_ARGS)92 ufshci_sysctl_num_failures(SYSCTL_HANDLER_ARGS)
93 {
94 struct ufshci_controller *ctrlr = arg1;
95 int64_t num_failures = 0;
96 int i;
97
98 num_failures =
99 ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_failures;
100
101 if (ctrlr->transfer_req_queue.hwq != NULL) {
102 for (i = 0; i < ctrlr->num_io_queues; i++)
103 num_failures +=
104 ctrlr->transfer_req_queue.hwq[i].num_failures;
105 }
106
107 return (sysctl_handle_64(oidp, &num_failures, 0, req));
108 }
109
110 static int
ufshci_sysctl_ahit(SYSCTL_HANDLER_ARGS)111 ufshci_sysctl_ahit(SYSCTL_HANDLER_ARGS)
112 {
113 struct ufshci_controller *ctrlr = arg1;
114 int64_t scale, timer;
115 const int64_t scale_factor = 10;
116
117 scale = UFSHCIV(UFSHCI_AHIT_REG_TS, ctrlr->ufs_dev.ahit);
118 timer = UFSHCIV(UFSHCI_AHIT_REG_AH8ITV, ctrlr->ufs_dev.ahit);
119
120 while (scale--)
121 timer *= scale_factor;
122
123 return (sysctl_handle_64(oidp, &timer, 0, req));
124 }
125
126 static void
ufshci_sysctl_initialize_queue(struct ufshci_hw_queue * hwq,struct sysctl_ctx_list * ctrlr_ctx,struct sysctl_oid * que_tree)127 ufshci_sysctl_initialize_queue(struct ufshci_hw_queue *hwq,
128 struct sysctl_ctx_list *ctrlr_ctx, struct sysctl_oid *que_tree)
129 {
130 struct sysctl_oid_list *que_list = SYSCTL_CHILDREN(que_tree);
131
132 SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "num_entries",
133 CTLFLAG_RD, &hwq->num_entries, 0,
134 "Number of entries in hardware queue");
135 SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "num_trackers",
136 CTLFLAG_RD, &hwq->num_trackers, 0,
137 "Number of trackers pre-allocated for this queue pair");
138 SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "sq_head", CTLFLAG_RD,
139 &hwq->sq_head, 0,
140 "Current head of submission queue (as observed by driver)");
141 SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "sq_tail", CTLFLAG_RD,
142 &hwq->sq_tail, 0,
143 "Current tail of submission queue (as observed by driver)");
144 SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "cq_head", CTLFLAG_RD,
145 &hwq->cq_head, 0,
146 "Current head of completion queue (as observed by driver)");
147
148 SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_cmds", CTLFLAG_RD,
149 &hwq->num_cmds, "Number of commands submitted");
150 SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_intr_handler_calls",
151 CTLFLAG_RD, &hwq->num_intr_handler_calls,
152 "Number of times interrupt handler was invoked (will typically be "
153 "less than number of actual interrupts generated due to "
154 "interrupt aggregation)");
155 SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_retries",
156 CTLFLAG_RD, &hwq->num_retries, "Number of commands retried");
157 SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_failures",
158 CTLFLAG_RD, &hwq->num_failures,
159 "Number of commands ending in failure after all retries");
160
161 /* TODO: Implement num_ignored */
162 /* TODO: Implement recovery state */
163 /* TODO: Implement dump debug */
164 }
165
166 void
ufshci_sysctl_initialize_ctrlr(struct ufshci_controller * ctrlr)167 ufshci_sysctl_initialize_ctrlr(struct ufshci_controller *ctrlr)
168 {
169 struct sysctl_ctx_list *ctrlr_ctx;
170 struct sysctl_oid *ctrlr_tree, *que_tree, *ioq_tree;
171 struct sysctl_oid_list *ctrlr_list, *ioq_list;
172 struct ufshci_device *dev = &ctrlr->ufs_dev;
173 #define QUEUE_NAME_LENGTH 16
174 char queue_name[QUEUE_NAME_LENGTH];
175 int i;
176
177 ctrlr_ctx = device_get_sysctl_ctx(ctrlr->dev);
178 ctrlr_tree = device_get_sysctl_tree(ctrlr->dev);
179 ctrlr_list = SYSCTL_CHILDREN(ctrlr_tree);
180
181 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "major_version",
182 CTLFLAG_RD, &ctrlr->major_version, 0, "UFS spec major version");
183
184 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "minor_version",
185 CTLFLAG_RD, &ctrlr->minor_version, 0, "UFS spec minor version");
186
187 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "io_queue_mode",
188 CTLFLAG_RD, &ctrlr->transfer_req_queue.queue_mode, 0,
189 "Active host-side queuing scheme "
190 "(Single-Doorbell or Multi-Circular-Queue)");
191
192 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_io_queues",
193 CTLFLAG_RD, &ctrlr->num_io_queues, 0, "Number of I/O queue pairs");
194
195 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "cap", CTLFLAG_RD,
196 &ctrlr->cap, 0, "Number of I/O queue pairs");
197
198 SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_enabled",
199 CTLFLAG_RD, &dev->is_wb_enabled, 0, "WriteBooster enable/disable");
200
201 SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_flush_enabled",
202 CTLFLAG_RD, &dev->is_wb_flush_enabled, 0,
203 "WriteBooster flush enable/disable");
204
205 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_buffer_type",
206 CTLFLAG_RD, &dev->wb_buffer_type, 0, "WriteBooster type");
207
208 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_buffer_size_mb",
209 CTLFLAG_RD, &dev->wb_buffer_size_mb, 0,
210 "WriteBooster buffer size in MB");
211
212 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO,
213 "wb_user_space_config_option", CTLFLAG_RD,
214 &dev->wb_user_space_config_option, 0,
215 "WriteBooster preserve user space mode");
216
217 SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "power_mode_supported",
218 CTLFLAG_RD, &dev->power_mode_supported, 0,
219 "Device power mode support");
220
221 SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO,
222 "auto_hibernation_supported", CTLFLAG_RD,
223 &dev->auto_hibernation_supported, 0,
224 "Device auto hibernation support");
225
226 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
227 "auto_hibernate_idle_timer_value",
228 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
229 ufshci_sysctl_ahit, "IU",
230 "Auto-Hibernate Idle Timer Value (in microseconds)");
231
232 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "power_mode",
233 CTLFLAG_RD, &dev->power_mode, 0, "Current device power mode");
234
235 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "timeout_period",
236 CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, &ctrlr->timeout_period,
237 0, ufshci_sysctl_timeout_period, "IU",
238 "Timeout period for I/O queues (in seconds)");
239
240 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_cmds",
241 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
242 ufshci_sysctl_num_cmds, "IU", "Number of commands submitted");
243
244 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
245 "num_intr_handler_calls", CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
246 ctrlr, 0, ufshci_sysctl_num_intr_handler_calls, "IU",
247 "Number of times interrupt handler was invoked (will "
248 "typically be less than number of actual interrupts "
249 "generated due to coalescing)");
250
251 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_retries",
252 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
253 ufshci_sysctl_num_retries, "IU", "Number of commands retried");
254
255 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_failures",
256 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
257 ufshci_sysctl_num_failures, "IU",
258 "Number of commands ending in failure after all retries");
259
260 que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO, "utmrq",
261 CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
262 "UTP Task Management Request Queue");
263
264 ufshci_sysctl_initialize_queue(
265 &ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q], ctrlr_ctx, que_tree);
266
267 /*
268 * Make sure that we've constructed the I/O queues before setting up the
269 * sysctls. Failed controllers won't allocate it, but we want the rest
270 * of the sysctls to diagnose things.
271 */
272 if (ctrlr->transfer_req_queue.hwq != NULL) {
273 ioq_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO,
274 "ioq", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
275 "UTP Transfer Request Queue (I/O Queue)");
276 ioq_list = SYSCTL_CHILDREN(ioq_tree);
277
278 for (i = 0; i < ctrlr->num_io_queues; i++) {
279 snprintf(queue_name, QUEUE_NAME_LENGTH, "%d", i);
280 que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ioq_list,
281 OID_AUTO, queue_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
282 NULL, "IO Queue");
283 ufshci_sysctl_initialize_queue(
284 &ctrlr->transfer_req_queue.hwq[i], ctrlr_ctx,
285 que_tree);
286 }
287 }
288 }
289