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