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