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 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 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 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 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 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 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 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 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 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 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