1 // SPDX-License-Identifier: GPL-2.0+
2 /**
3 * ufs.c - UFS specific U-boot commands
4 *
5 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
6 *
7 */
8 #include <common.h>
9 #include <command.h>
10 #include <ufs.h>
11
12 #ifndef CONFIG_UFS
do_ufs(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])13 static int do_ufs(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
14 {
15 int dev, ret;
16
17 if (argc >= 2) {
18 if (!strcmp(argv[1], "init")) {
19 if (argc == 3) {
20 dev = simple_strtoul(argv[2], NULL, 10);
21 ret = ufs_probe_dev(dev);
22 if (ret)
23 return CMD_RET_FAILURE;
24 } else {
25 ufs_probe();
26 }
27
28 return CMD_RET_SUCCESS;
29 }
30 }
31
32 return CMD_RET_USAGE;
33 }
34
35 U_BOOT_CMD(ufs, 3, 1, do_ufs,
36 "UFS sub system",
37 "init [dev] - init UFS subsystem\n"
38 );
39 #else
40
41 #define UFS_BLKSIZE_SHIFT 12
42 #define b2m(a) (((a) >> 10) / 1000)
43 #define b2k(a) (((a) >> 10) % 1000)
44 static int curr_device = 0;
45
do_ufs_read(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])46 static int do_ufs_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
47 {
48 u32 blk, cnt;
49 u64 addr;
50 unsigned long long start_ticks, end_ticks;
51 unsigned long long size, speed;
52
53 if (argc != 5) /* 5 arg */
54 return CMD_RET_USAGE;
55
56 addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */
57 blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */
58 cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */
59
60 printf("\nUFS read: dev # %d, block # %d, count %d ... ", curr_device, blk, cnt);
61
62 ufs_storage_init();
63 start_ticks = get_ticks();
64 if (ufs_read_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT, cnt << UFS_BLKSIZE_SHIFT)) {
65 printf("0 blocks read: ERROR\n");
66 return CMD_RET_FAILURE;
67 }
68 end_ticks = get_ticks();
69 printf("%d blocks read: OK\n", cnt);
70
71 size = cnt << UFS_BLKSIZE_SHIFT;
72 speed = (size * CONFIG_SYS_TIMER_RATE) / (end_ticks - start_ticks);
73 printf("%llu.%03llu MB/s\n", b2m(speed), b2k(speed));
74
75 return CMD_RET_SUCCESS;
76 }
77
do_ufs_write(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])78 static int do_ufs_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
79 {
80 u32 blk, cnt;
81 u64 addr;
82 unsigned long long start_ticks, end_ticks;
83 unsigned long long size, speed;
84
85 if (argc != 5) /* 5 arg */
86 return CMD_RET_USAGE;
87
88 addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */
89 blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */
90 cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */
91
92 if (strncmp(argv[0], "write.ext4sp", sizeof("write.ext4sp")) == 0) {
93 #ifdef CONFIG_EXT4_SPARSE
94 printf("\nUFS write ext4 sparse: dev # %d, block # %d, count %d ...\n",
95 curr_device, blk, cnt);
96
97 return ufs_ext4_unsparse((void *)(uintptr_t)addr, blk, cnt);
98 #else
99 printf("Not support.\n");
100 return CMD_RET_SUCCESS;
101 #endif
102 }
103
104 printf("\nUFS write: dev # %d, block # %d, count %d ... ", curr_device, blk, cnt);
105
106 ufs_storage_init();
107 start_ticks = get_ticks();
108 if (ufs_write_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT, cnt << UFS_BLKSIZE_SHIFT)) {
109 printf("0 blocks write: ERROR\n");
110 return CMD_RET_FAILURE;
111 }
112 end_ticks = get_ticks();
113 printf("%d blocks written: OK\n", cnt);
114
115 size = cnt << UFS_BLKSIZE_SHIFT;
116 speed = (size * CONFIG_SYS_TIMER_RATE) / (end_ticks - start_ticks);
117 printf("%llu.%03llu MB/s\n", b2m(speed), b2k(speed));
118
119 /* must write boot data to boot lun due to bootup demand,
120 * start from block 0, no longer than 0x400 blocks(4M).
121 */
122 if ((blk == 0) && (cnt <= 0x400)) {
123 /* write boot data */
124 if (ufs_write_boot_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT,
125 cnt << UFS_BLKSIZE_SHIFT)) {
126 printf("boot write: ERROR\n");
127 return CMD_RET_FAILURE;
128 }
129 }
130
131 return CMD_RET_SUCCESS;
132 }
133
do_ufs_bootread(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])134 static int do_ufs_bootread(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
135 {
136 u32 blk, cnt;
137 u64 addr;
138
139 if (argc != 5) /* 5 arg */
140 return CMD_RET_USAGE;
141
142 addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */
143 blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */
144 cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */
145
146 printf("\nUFS read: dev # %d, block # %d, count %d ... ", curr_device, blk, cnt);
147
148 ufs_storage_init();
149 if (ufs_read_boot_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT,
150 cnt << UFS_BLKSIZE_SHIFT)) {
151 printf("boot read: ERROR\n");
152 return CMD_RET_FAILURE;
153 }
154 printf("%d blocks read: OK\n", cnt);
155
156 return CMD_RET_SUCCESS;
157 }
158
do_ufs_bootwrite(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])159 static int do_ufs_bootwrite(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
160 {
161 u32 blk, cnt;
162 u64 addr;
163
164 if (argc != 5) /* 5 arg */
165 return CMD_RET_USAGE;
166
167 addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */
168 blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */
169 cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */
170
171 printf("\nUFS write: dev # %d, block # %d, count %d ... ", curr_device, blk, cnt);
172
173 ufs_storage_init();
174 if (ufs_write_boot_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT,
175 cnt << UFS_BLKSIZE_SHIFT)) {
176 printf("boot write: ERROR\n");
177 return CMD_RET_FAILURE;
178 }
179 printf("%d blocks write: OK\n", cnt);
180
181 return CMD_RET_SUCCESS;
182 }
183
do_ufs_reinit(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])184 static int do_ufs_reinit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
185 {
186 if (argc != 2) /* 2 arg */
187 return CMD_RET_USAGE;
188
189 ufs_reinit();
190
191 return CMD_RET_SUCCESS;
192 }
193
do_ufs_reg(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])194 static int do_ufs_reg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
195 {
196 if (argc != 2) /* 2 arg */
197 return CMD_RET_USAGE;
198
199 ufs_reg_dump();
200
201 return CMD_RET_SUCCESS;
202 }
203
do_ufs_setlun(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])204 static int do_ufs_setlun(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
205 {
206 u32 lun_num;
207
208 if (argc != 2) /* 2 arg */
209 return CMD_RET_USAGE;
210
211 lun_num = simple_strtoul(argv[1], NULL, 16); /* arg 1: lun, 16 in hex */
212 ufs_set_active_lun(lun_num);
213
214 return CMD_RET_SUCCESS;
215 }
216
do_ufs_bootlun(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])217 static int do_ufs_bootlun(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
218 {
219 u32 lun_num;
220
221 if (argc != 2) /* 2 arg */
222 return CMD_RET_USAGE;
223
224 lun_num = simple_strtoul(argv[1], NULL, 16); /* arg 1: lun, 16 in hex */
225 ufs_set_bootlun(lun_num);
226
227 return CMD_RET_SUCCESS;
228 }
229
do_ufs_hi(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])230 static int do_ufs_hi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
231 {
232 int ret;
233
234 if (argc != 2) /* 2 arg */
235 return CMD_RET_USAGE;
236
237 ret = ufs_hibernate_enter();
238 if (ret)
239 return CMD_RET_FAILURE;
240 printf("hibernate in ok\n");
241 return CMD_RET_SUCCESS;
242 }
243
do_ufs_ho(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])244 static int do_ufs_ho(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
245 {
246 int ret;
247
248 if (argc != 2) /* 2 arg */
249 return CMD_RET_USAGE;
250
251 ret = ufs_hibernate_exit();
252 if (ret)
253 return CMD_RET_FAILURE;
254 printf("hibernate out ok\n");
255 return CMD_RET_SUCCESS;
256 }
257
do_ufs_mode(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])258 static int do_ufs_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
259 {
260 struct pwr_mode_params pmp;
261 uint32_t mode, gear, rate, lane;
262
263 if (argc != 5) /* 5 arg */
264 return CMD_RET_USAGE;
265
266 mode = simple_strtoul(argv[1], NULL, 16); /* arg 1: mode, 16 in hex */
267 gear = simple_strtoul(argv[2], NULL, 16); /* arg 2: gear, 16 in hex */
268 rate = simple_strtoul(argv[3], NULL, 16); /* arg 3: rate, 16 in hex */
269 lane = simple_strtoul(argv[4], NULL, 16); /* arg 4: lane, 16 in hex */
270 mode = (mode << 4) | mode; /* rx shift 4 */
271
272 pmp.pwr_mode = mode;
273 pmp.tx_gear = gear;
274 pmp.rx_gear = gear;
275 pmp.hs_series = rate;
276 pmp.tx_lanes = lane;
277 pmp.rx_lanes = lane;
278
279 printf("UFS %s Gear-%d Rate-%c Lane-%d\n",
280 ((mode == SLOW_MODE) ? "Slow" :
281 ((mode == SLOWAUTO_MODE) ? "SlowAuto" :
282 ((mode == FAST_MODE) ? "Fast" : "FastAuto"))),
283 gear, (rate == 1) ? 'A' : 'B', lane);
284
285 if (do_mode_change(&pmp)) {
286 printf("power mode change fail\n");
287 return CMD_RET_FAILURE;
288 } else {
289 printf("power mode change ok\n");
290 }
291 return CMD_RET_SUCCESS;
292 }
293
294 static cmd_tbl_t cmd_ufs[] = {
295 U_BOOT_CMD_MKENT(read, 5, 0, do_ufs_read, "", ""),
296 U_BOOT_CMD_MKENT(write, 5, 0, do_ufs_write, "", ""),
297 U_BOOT_CMD_MKENT(bootread, 5, 0, do_ufs_bootread, "", ""),
298 U_BOOT_CMD_MKENT(bootwrite, 5, 0, do_ufs_bootwrite, "", ""),
299 U_BOOT_CMD_MKENT(reinit, 2, 0, do_ufs_reinit, "", ""),
300 U_BOOT_CMD_MKENT(reg, 2, 0, do_ufs_reg, "", ""),
301 U_BOOT_CMD_MKENT(setlun, 2, 0, do_ufs_setlun, "", ""),
302 U_BOOT_CMD_MKENT(bootlun, 2, 0, do_ufs_bootlun, "", ""),
303 U_BOOT_CMD_MKENT(hi, 2, 0, do_ufs_hi, "", ""),
304 U_BOOT_CMD_MKENT(ho, 2, 0, do_ufs_ho, "", ""),
305 U_BOOT_CMD_MKENT(mode, 5, 0, do_ufs_mode, "", ""),
306 };
307
do_ufsops(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])308 static int do_ufsops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
309 {
310 cmd_tbl_t *cp = NULL;
311
312 if (argc == 1) {
313 cmd_usage(cmdtp);
314 return CMD_RET_SUCCESS;
315 }
316
317 cp = find_cmd_tbl(argv[1], cmd_ufs, ARRAY_SIZE(cmd_ufs));
318
319 /* Drop the ufs command */
320 argc--;
321 argv++;
322
323 if (cp == NULL || argc > cp->maxargs)
324 return CMD_RET_USAGE;
325
326 return cp->cmd(cmdtp, flag, argc, argv);
327 }
328
329 U_BOOT_CMD(
330 ufs, 6, 0, do_ufsops,
331 "UFS sub system",
332 "read <device num> addr blk# cnt\n"
333 "ufs write <device num> addr blk# cnt\n"
334 "ufs write.ext4sp <device num> addr blk# cnt\n"
335 "ufs bootread <device num> addr blk# cnt\n"
336 "ufs bootwrite <device num> addr blk# cnt\n"
337 "ufs reinit <device num>\n"
338 "ufs reg <device num>\n"
339 );
340
do_ufsinfo(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])341 static int do_ufsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
342 {
343 u32 type;
344
345 switch (argc) {
346 case 0: /* 0 arg */
347 case 1: /* 1 arg */
348 ufs_show_desc_info(0xF);
349 break;
350 case 2: /* 2 arg */
351 if (strncmp(argv[1], "-h", sizeof("-h")) == 0) {
352 cmd_usage(cmdtp);
353 break;
354 }
355
356 type = simple_strtoul(argv[1], NULL, 16); /* arg 1: type, 16 in hex */
357 ufs_show_desc_info(type);
358 break;
359 default:
360 return CMD_RET_USAGE;
361 }
362
363 return CMD_RET_SUCCESS;
364 }
365
366 U_BOOT_CMD(
367 ufsinfo, 2, 0, do_ufsinfo,
368 "display UFS info",
369 "<idn>\n"
370 " idn :0x0 -- device descriptor\n"
371 " idn :0x1 -- configuration descriptor\n"
372 " idn :0x2 -- unit descriptor\n"
373 " idn :0x4 -- interconnect descriptor\n"
374 " idn :0x5 -- string descriptor\n"
375 " idn :0x7 -- geometry descriptor\n"
376 " idn :0x9 -- health descriptor\n"
377 " idn :0xE -- show all info\n"
378 " idn :0xF -- show basic info\n"
379 );
380 #endif
381