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