1aa9b1b59SFrank Wang /* 2aa9b1b59SFrank Wang * Copyright 2017 Rockchip Electronics Co., Ltd 3aa9b1b59SFrank Wang * Frank Wang <frank.wang@rock-chips.com> 4aa9b1b59SFrank Wang * 5aa9b1b59SFrank Wang * SPDX-License-Identifier: GPL-2.0+ 6aa9b1b59SFrank Wang */ 7aa9b1b59SFrank Wang 8aa9b1b59SFrank Wang #include <errno.h> 9aa9b1b59SFrank Wang #include <common.h> 10aa9b1b59SFrank Wang #include <command.h> 11aa9b1b59SFrank Wang #include <console.h> 12c293902fSJon Lin #include <dm/device.h> 13aa9b1b59SFrank Wang #include <g_dnl.h> 14aa9b1b59SFrank Wang #include <part.h> 15aa9b1b59SFrank Wang #include <usb.h> 16aa9b1b59SFrank Wang #include <usb_mass_storage.h> 17aa9b1b59SFrank Wang #include <rockusb.h> 18aa9b1b59SFrank Wang 19aa9b1b59SFrank Wang static struct rockusb rkusb; 20aa9b1b59SFrank Wang static struct rockusb *g_rkusb; 21aa9b1b59SFrank Wang 22aa9b1b59SFrank Wang static int rkusb_read_sector(struct ums *ums_dev, 23aa9b1b59SFrank Wang ulong start, lbaint_t blkcnt, void *buf) 24aa9b1b59SFrank Wang { 25aa9b1b59SFrank Wang struct blk_desc *block_dev = &ums_dev->block_dev; 26aa9b1b59SFrank Wang lbaint_t blkstart = start + ums_dev->start_sector; 27b4609917SFrank Wang int ret; 28aa9b1b59SFrank Wang 29957c1cf2SShunqian Zheng if ((blkstart + blkcnt) > RKUSB_READ_LIMIT_ADDR) { 30628c8271SJason Zhu memset(buf, 0xcc, blkcnt * SECTOR_SIZE); 31628c8271SJason Zhu return blkcnt; 32628c8271SJason Zhu } else { 33b4609917SFrank Wang ret = blk_dread(block_dev, blkstart, blkcnt, buf); 34b4609917SFrank Wang if (!ret) 35b4609917SFrank Wang ret = -EIO; 36b4609917SFrank Wang return ret; 37aa9b1b59SFrank Wang } 38628c8271SJason Zhu } 39aa9b1b59SFrank Wang 40aa9b1b59SFrank Wang static int rkusb_write_sector(struct ums *ums_dev, 41aa9b1b59SFrank Wang ulong start, lbaint_t blkcnt, const void *buf) 42aa9b1b59SFrank Wang { 43aa9b1b59SFrank Wang struct blk_desc *block_dev = &ums_dev->block_dev; 44aa9b1b59SFrank Wang lbaint_t blkstart = start + ums_dev->start_sector; 45c293902fSJon Lin struct blk_desc *mtd_blk = NULL; 46177c8736SJon Lin int ret; 47aa9b1b59SFrank Wang 48c293902fSJon Lin if (block_dev->if_type == IF_TYPE_MTD) { 49c293902fSJon Lin mtd_blk = dev_get_uclass_platdata(block_dev->bdev); 50c293902fSJon Lin mtd_blk->op_flag |= BLK_MTD_CONT_WRITE; 51c293902fSJon Lin } 52b4609917SFrank Wang 53177c8736SJon Lin ret = blk_dwrite(block_dev, blkstart, blkcnt, buf); 54b4609917SFrank Wang if (!ret) 55b4609917SFrank Wang ret = -EIO; 56bc670a72SYifeng Zhao #if defined(CONFIG_SCSI) && defined(CONFIG_CMD_SCSI) && (defined(CONFIG_UFS)) 57bc670a72SYifeng Zhao if (block_dev->if_type == IF_TYPE_SCSI && block_dev->rawblksz == 4096) { 58bc670a72SYifeng Zhao /* write loader to UFS BootA */ 59bc670a72SYifeng Zhao if (blkstart < 8192 && blkstart >= 64) 60bc670a72SYifeng Zhao blk_write_devnum(IF_TYPE_SCSI, 1, blkstart, blkcnt, (ulong *)buf); 61bc670a72SYifeng Zhao } 62bc670a72SYifeng Zhao #endif 63c293902fSJon Lin if (block_dev->if_type == IF_TYPE_MTD) { 64c293902fSJon Lin mtd_blk->op_flag &= ~(BLK_MTD_CONT_WRITE); 65c293902fSJon Lin } 66177c8736SJon Lin return ret; 67aa9b1b59SFrank Wang } 68aa9b1b59SFrank Wang 69aa9b1b59SFrank Wang static int rkusb_erase_sector(struct ums *ums_dev, 70aa9b1b59SFrank Wang ulong start, lbaint_t blkcnt) 71aa9b1b59SFrank Wang { 72aa9b1b59SFrank Wang struct blk_desc *block_dev = &ums_dev->block_dev; 73aa9b1b59SFrank Wang lbaint_t blkstart = start + ums_dev->start_sector; 74aa9b1b59SFrank Wang 75712e1ef7SYifeng Zhao #if defined(CONFIG_SCSI) && defined(CONFIG_CMD_SCSI) && (defined(CONFIG_UFS)) 76712e1ef7SYifeng Zhao if (block_dev->if_type == IF_TYPE_SCSI && block_dev->rawblksz == 4096) { 77712e1ef7SYifeng Zhao /* write loader to UFS BootA */ 78712e1ef7SYifeng Zhao if (blkstart < 8192) { 79712e1ef7SYifeng Zhao lbaint_t cur_cnt = 8192 - blkstart; 80712e1ef7SYifeng Zhao 81712e1ef7SYifeng Zhao if (cur_cnt > blkcnt) 82712e1ef7SYifeng Zhao cur_cnt = blkcnt; 83712e1ef7SYifeng Zhao blk_erase_devnum(IF_TYPE_SCSI, 1, blkstart, cur_cnt); 84712e1ef7SYifeng Zhao blkcnt -= cur_cnt; 85712e1ef7SYifeng Zhao blkstart += cur_cnt; 86712e1ef7SYifeng Zhao } 87712e1ef7SYifeng Zhao } 88712e1ef7SYifeng Zhao #endif 89712e1ef7SYifeng Zhao 90aa9b1b59SFrank Wang return blk_derase(block_dev, blkstart, blkcnt); 91aa9b1b59SFrank Wang } 92aa9b1b59SFrank Wang 93aa9b1b59SFrank Wang static void rkusb_fini(void) 94aa9b1b59SFrank Wang { 95aa9b1b59SFrank Wang int i; 96aa9b1b59SFrank Wang 97aa9b1b59SFrank Wang for (i = 0; i < g_rkusb->ums_cnt; i++) 98aa9b1b59SFrank Wang free((void *)g_rkusb->ums[i].name); 99aa9b1b59SFrank Wang free(g_rkusb->ums); 100aa9b1b59SFrank Wang g_rkusb->ums = NULL; 101aa9b1b59SFrank Wang g_rkusb->ums_cnt = 0; 102f5b174d4SFrank Wang g_rkusb = NULL; 103aa9b1b59SFrank Wang } 104aa9b1b59SFrank Wang 105aa9b1b59SFrank Wang #define RKUSB_NAME_LEN 16 106aa9b1b59SFrank Wang 107aa9b1b59SFrank Wang static int rkusb_init(const char *devtype, const char *devnums_part_str) 108aa9b1b59SFrank Wang { 109aa9b1b59SFrank Wang char *s, *t, *devnum_part_str, *name; 110aa9b1b59SFrank Wang struct blk_desc *block_dev; 111aa9b1b59SFrank Wang disk_partition_t info; 112aa9b1b59SFrank Wang int partnum, cnt; 113aa9b1b59SFrank Wang int ret = -1; 114aa9b1b59SFrank Wang struct ums *ums_new; 115aa9b1b59SFrank Wang 116aa9b1b59SFrank Wang s = strdup(devnums_part_str); 117aa9b1b59SFrank Wang if (!s) 118aa9b1b59SFrank Wang return -1; 119aa9b1b59SFrank Wang 120aa9b1b59SFrank Wang t = s; 121aa9b1b59SFrank Wang g_rkusb->ums_cnt = 0; 122aa9b1b59SFrank Wang 123aa9b1b59SFrank Wang for (;;) { 124aa9b1b59SFrank Wang devnum_part_str = strsep(&t, ","); 125aa9b1b59SFrank Wang if (!devnum_part_str) 126aa9b1b59SFrank Wang break; 127bc670a72SYifeng Zhao #if defined(CONFIG_SCSI) && defined(CONFIG_CMD_SCSI) && (defined(CONFIG_UFS)) 128bc670a72SYifeng Zhao if (!strcmp(devtype, "scsi")) { 129bc670a72SYifeng Zhao block_dev= blk_get_devnum_by_typename(devtype, 0); 130bc670a72SYifeng Zhao if (block_dev == NULL) 131bc670a72SYifeng Zhao return -ENXIO; 132bc670a72SYifeng Zhao } else 133bc670a72SYifeng Zhao #endif 134bc670a72SYifeng Zhao { 135aa9b1b59SFrank Wang partnum = blk_get_device_part_str(devtype, devnum_part_str, 136aa9b1b59SFrank Wang &block_dev, &info, 1); 137aa9b1b59SFrank Wang if (partnum < 0) 138aa9b1b59SFrank Wang goto cleanup; 139bc670a72SYifeng Zhao } 140aa9b1b59SFrank Wang 141aa9b1b59SFrank Wang /* f_mass_storage.c assumes SECTOR_SIZE sectors */ 142aa9b1b59SFrank Wang if (block_dev->blksz != SECTOR_SIZE) 143aa9b1b59SFrank Wang goto cleanup; 144aa9b1b59SFrank Wang 145aa9b1b59SFrank Wang ums_new = realloc(g_rkusb->ums, (g_rkusb->ums_cnt + 1) * 146aa9b1b59SFrank Wang sizeof(*g_rkusb->ums)); 147aa9b1b59SFrank Wang if (!ums_new) 148aa9b1b59SFrank Wang goto cleanup; 149aa9b1b59SFrank Wang g_rkusb->ums = ums_new; 150aa9b1b59SFrank Wang cnt = g_rkusb->ums_cnt; 151aa9b1b59SFrank Wang 152dd31eefeSFrank Wang /* Expose all partitions for rockusb command */ 153aa9b1b59SFrank Wang g_rkusb->ums[cnt].start_sector = 0; 154aa9b1b59SFrank Wang g_rkusb->ums[cnt].num_sectors = block_dev->lba; 155aa9b1b59SFrank Wang 156aa9b1b59SFrank Wang g_rkusb->ums[cnt].read_sector = rkusb_read_sector; 157aa9b1b59SFrank Wang g_rkusb->ums[cnt].write_sector = rkusb_write_sector; 158aa9b1b59SFrank Wang g_rkusb->ums[cnt].erase_sector = rkusb_erase_sector; 159aa9b1b59SFrank Wang 160aa9b1b59SFrank Wang name = malloc(RKUSB_NAME_LEN); 161aa9b1b59SFrank Wang if (!name) 162aa9b1b59SFrank Wang goto cleanup; 163aa9b1b59SFrank Wang snprintf(name, RKUSB_NAME_LEN, "rkusb disk %d", cnt); 164aa9b1b59SFrank Wang g_rkusb->ums[cnt].name = name; 165aa9b1b59SFrank Wang g_rkusb->ums[cnt].block_dev = *block_dev; 166aa9b1b59SFrank Wang 167aa9b1b59SFrank Wang printf("RKUSB: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n", 168aa9b1b59SFrank Wang g_rkusb->ums_cnt, 169aa9b1b59SFrank Wang g_rkusb->ums[cnt].block_dev.devnum, 170aa9b1b59SFrank Wang g_rkusb->ums[cnt].block_dev.hwpart, 171aa9b1b59SFrank Wang g_rkusb->ums[cnt].start_sector, 172aa9b1b59SFrank Wang g_rkusb->ums[cnt].num_sectors); 173aa9b1b59SFrank Wang 174aa9b1b59SFrank Wang g_rkusb->ums_cnt++; 175aa9b1b59SFrank Wang } 176aa9b1b59SFrank Wang 177aa9b1b59SFrank Wang if (g_rkusb->ums_cnt) 178aa9b1b59SFrank Wang ret = 0; 179aa9b1b59SFrank Wang 180aa9b1b59SFrank Wang cleanup: 181aa9b1b59SFrank Wang free(s); 182aa9b1b59SFrank Wang if (ret < 0) 183aa9b1b59SFrank Wang rkusb_fini(); 184aa9b1b59SFrank Wang 185aa9b1b59SFrank Wang return ret; 186aa9b1b59SFrank Wang } 187aa9b1b59SFrank Wang 18836c87911Swilliam.wu void rkusb_force_to_usb2(bool enable) 18936c87911Swilliam.wu { 19079553f2dSWilliam Wu if (g_rkusb) 19136c87911Swilliam.wu g_rkusb->force_usb2 = enable; 19236c87911Swilliam.wu } 19336c87911Swilliam.wu 19436c87911Swilliam.wu bool rkusb_force_usb2_enabled(void) 19536c87911Swilliam.wu { 19679553f2dSWilliam Wu if (!g_rkusb) 19779553f2dSWilliam Wu return true; 19879553f2dSWilliam Wu 19936c87911Swilliam.wu return g_rkusb->force_usb2; 20036c87911Swilliam.wu } 20136c87911Swilliam.wu 20236c87911Swilliam.wu void rkusb_switch_to_usb3_enable(bool enable) 20336c87911Swilliam.wu { 20479553f2dSWilliam Wu if (g_rkusb) 20536c87911Swilliam.wu g_rkusb->switch_usb3 = enable; 20636c87911Swilliam.wu } 20736c87911Swilliam.wu 20836c87911Swilliam.wu bool rkusb_switch_usb3_enabled(void) 20936c87911Swilliam.wu { 21079553f2dSWilliam Wu if (!g_rkusb) 21179553f2dSWilliam Wu return false; 21279553f2dSWilliam Wu 21336c87911Swilliam.wu return g_rkusb->switch_usb3; 21436c87911Swilliam.wu } 21536c87911Swilliam.wu 216aa9b1b59SFrank Wang static int do_rkusb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) 217aa9b1b59SFrank Wang { 218aa9b1b59SFrank Wang const char *usb_controller; 219aa9b1b59SFrank Wang const char *devtype; 220aa9b1b59SFrank Wang const char *devnum; 221aa9b1b59SFrank Wang unsigned int controller_index; 222aa9b1b59SFrank Wang int rc; 223aa9b1b59SFrank Wang int cable_ready_timeout __maybe_unused; 224fc1a5563SJason Zhu const char *s; 225aa9b1b59SFrank Wang 226aa9b1b59SFrank Wang if (argc != 4) 227aa9b1b59SFrank Wang return CMD_RET_USAGE; 228aa9b1b59SFrank Wang 229aa9b1b59SFrank Wang usb_controller = argv[1]; 230aa9b1b59SFrank Wang devtype = argv[2]; 231aa9b1b59SFrank Wang devnum = argv[3]; 232aa9b1b59SFrank Wang 233535b44c0SJoseph Chen if (!strcmp(devtype, "mmc") && !strcmp(devnum, "1")) { 234535b44c0SJoseph Chen pr_err("Forbid to flash mmc 1(sdcard)\n"); 235535b44c0SJoseph Chen return CMD_RET_FAILURE; 236208ee25dSYifeng Zhao } else if ((!strcmp(devtype, "nvme") || !strcmp(devtype, "scsi")) && !strcmp(devnum, "0")) { 237b6d00a7aSJoseph Chen /* 238b6d00a7aSJoseph Chen * Add partnum ":0" to active 'allow_whole_dev' partition 239b6d00a7aSJoseph Chen * search mechanism on multi storage, where there maybe not 240b6d00a7aSJoseph Chen * valid partition table. 241b6d00a7aSJoseph Chen */ 242b6d00a7aSJoseph Chen devnum = "0:0"; 243535b44c0SJoseph Chen } 244535b44c0SJoseph Chen 245aa9b1b59SFrank Wang g_rkusb = &rkusb; 246aa9b1b59SFrank Wang rc = rkusb_init(devtype, devnum); 247aa9b1b59SFrank Wang if (rc < 0) 248aa9b1b59SFrank Wang return CMD_RET_FAILURE; 249aa9b1b59SFrank Wang 2501b01cf55SYifeng Zhao if (g_rkusb->ums[0].block_dev.if_type == IF_TYPE_MTD && 2511b01cf55SYifeng Zhao g_rkusb->ums[0].block_dev.devnum == BLK_MTD_NAND) { 2521b01cf55SYifeng Zhao #ifdef CONFIG_CMD_GO 2531b01cf55SYifeng Zhao pr_err("Enter bootrom rockusb...\n"); 2541b01cf55SYifeng Zhao flushc(); 2551b01cf55SYifeng Zhao run_command("rbrom", 0); 2561b01cf55SYifeng Zhao #else 2571b01cf55SYifeng Zhao pr_err("rockusb: count not support loader upgrade!\n"); 2581b01cf55SYifeng Zhao #endif 2591b01cf55SYifeng Zhao } 2601b01cf55SYifeng Zhao 261*6a8f377cSWilliam Wu re_enumerate: 262aa9b1b59SFrank Wang controller_index = (unsigned int)(simple_strtoul( 263aa9b1b59SFrank Wang usb_controller, NULL, 0)); 264b95d4446SJean-Jacques Hiblot rc = usb_gadget_initialize(controller_index); 265b95d4446SJean-Jacques Hiblot if (rc) { 26690aa625cSMasahiro Yamada pr_err("Couldn't init USB controller."); 267aa9b1b59SFrank Wang rc = CMD_RET_FAILURE; 268aa9b1b59SFrank Wang goto cleanup_rkusb; 269aa9b1b59SFrank Wang } 270aa9b1b59SFrank Wang 271aa9b1b59SFrank Wang rc = fsg_init(g_rkusb->ums, g_rkusb->ums_cnt); 272aa9b1b59SFrank Wang if (rc) { 27390aa625cSMasahiro Yamada pr_err("fsg_init failed"); 274aa9b1b59SFrank Wang rc = CMD_RET_FAILURE; 275aa9b1b59SFrank Wang goto cleanup_board; 276aa9b1b59SFrank Wang } 277aa9b1b59SFrank Wang 27836c87911Swilliam.wu if (rkusb_switch_usb3_enabled()) { 27936c87911Swilliam.wu /* Maskrom usb3 serialnumber get from upgrade tool */ 28036c87911Swilliam.wu rkusb_switch_to_usb3_enable(false); 28136c87911Swilliam.wu } else { 282fc1a5563SJason Zhu s = env_get("serial#"); 28306c1e1b3SJason Zhu if (s) { 28406c1e1b3SJason Zhu char *sn = (char *)calloc(strlen(s) + 1, sizeof(char)); 28506c1e1b3SJason Zhu char *sn_p = sn; 28606c1e1b3SJason Zhu 28706c1e1b3SJason Zhu if (!sn) 28806c1e1b3SJason Zhu goto cleanup_board; 28906c1e1b3SJason Zhu 29006c1e1b3SJason Zhu memcpy(sn, s, strlen(s)); 29106c1e1b3SJason Zhu while (*sn_p) { 29206c1e1b3SJason Zhu if (*sn_p == '\\' || *sn_p == '/') 29306c1e1b3SJason Zhu *sn_p = '_'; 29406c1e1b3SJason Zhu sn_p++; 29506c1e1b3SJason Zhu } 29606c1e1b3SJason Zhu 29706c1e1b3SJason Zhu g_dnl_set_serialnumber(sn); 29806c1e1b3SJason Zhu free(sn); 29936c87911Swilliam.wu #if defined(CONFIG_SUPPORT_USBPLUG) 30036c87911Swilliam.wu } else { 30136c87911Swilliam.wu char sn[9] = "Rockchip"; 30236c87911Swilliam.wu 30336c87911Swilliam.wu g_dnl_set_serialnumber(sn); 30436c87911Swilliam.wu #endif 30536c87911Swilliam.wu } 30606c1e1b3SJason Zhu } 307fc1a5563SJason Zhu 308aa9b1b59SFrank Wang rc = g_dnl_register("rkusb_ums_dnl"); 309aa9b1b59SFrank Wang if (rc) { 31090aa625cSMasahiro Yamada pr_err("g_dnl_register failed"); 311aa9b1b59SFrank Wang rc = CMD_RET_FAILURE; 312aa9b1b59SFrank Wang goto cleanup_board; 313aa9b1b59SFrank Wang } 314aa9b1b59SFrank Wang 315aa9b1b59SFrank Wang /* Timeout unit: seconds */ 316aa9b1b59SFrank Wang cable_ready_timeout = UMS_CABLE_READY_TIMEOUT; 317aa9b1b59SFrank Wang 318aa9b1b59SFrank Wang if (!g_dnl_board_usb_cable_connected()) { 319aa9b1b59SFrank Wang puts("Please connect USB cable.\n"); 320aa9b1b59SFrank Wang 321aa9b1b59SFrank Wang while (!g_dnl_board_usb_cable_connected()) { 322aa9b1b59SFrank Wang if (ctrlc()) { 323aa9b1b59SFrank Wang puts("\rCTRL+C - Operation aborted.\n"); 324aa9b1b59SFrank Wang rc = CMD_RET_SUCCESS; 325aa9b1b59SFrank Wang goto cleanup_register; 326aa9b1b59SFrank Wang } 327aa9b1b59SFrank Wang if (!cable_ready_timeout) { 328aa9b1b59SFrank Wang puts("\rUSB cable not detected.\nCommand exit.\n"); 329aa9b1b59SFrank Wang rc = CMD_RET_SUCCESS; 330aa9b1b59SFrank Wang goto cleanup_register; 331aa9b1b59SFrank Wang } 332aa9b1b59SFrank Wang 333aa9b1b59SFrank Wang printf("\rAuto exit in: %.2d s.", cable_ready_timeout); 334aa9b1b59SFrank Wang mdelay(1000); 335aa9b1b59SFrank Wang cable_ready_timeout--; 336aa9b1b59SFrank Wang } 337aa9b1b59SFrank Wang puts("\r\n"); 338aa9b1b59SFrank Wang } 339aa9b1b59SFrank Wang 340aa9b1b59SFrank Wang while (1) { 341aa9b1b59SFrank Wang usb_gadget_handle_interrupts(controller_index); 342aa9b1b59SFrank Wang 343aa9b1b59SFrank Wang rc = fsg_main_thread(NULL); 344aa9b1b59SFrank Wang if (rc) { 34536c87911Swilliam.wu if (rc == -ENODEV && rkusb_usb3_capable() && 34636c87911Swilliam.wu !rkusb_force_usb2_enabled()) { 34736c87911Swilliam.wu printf("wait for usb3 connect timeout\n"); 34836c87911Swilliam.wu rkusb_force_to_usb2(true); 34936c87911Swilliam.wu g_dnl_unregister(); 35036c87911Swilliam.wu usb_gadget_release(controller_index); 35136c87911Swilliam.wu goto re_enumerate; 35236c87911Swilliam.wu } 35336c87911Swilliam.wu 354aa9b1b59SFrank Wang /* Check I/O error */ 355aa9b1b59SFrank Wang if (rc == -EIO) 356aa9b1b59SFrank Wang printf("\rCheck USB cable connection\n"); 357aa9b1b59SFrank Wang 358aa9b1b59SFrank Wang /* Check CTRL+C */ 359aa9b1b59SFrank Wang if (rc == -EPIPE) 360aa9b1b59SFrank Wang printf("\rCTRL+C - Operation aborted\n"); 361aa9b1b59SFrank Wang 362aa9b1b59SFrank Wang rc = CMD_RET_SUCCESS; 363aa9b1b59SFrank Wang goto cleanup_register; 364aa9b1b59SFrank Wang } 36536c87911Swilliam.wu 36636c87911Swilliam.wu if (rkusb_switch_usb3_enabled()) { 36736c87911Swilliam.wu printf("rockusb switch to usb3\n"); 36836c87911Swilliam.wu g_dnl_unregister(); 36936c87911Swilliam.wu usb_gadget_release(controller_index); 37036c87911Swilliam.wu goto re_enumerate; 37136c87911Swilliam.wu } 372aa9b1b59SFrank Wang } 373aa9b1b59SFrank Wang 374aa9b1b59SFrank Wang cleanup_register: 375aa9b1b59SFrank Wang g_dnl_unregister(); 376aa9b1b59SFrank Wang cleanup_board: 377b95d4446SJean-Jacques Hiblot usb_gadget_release(controller_index); 378aa9b1b59SFrank Wang cleanup_rkusb: 379aa9b1b59SFrank Wang rkusb_fini(); 380aa9b1b59SFrank Wang 381aa9b1b59SFrank Wang return rc; 382aa9b1b59SFrank Wang } 383aa9b1b59SFrank Wang 384f6aff21fSJoseph Chen U_BOOT_CMD_ALWAYS(rockusb, 4, 1, do_rkusb, 385aa9b1b59SFrank Wang "Use the rockusb Protocol", 386aa9b1b59SFrank Wang "<USB_controller> <devtype> <dev[:part]> e.g. rockusb 0 mmc 0\n" 387aa9b1b59SFrank Wang ); 388