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 ufshci_mmio_write_4(ctrlr, is,
27 UFSHCIM(UFSHCI_IS_REG_UPMS));
28 break;
29 }
30
31 if (timeout - ticks < 0) {
32 ufshci_printf(ctrlr,
33 "Power mode is not changed "
34 "within %d ms\n",
35 ctrlr->uic_cmd_timeout_in_ms);
36 return (ENXIO);
37 }
38
39 /* TODO: Replace busy-wait with interrupt-based pause. */
40 DELAY(10);
41 }
42
43 /* Check HCS power mode change request status */
44 hcs = ufshci_mmio_read_4(ctrlr, hcs);
45 if (UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs) != 0x01) {
46 ufshci_printf(ctrlr,
47 "Power mode change request status error: 0x%x\n",
48 UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs));
49 return (ENXIO);
50 }
51
52 return (0);
53 }
54
55 int
ufshci_uic_cmd_ready(struct ufshci_controller * ctrlr)56 ufshci_uic_cmd_ready(struct ufshci_controller *ctrlr)
57 {
58 uint32_t hcs;
59 int timeout;
60
61 /* Wait for the HCS flag to change */
62 timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms);
63
64 while (1) {
65 hcs = ufshci_mmio_read_4(ctrlr, hcs);
66 if (UFSHCIV(UFSHCI_HCS_REG_UCRDY, hcs))
67 break;
68
69 if (timeout - ticks < 0) {
70 ufshci_printf(ctrlr,
71 "UIC command is not ready "
72 "within %d ms\n",
73 ctrlr->uic_cmd_timeout_in_ms);
74 return (ENXIO);
75 }
76
77 /* TODO: Replace busy-wait with interrupt-based pause. */
78 DELAY(10);
79 }
80
81 return (0);
82 }
83
84 static int
ufshci_uic_wait_cmd(struct ufshci_controller * ctrlr,struct ufshci_uic_cmd * uic_cmd)85 ufshci_uic_wait_cmd(struct ufshci_controller *ctrlr,
86 struct ufshci_uic_cmd *uic_cmd)
87 {
88 uint32_t is;
89 int timeout;
90
91 mtx_assert(&ctrlr->uic_cmd_lock, MA_OWNED);
92
93 /* Wait for the IS flag to change */
94 timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms);
95 int delta = 10;
96
97 while (1) {
98 is = ufshci_mmio_read_4(ctrlr, is);
99 if (UFSHCIV(UFSHCI_IS_REG_UCCS, is)) {
100 ufshci_mmio_write_4(ctrlr, is,
101 UFSHCIM(UFSHCI_IS_REG_UCCS));
102 break;
103 }
104 if (timeout - ticks < 0) {
105 ufshci_printf(ctrlr,
106 "UIC command is not completed "
107 "within %d ms\n",
108 ctrlr->uic_cmd_timeout_in_ms);
109 return (ENXIO);
110 }
111
112 DELAY(delta);
113 delta = min(1000, delta * 2);
114 }
115
116 return (0);
117 }
118
119 static int
ufshci_uic_send_cmd(struct ufshci_controller * ctrlr,struct ufshci_uic_cmd * uic_cmd,uint32_t * return_value)120 ufshci_uic_send_cmd(struct ufshci_controller *ctrlr,
121 struct ufshci_uic_cmd *uic_cmd, uint32_t *return_value)
122 {
123 int error;
124 uint32_t config_result_code;
125
126 mtx_lock(&ctrlr->uic_cmd_lock);
127
128 error = ufshci_uic_cmd_ready(ctrlr);
129 if (error) {
130 mtx_unlock(&ctrlr->uic_cmd_lock);
131 return (ENXIO);
132 }
133
134 ufshci_mmio_write_4(ctrlr, ucmdarg1, uic_cmd->argument1);
135 ufshci_mmio_write_4(ctrlr, ucmdarg2, uic_cmd->argument2);
136 ufshci_mmio_write_4(ctrlr, ucmdarg3, uic_cmd->argument3);
137
138 ufshci_mmio_write_4(ctrlr, uiccmd, uic_cmd->opcode);
139
140 error = ufshci_uic_wait_cmd(ctrlr, uic_cmd);
141
142 mtx_unlock(&ctrlr->uic_cmd_lock);
143
144 if (error)
145 return (ENXIO);
146
147 config_result_code = ufshci_mmio_read_4(ctrlr, ucmdarg2);
148 if (config_result_code) {
149 ufshci_printf(ctrlr,
150 "Failed to send UIC command. (config result code = 0x%x)\n",
151 config_result_code);
152 }
153
154 if (return_value != NULL)
155 *return_value = ufshci_mmio_read_4(ctrlr, ucmdarg3);
156
157 return (0);
158 }
159
160 int
ufshci_uic_send_dme_link_startup(struct ufshci_controller * ctrlr)161 ufshci_uic_send_dme_link_startup(struct ufshci_controller *ctrlr)
162 {
163 struct ufshci_uic_cmd uic_cmd;
164 uic_cmd.opcode = UFSHCI_DME_LINK_STARTUP;
165 uic_cmd.argument1 = 0;
166 uic_cmd.argument2 = 0;
167 uic_cmd.argument3 = 0;
168
169 return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
170 }
171
172 int
ufshci_uic_send_dme_get(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t * return_value)173 ufshci_uic_send_dme_get(struct ufshci_controller *ctrlr, uint16_t attribute,
174 uint32_t *return_value)
175 {
176 struct ufshci_uic_cmd uic_cmd;
177
178 uic_cmd.opcode = UFSHCI_DME_GET;
179 uic_cmd.argument1 = attribute << 16;
180 uic_cmd.argument2 = 0;
181 uic_cmd.argument3 = 0;
182
183 return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, return_value));
184 }
185
186 int
ufshci_uic_send_dme_set(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t value)187 ufshci_uic_send_dme_set(struct ufshci_controller *ctrlr, uint16_t attribute,
188 uint32_t value)
189 {
190 struct ufshci_uic_cmd uic_cmd;
191
192 uic_cmd.opcode = UFSHCI_DME_SET;
193 uic_cmd.argument1 = attribute << 16;
194 /* This drvier always sets only volatile values. */
195 uic_cmd.argument2 = UFSHCI_ATTR_SET_TYPE_NORMAL << 16;
196 uic_cmd.argument3 = value;
197
198 return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
199 }
200
201 int
ufshci_uic_send_dme_peer_get(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t * return_value)202 ufshci_uic_send_dme_peer_get(struct ufshci_controller *ctrlr,
203 uint16_t attribute, uint32_t *return_value)
204 {
205 struct ufshci_uic_cmd uic_cmd;
206
207 uic_cmd.opcode = UFSHCI_DME_PEER_GET;
208 uic_cmd.argument1 = attribute << 16;
209 uic_cmd.argument2 = 0;
210 uic_cmd.argument3 = 0;
211
212 return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, return_value));
213 }
214
215 int
ufshci_uic_send_dme_peer_set(struct ufshci_controller * ctrlr,uint16_t attribute,uint32_t value)216 ufshci_uic_send_dme_peer_set(struct ufshci_controller *ctrlr,
217 uint16_t attribute, uint32_t value)
218 {
219 struct ufshci_uic_cmd uic_cmd;
220
221 uic_cmd.opcode = UFSHCI_DME_PEER_SET;
222 uic_cmd.argument1 = attribute << 16;
223 /* This drvier always sets only volatile values. */
224 uic_cmd.argument2 = UFSHCI_ATTR_SET_TYPE_NORMAL << 16;
225 uic_cmd.argument3 = value;
226
227 return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
228 }
229
230 int
ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller * ctrlr)231 ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller *ctrlr)
232 {
233 struct ufshci_uic_cmd uic_cmd;
234
235 uic_cmd.opcode = UFSHCI_DME_ENDPOINT_RESET;
236 uic_cmd.argument1 = 0;
237 uic_cmd.argument2 = 0;
238 uic_cmd.argument3 = 0;
239
240 return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
241 }
242