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