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