xref: /freebsd/sys/dev/ufshci/ufshci_uic_cmd.c (revision 7d748c594ef0ddc45b3b8d0c4d368150e975724e)
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