xref: /freebsd/sys/dev/ufshci/ufshci_uic_cmd.c (revision db8b06468baefb9d9b5db5f91084d85071bfb5cb)
1 /*-
2  * Copyright (c) 2025, Samsung Electronics Co., Ltd.
3  * Written by Jaeyoon Choi
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 #include <sys/param.h>
8 #include <sys/bus.h>
9 #include <sys/conf.h>
10 
11 #include "ufshci_private.h"
12 #include "ufshci_reg.h"
13 
14 int
ufshci_uic_power_mode_ready(struct ufshci_controller * ctrlr)15 ufshci_uic_power_mode_ready(struct ufshci_controller *ctrlr)
16 {
17 	uint32_t is, hcs;
18 	int timeout;
19 
20 	/* Wait for the IS flag to change */
21 	timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms);
22 
23 	while (1) {
24 		is = ufshci_mmio_read_4(ctrlr, is);
25 		if (UFSHCIV(UFSHCI_IS_REG_UPMS, is)) {
26 			/* Clear 'Power Mode completion status' */
27 			ufshci_mmio_write_4(ctrlr, is,
28 			    UFSHCIM(UFSHCI_IS_REG_UPMS));
29 			break;
30 		}
31 
32 		if (timeout - ticks < 0) {
33 			ufshci_printf(ctrlr,
34 			    "Power mode is not changed "
35 			    "within %d ms\n",
36 			    ctrlr->device_init_timeout_in_ms);
37 			return (ENXIO);
38 		}
39 
40 		/* TODO: Replace busy-wait with interrupt-based pause. */
41 		DELAY(10);
42 	}
43 
44 	/* Check HCS power mode change request status */
45 	hcs = ufshci_mmio_read_4(ctrlr, hcs);
46 	if (UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs) != 0x01) {
47 		ufshci_printf(ctrlr,
48 		    "Power mode change request status error: 0x%x\n",
49 		    UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs));
50 		return (ENXIO);
51 	}
52 
53 	return (0);
54 }
55 
56 int
ufshci_uic_hibernation_ready(struct ufshci_controller * ctrlr)57 ufshci_uic_hibernation_ready(struct ufshci_controller *ctrlr)
58 {
59 	uint32_t is, hcs;
60 	int timeout;
61 
62 	/* Wait for the IS flag to change */
63 	timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms);
64 
65 	while (1) {
66 		is = ufshci_mmio_read_4(ctrlr, is);
67 		if (UFSHCIV(UFSHCI_IS_REG_UHES, is)) {
68 			/* Clear 'UIC Hibernate Enter Status' */
69 			ufshci_mmio_write_4(ctrlr, is,
70 			    UFSHCIM(UFSHCI_IS_REG_UHES));
71 			break;
72 		}
73 		if (UFSHCIV(UFSHCI_IS_REG_UHXS, is)) {
74 			/* Clear 'UIC Hibernate Exit Status' */
75 			ufshci_mmio_write_4(ctrlr, is,
76 			    UFSHCIM(UFSHCI_IS_REG_UHXS));
77 			break;
78 		}
79 
80 		if (timeout - ticks < 0) {
81 			ufshci_printf(ctrlr,
82 			    "Hibernation enter/exit are not completed "
83 			    "within %d ms\n",
84 			    ctrlr->uic_cmd_timeout_in_ms);
85 			return (ENXIO);
86 		}
87 
88 		/* TODO: Replace busy-wait with interrupt-based pause. */
89 		DELAY(10);
90 	}
91 
92 	/* Check HCS power mode change request status */
93 	hcs = ufshci_mmio_read_4(ctrlr, hcs);
94 	if (UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs) != 0x01) {
95 		ufshci_printf(ctrlr,
96 		    "Hibernation enter/exit request status error: 0x%x\n",
97 		    UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs));
98 		return (ENXIO);
99 	}
100 
101 	return (0);
102 }
103 
104 int
ufshci_uic_cmd_ready(struct ufshci_controller * ctrlr)105 ufshci_uic_cmd_ready(struct ufshci_controller *ctrlr)
106 {
107 	uint32_t hcs;
108 	int timeout;
109 
110 	/* Wait for the HCS flag to change */
111 	timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms);
112 
113 	while (1) {
114 		hcs = ufshci_mmio_read_4(ctrlr, hcs);
115 		if (UFSHCIV(UFSHCI_HCS_REG_UCRDY, hcs))
116 			break;
117 
118 		if (timeout - ticks < 0) {
119 			ufshci_printf(ctrlr,
120 			    "UIC command is not ready "
121 			    "within %d ms\n",
122 			    ctrlr->uic_cmd_timeout_in_ms);
123 			return (ENXIO);
124 		}
125 
126 		/* TODO: Replace busy-wait with interrupt-based pause. */
127 		DELAY(10);
128 	}
129 
130 	return (0);
131 }
132 
133 static int
ufshci_uic_wait_cmd(struct ufshci_controller * ctrlr,struct ufshci_uic_cmd * uic_cmd)134 ufshci_uic_wait_cmd(struct ufshci_controller *ctrlr,
135     struct ufshci_uic_cmd *uic_cmd)
136 {
137 	uint32_t is;
138 	int timeout;
139 
140 	mtx_assert(&ctrlr->uic_cmd_lock, MA_OWNED);
141 
142 	/* Wait for the IS flag to change */
143 	timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms);
144 	int delta = 10;
145 
146 	while (1) {
147 		is = ufshci_mmio_read_4(ctrlr, is);
148 		if (UFSHCIV(UFSHCI_IS_REG_UCCS, is)) {
149 			ufshci_mmio_write_4(ctrlr, is,
150 			    UFSHCIM(UFSHCI_IS_REG_UCCS));
151 			break;
152 		}
153 		if (timeout - ticks < 0) {
154 			ufshci_printf(ctrlr,
155 			    "UIC command is not completed "
156 			    "within %d ms\n",
157 			    ctrlr->uic_cmd_timeout_in_ms);
158 			return (ENXIO);
159 		}
160 
161 		DELAY(delta);
162 		delta = min(1000, delta * 2);
163 	}
164 
165 	return (0);
166 }
167 
168 static int
ufshci_uic_send_cmd(struct ufshci_controller * ctrlr,struct ufshci_uic_cmd * uic_cmd,uint32_t * return_value)169 ufshci_uic_send_cmd(struct ufshci_controller *ctrlr,
170     struct ufshci_uic_cmd *uic_cmd, uint32_t *return_value)
171 {
172 	int error;
173 	uint32_t config_result_code;
174 
175 	mtx_lock(&ctrlr->uic_cmd_lock);
176 
177 	error = ufshci_uic_cmd_ready(ctrlr);
178 	if (error) {
179 		mtx_unlock(&ctrlr->uic_cmd_lock);
180 		return (ENXIO);
181 	}
182 
183 	ufshci_mmio_write_4(ctrlr, ucmdarg1, uic_cmd->argument1);
184 	ufshci_mmio_write_4(ctrlr, ucmdarg2, uic_cmd->argument2);
185 	ufshci_mmio_write_4(ctrlr, ucmdarg3, uic_cmd->argument3);
186 
187 	ufshci_mmio_write_4(ctrlr, uiccmd, uic_cmd->opcode);
188 
189 	error = ufshci_uic_wait_cmd(ctrlr, uic_cmd);
190 
191 	mtx_unlock(&ctrlr->uic_cmd_lock);
192 
193 	if (error)
194 		return (ENXIO);
195 
196 	config_result_code = ufshci_mmio_read_4(ctrlr, ucmdarg2);
197 	if (config_result_code) {
198 		ufshci_printf(ctrlr,
199 		    "Failed to send UIC command (Opcode: 0x%x"
200 		    ", config result code = 0x%x)\n",
201 		    uic_cmd->opcode, config_result_code);
202 	}
203 
204 	if (return_value != NULL)
205 		*return_value = ufshci_mmio_read_4(ctrlr, ucmdarg3);
206 
207 	return (0);
208 }
209 
210 int
ufshci_uic_send_dme_link_startup(struct ufshci_controller * ctrlr)211 ufshci_uic_send_dme_link_startup(struct ufshci_controller *ctrlr)
212 {
213 	struct ufshci_uic_cmd uic_cmd;
214 	uic_cmd.opcode = UFSHCI_DME_LINK_STARTUP;
215 	uic_cmd.argument1 = 0;
216 	uic_cmd.argument2 = 0;
217 	uic_cmd.argument3 = 0;
218 
219 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
220 }
221 
222 int
ufshci_uic_send_dme_get(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t * return_value)223 ufshci_uic_send_dme_get(struct ufshci_controller *ctrlr, uint16_t attribute,
224     uint32_t *return_value)
225 {
226 	struct ufshci_uic_cmd uic_cmd;
227 
228 	uic_cmd.opcode = UFSHCI_DME_GET;
229 	uic_cmd.argument1 = attribute << 16;
230 	uic_cmd.argument2 = 0;
231 	uic_cmd.argument3 = 0;
232 
233 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, return_value));
234 }
235 
236 int
ufshci_uic_send_dme_set(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t value)237 ufshci_uic_send_dme_set(struct ufshci_controller *ctrlr, uint16_t attribute,
238     uint32_t value)
239 {
240 	struct ufshci_uic_cmd uic_cmd;
241 
242 	uic_cmd.opcode = UFSHCI_DME_SET;
243 	uic_cmd.argument1 = attribute << 16;
244 	/* This drvier always sets only volatile values. */
245 	uic_cmd.argument2 = UFSHCI_ATTR_SET_TYPE_NORMAL << 16;
246 	uic_cmd.argument3 = value;
247 
248 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
249 }
250 
251 int
ufshci_uic_send_dme_peer_get(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t * return_value)252 ufshci_uic_send_dme_peer_get(struct ufshci_controller *ctrlr,
253     uint16_t attribute, uint32_t *return_value)
254 {
255 	struct ufshci_uic_cmd uic_cmd;
256 
257 	uic_cmd.opcode = UFSHCI_DME_PEER_GET;
258 	uic_cmd.argument1 = attribute << 16;
259 	uic_cmd.argument2 = 0;
260 	uic_cmd.argument3 = 0;
261 
262 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, return_value));
263 }
264 
265 int
ufshci_uic_send_dme_peer_set(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t value)266 ufshci_uic_send_dme_peer_set(struct ufshci_controller *ctrlr,
267     uint16_t attribute, uint32_t value)
268 {
269 	struct ufshci_uic_cmd uic_cmd;
270 
271 	uic_cmd.opcode = UFSHCI_DME_PEER_SET;
272 	uic_cmd.argument1 = attribute << 16;
273 	/* This drvier always sets only volatile values. */
274 	uic_cmd.argument2 = UFSHCI_ATTR_SET_TYPE_NORMAL << 16;
275 	uic_cmd.argument3 = value;
276 
277 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
278 }
279 
280 int
ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller * ctrlr)281 ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller *ctrlr)
282 {
283 	struct ufshci_uic_cmd uic_cmd;
284 
285 	uic_cmd.opcode = UFSHCI_DME_ENDPOINT_RESET;
286 	uic_cmd.argument1 = 0;
287 	uic_cmd.argument2 = 0;
288 	uic_cmd.argument3 = 0;
289 
290 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
291 }
292 
293 int
ufshci_uic_send_dme_hibernate_enter(struct ufshci_controller * ctrlr)294 ufshci_uic_send_dme_hibernate_enter(struct ufshci_controller *ctrlr)
295 {
296 	struct ufshci_uic_cmd uic_cmd;
297 
298 	uic_cmd.opcode = UFSHCI_DME_HIBERNATE_ENTER;
299 	uic_cmd.argument1 = 0;
300 	uic_cmd.argument2 = 0;
301 	uic_cmd.argument3 = 0;
302 
303 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
304 }
305 
306 int
ufshci_uic_send_dme_hibernate_exit(struct ufshci_controller * ctrlr)307 ufshci_uic_send_dme_hibernate_exit(struct ufshci_controller *ctrlr)
308 {
309 	struct ufshci_uic_cmd uic_cmd;
310 
311 	uic_cmd.opcode = UFSHCI_DME_HIBERNATE_EXIT;
312 	uic_cmd.argument1 = 0;
313 	uic_cmd.argument2 = 0;
314 	uic_cmd.argument3 = 0;
315 
316 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
317 }
318