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