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, "Host controller capabilities register value");
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,
218 "auto_hibernation_supported", CTLFLAG_RD,
219 &dev->auto_hibernation_supported, 0,
220 "Device auto hibernation support");
221
222 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
223 "auto_hibernate_idle_timer_value",
224 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
225 ufshci_sysctl_ahit, "IU",
226 "Auto-Hibernate Idle Timer Value (in microseconds)");
227
228 SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "power_mode_supported",
229 CTLFLAG_RD, &dev->power_mode_supported, 0,
230 "Device power mode support");
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_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "tx_rx_power_mode",
236 CTLFLAG_RD, &ctrlr->tx_rx_power_mode, 0,
237 "Current TX/RX PA_PWRMode value");
238
239 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "max_tx_lanes",
240 CTLFLAG_RD, &ctrlr->max_tx_lanes, 0,
241 "Maximum available TX data lanes");
242
243 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "max_rx_lanes",
244 CTLFLAG_RD, &ctrlr->max_rx_lanes, 0,
245 "Maximum available RX data lanes");
246
247 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "tx_lanes", CTLFLAG_RD,
248 &ctrlr->tx_lanes, 0, "Active TX data lanes");
249
250 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "rx_lanes", CTLFLAG_RD,
251 &ctrlr->rx_lanes, 0, "Active RX data lanes");
252
253 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "max_rx_hs_gear",
254 CTLFLAG_RD, &ctrlr->max_rx_hs_gear, 0,
255 "Maximum available RX HS gear");
256
257 SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "hs_gear", CTLFLAG_RD,
258 &ctrlr->hs_gear, 0, "Active HS gear");
259
260 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "timeout_period",
261 CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, &ctrlr->timeout_period,
262 0, ufshci_sysctl_timeout_period, "IU",
263 "Timeout period for I/O queues (in seconds)");
264
265 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_cmds",
266 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
267 ufshci_sysctl_num_cmds, "IU", "Number of commands submitted");
268
269 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
270 "num_intr_handler_calls", CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
271 ctrlr, 0, ufshci_sysctl_num_intr_handler_calls, "IU",
272 "Number of times interrupt handler was invoked (will "
273 "typically be less than number of actual interrupts "
274 "generated due to coalescing)");
275
276 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_retries",
277 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
278 ufshci_sysctl_num_retries, "IU", "Number of commands retried");
279
280 SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_failures",
281 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
282 ufshci_sysctl_num_failures, "IU",
283 "Number of commands ending in failure after all retries");
284
285 que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO, "utmrq",
286 CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
287 "UTP Task Management Request Queue");
288
289 ufshci_sysctl_initialize_queue(
290 &ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q], ctrlr_ctx, que_tree);
291
292 /*
293 * Make sure that we've constructed the I/O queues before setting up the
294 * sysctls. Failed controllers won't allocate it, but we want the rest
295 * of the sysctls to diagnose things.
296 */
297 if (ctrlr->transfer_req_queue.hwq != NULL) {
298 ioq_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO,
299 "ioq", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
300 "UTP Transfer Request Queue (I/O Queue)");
301 ioq_list = SYSCTL_CHILDREN(ioq_tree);
302
303 for (i = 0; i < ctrlr->num_io_queues; i++) {
304 snprintf(queue_name, QUEUE_NAME_LENGTH, "%d", i);
305 que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ioq_list,
306 OID_AUTO, queue_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
307 NULL, "IO Queue");
308 ufshci_sysctl_initialize_queue(
309 &ctrlr->transfer_req_queue.hwq[i], ctrlr_ctx,
310 que_tree);
311 }
312 }
313 }
314