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
rkusb_read_sector(struct ums * ums_dev,ulong start,lbaint_t blkcnt,void * buf)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
rkusb_write_sector(struct ums * ums_dev,ulong start,lbaint_t blkcnt,const void * buf)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
rkusb_erase_sector(struct ums * ums_dev,ulong start,lbaint_t blkcnt)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 }
85712e1ef7SYifeng Zhao }
86712e1ef7SYifeng Zhao #endif
87712e1ef7SYifeng Zhao
88aa9b1b59SFrank Wang return blk_derase(block_dev, blkstart, blkcnt);
89aa9b1b59SFrank Wang }
90aa9b1b59SFrank Wang
rkusb_fini(void)91aa9b1b59SFrank Wang static void rkusb_fini(void)
92aa9b1b59SFrank Wang {
93aa9b1b59SFrank Wang int i;
94aa9b1b59SFrank Wang
95aa9b1b59SFrank Wang for (i = 0; i < g_rkusb->ums_cnt; i++)
96aa9b1b59SFrank Wang free((void *)g_rkusb->ums[i].name);
97aa9b1b59SFrank Wang free(g_rkusb->ums);
98aa9b1b59SFrank Wang g_rkusb->ums = NULL;
99aa9b1b59SFrank Wang g_rkusb->ums_cnt = 0;
100f5b174d4SFrank Wang g_rkusb = NULL;
101aa9b1b59SFrank Wang }
102aa9b1b59SFrank Wang
103aa9b1b59SFrank Wang #define RKUSB_NAME_LEN 16
104aa9b1b59SFrank Wang
rkusb_init(const char * devtype,const char * devnums_part_str)105aa9b1b59SFrank Wang static int rkusb_init(const char *devtype, const char *devnums_part_str)
106aa9b1b59SFrank Wang {
107aa9b1b59SFrank Wang char *s, *t, *devnum_part_str, *name;
108aa9b1b59SFrank Wang struct blk_desc *block_dev;
109aa9b1b59SFrank Wang disk_partition_t info;
110aa9b1b59SFrank Wang int partnum, cnt;
111aa9b1b59SFrank Wang int ret = -1;
112aa9b1b59SFrank Wang struct ums *ums_new;
113aa9b1b59SFrank Wang
114aa9b1b59SFrank Wang s = strdup(devnums_part_str);
115aa9b1b59SFrank Wang if (!s)
116aa9b1b59SFrank Wang return -1;
117aa9b1b59SFrank Wang
118aa9b1b59SFrank Wang t = s;
119aa9b1b59SFrank Wang g_rkusb->ums_cnt = 0;
120aa9b1b59SFrank Wang
121aa9b1b59SFrank Wang for (;;) {
122aa9b1b59SFrank Wang devnum_part_str = strsep(&t, ",");
123aa9b1b59SFrank Wang if (!devnum_part_str)
124aa9b1b59SFrank Wang break;
125bc670a72SYifeng Zhao #if defined(CONFIG_SCSI) && defined(CONFIG_CMD_SCSI) && (defined(CONFIG_UFS))
126bc670a72SYifeng Zhao if (!strcmp(devtype, "scsi")) {
127bc670a72SYifeng Zhao block_dev= blk_get_devnum_by_typename(devtype, 0);
128bc670a72SYifeng Zhao if (block_dev == NULL)
129bc670a72SYifeng Zhao return -ENXIO;
130bc670a72SYifeng Zhao } else
131bc670a72SYifeng Zhao #endif
132bc670a72SYifeng Zhao {
133aa9b1b59SFrank Wang partnum = blk_get_device_part_str(devtype, devnum_part_str,
134aa9b1b59SFrank Wang &block_dev, &info, 1);
135aa9b1b59SFrank Wang if (partnum < 0)
136aa9b1b59SFrank Wang goto cleanup;
137bc670a72SYifeng Zhao }
138aa9b1b59SFrank Wang
139aa9b1b59SFrank Wang /* f_mass_storage.c assumes SECTOR_SIZE sectors */
140aa9b1b59SFrank Wang if (block_dev->blksz != SECTOR_SIZE)
141aa9b1b59SFrank Wang goto cleanup;
142aa9b1b59SFrank Wang
143aa9b1b59SFrank Wang ums_new = realloc(g_rkusb->ums, (g_rkusb->ums_cnt + 1) *
144aa9b1b59SFrank Wang sizeof(*g_rkusb->ums));
145aa9b1b59SFrank Wang if (!ums_new)
146aa9b1b59SFrank Wang goto cleanup;
147aa9b1b59SFrank Wang g_rkusb->ums = ums_new;
148aa9b1b59SFrank Wang cnt = g_rkusb->ums_cnt;
149aa9b1b59SFrank Wang
150dd31eefeSFrank Wang /* Expose all partitions for rockusb command */
151aa9b1b59SFrank Wang g_rkusb->ums[cnt].start_sector = 0;
152aa9b1b59SFrank Wang g_rkusb->ums[cnt].num_sectors = block_dev->lba;
153aa9b1b59SFrank Wang
154aa9b1b59SFrank Wang g_rkusb->ums[cnt].read_sector = rkusb_read_sector;
155aa9b1b59SFrank Wang g_rkusb->ums[cnt].write_sector = rkusb_write_sector;
156aa9b1b59SFrank Wang g_rkusb->ums[cnt].erase_sector = rkusb_erase_sector;
157aa9b1b59SFrank Wang
158aa9b1b59SFrank Wang name = malloc(RKUSB_NAME_LEN);
159aa9b1b59SFrank Wang if (!name)
160aa9b1b59SFrank Wang goto cleanup;
161aa9b1b59SFrank Wang snprintf(name, RKUSB_NAME_LEN, "rkusb disk %d", cnt);
162aa9b1b59SFrank Wang g_rkusb->ums[cnt].name = name;
163aa9b1b59SFrank Wang g_rkusb->ums[cnt].block_dev = *block_dev;
164aa9b1b59SFrank Wang
165aa9b1b59SFrank Wang printf("RKUSB: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n",
166aa9b1b59SFrank Wang g_rkusb->ums_cnt,
167aa9b1b59SFrank Wang g_rkusb->ums[cnt].block_dev.devnum,
168aa9b1b59SFrank Wang g_rkusb->ums[cnt].block_dev.hwpart,
169aa9b1b59SFrank Wang g_rkusb->ums[cnt].start_sector,
170aa9b1b59SFrank Wang g_rkusb->ums[cnt].num_sectors);
171aa9b1b59SFrank Wang
172aa9b1b59SFrank Wang g_rkusb->ums_cnt++;
173aa9b1b59SFrank Wang }
174aa9b1b59SFrank Wang
175aa9b1b59SFrank Wang if (g_rkusb->ums_cnt)
176aa9b1b59SFrank Wang ret = 0;
177aa9b1b59SFrank Wang
178aa9b1b59SFrank Wang cleanup:
179aa9b1b59SFrank Wang free(s);
180aa9b1b59SFrank Wang if (ret < 0)
181aa9b1b59SFrank Wang rkusb_fini();
182aa9b1b59SFrank Wang
183aa9b1b59SFrank Wang return ret;
184aa9b1b59SFrank Wang }
185aa9b1b59SFrank Wang
rkusb_force_to_usb2(bool enable)18636c87911Swilliam.wu void rkusb_force_to_usb2(bool enable)
18736c87911Swilliam.wu {
18879553f2dSWilliam Wu if (g_rkusb)
18936c87911Swilliam.wu g_rkusb->force_usb2 = enable;
19036c87911Swilliam.wu }
19136c87911Swilliam.wu
rkusb_force_usb2_enabled(void)19236c87911Swilliam.wu bool rkusb_force_usb2_enabled(void)
19336c87911Swilliam.wu {
19479553f2dSWilliam Wu if (!g_rkusb)
19579553f2dSWilliam Wu return true;
19679553f2dSWilliam Wu
19736c87911Swilliam.wu return g_rkusb->force_usb2;
19836c87911Swilliam.wu }
19936c87911Swilliam.wu
rkusb_switch_to_usb3_enable(bool enable)20036c87911Swilliam.wu void rkusb_switch_to_usb3_enable(bool enable)
20136c87911Swilliam.wu {
20279553f2dSWilliam Wu if (g_rkusb)
20336c87911Swilliam.wu g_rkusb->switch_usb3 = enable;
20436c87911Swilliam.wu }
20536c87911Swilliam.wu
rkusb_switch_usb3_enabled(void)20636c87911Swilliam.wu bool rkusb_switch_usb3_enabled(void)
20736c87911Swilliam.wu {
20879553f2dSWilliam Wu if (!g_rkusb)
20979553f2dSWilliam Wu return false;
21079553f2dSWilliam Wu
21136c87911Swilliam.wu return g_rkusb->switch_usb3;
21236c87911Swilliam.wu }
21336c87911Swilliam.wu
do_rkusb(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])214aa9b1b59SFrank Wang static int do_rkusb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
215aa9b1b59SFrank Wang {
216aa9b1b59SFrank Wang const char *usb_controller;
217aa9b1b59SFrank Wang const char *devtype;
218aa9b1b59SFrank Wang const char *devnum;
219aa9b1b59SFrank Wang unsigned int controller_index;
220aa9b1b59SFrank Wang int rc;
221aa9b1b59SFrank Wang int cable_ready_timeout __maybe_unused;
222fc1a5563SJason Zhu const char *s;
223aa9b1b59SFrank Wang
224aa9b1b59SFrank Wang if (argc != 4)
225aa9b1b59SFrank Wang return CMD_RET_USAGE;
226aa9b1b59SFrank Wang
227aa9b1b59SFrank Wang usb_controller = argv[1];
228aa9b1b59SFrank Wang devtype = argv[2];
229aa9b1b59SFrank Wang devnum = argv[3];
230aa9b1b59SFrank Wang
231535b44c0SJoseph Chen if (!strcmp(devtype, "mmc") && !strcmp(devnum, "1")) {
232535b44c0SJoseph Chen pr_err("Forbid to flash mmc 1(sdcard)\n");
233535b44c0SJoseph Chen return CMD_RET_FAILURE;
234208ee25dSYifeng Zhao } else if ((!strcmp(devtype, "nvme") || !strcmp(devtype, "scsi")) && !strcmp(devnum, "0")) {
235b6d00a7aSJoseph Chen /*
236b6d00a7aSJoseph Chen * Add partnum ":0" to active 'allow_whole_dev' partition
237b6d00a7aSJoseph Chen * search mechanism on multi storage, where there maybe not
238b6d00a7aSJoseph Chen * valid partition table.
239b6d00a7aSJoseph Chen */
240b6d00a7aSJoseph Chen devnum = "0:0";
241535b44c0SJoseph Chen }
242535b44c0SJoseph Chen
243aa9b1b59SFrank Wang g_rkusb = &rkusb;
244aa9b1b59SFrank Wang rc = rkusb_init(devtype, devnum);
245aa9b1b59SFrank Wang if (rc < 0)
246aa9b1b59SFrank Wang return CMD_RET_FAILURE;
247aa9b1b59SFrank Wang
2481b01cf55SYifeng Zhao if (g_rkusb->ums[0].block_dev.if_type == IF_TYPE_MTD &&
2491b01cf55SYifeng Zhao g_rkusb->ums[0].block_dev.devnum == BLK_MTD_NAND) {
2501b01cf55SYifeng Zhao #ifdef CONFIG_CMD_GO
2511b01cf55SYifeng Zhao pr_err("Enter bootrom rockusb...\n");
2521b01cf55SYifeng Zhao flushc();
2531b01cf55SYifeng Zhao run_command("rbrom", 0);
2541b01cf55SYifeng Zhao #else
2551b01cf55SYifeng Zhao pr_err("rockusb: count not support loader upgrade!\n");
2561b01cf55SYifeng Zhao #endif
2571b01cf55SYifeng Zhao }
2581b01cf55SYifeng Zhao
259*6a8f377cSWilliam Wu re_enumerate:
260aa9b1b59SFrank Wang controller_index = (unsigned int)(simple_strtoul(
261aa9b1b59SFrank Wang usb_controller, NULL, 0));
262b95d4446SJean-Jacques Hiblot rc = usb_gadget_initialize(controller_index);
263b95d4446SJean-Jacques Hiblot if (rc) {
26490aa625cSMasahiro Yamada pr_err("Couldn't init USB controller.");
265aa9b1b59SFrank Wang rc = CMD_RET_FAILURE;
266aa9b1b59SFrank Wang goto cleanup_rkusb;
267aa9b1b59SFrank Wang }
268aa9b1b59SFrank Wang
269aa9b1b59SFrank Wang rc = fsg_init(g_rkusb->ums, g_rkusb->ums_cnt);
270aa9b1b59SFrank Wang if (rc) {
27190aa625cSMasahiro Yamada pr_err("fsg_init failed");
272aa9b1b59SFrank Wang rc = CMD_RET_FAILURE;
273aa9b1b59SFrank Wang goto cleanup_board;
274aa9b1b59SFrank Wang }
275aa9b1b59SFrank Wang
27636c87911Swilliam.wu if (rkusb_switch_usb3_enabled()) {
27736c87911Swilliam.wu /* Maskrom usb3 serialnumber get from upgrade tool */
27836c87911Swilliam.wu rkusb_switch_to_usb3_enable(false);
27936c87911Swilliam.wu } else {
280fc1a5563SJason Zhu s = env_get("serial#");
28106c1e1b3SJason Zhu if (s) {
28206c1e1b3SJason Zhu char *sn = (char *)calloc(strlen(s) + 1, sizeof(char));
28306c1e1b3SJason Zhu char *sn_p = sn;
28406c1e1b3SJason Zhu
28506c1e1b3SJason Zhu if (!sn)
28606c1e1b3SJason Zhu goto cleanup_board;
28706c1e1b3SJason Zhu
28806c1e1b3SJason Zhu memcpy(sn, s, strlen(s));
28906c1e1b3SJason Zhu while (*sn_p) {
29006c1e1b3SJason Zhu if (*sn_p == '\\' || *sn_p == '/')
29106c1e1b3SJason Zhu *sn_p = '_';
29206c1e1b3SJason Zhu sn_p++;
29306c1e1b3SJason Zhu }
29406c1e1b3SJason Zhu
29506c1e1b3SJason Zhu g_dnl_set_serialnumber(sn);
29606c1e1b3SJason Zhu free(sn);
29736c87911Swilliam.wu #if defined(CONFIG_SUPPORT_USBPLUG)
29836c87911Swilliam.wu } else {
29936c87911Swilliam.wu char sn[9] = "Rockchip";
30036c87911Swilliam.wu
30136c87911Swilliam.wu g_dnl_set_serialnumber(sn);
30236c87911Swilliam.wu #endif
30336c87911Swilliam.wu }
30406c1e1b3SJason Zhu }
305fc1a5563SJason Zhu
306aa9b1b59SFrank Wang rc = g_dnl_register("rkusb_ums_dnl");
307aa9b1b59SFrank Wang if (rc) {
30890aa625cSMasahiro Yamada pr_err("g_dnl_register failed");
309aa9b1b59SFrank Wang rc = CMD_RET_FAILURE;
310aa9b1b59SFrank Wang goto cleanup_board;
311aa9b1b59SFrank Wang }
312aa9b1b59SFrank Wang
313aa9b1b59SFrank Wang /* Timeout unit: seconds */
314aa9b1b59SFrank Wang cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
315aa9b1b59SFrank Wang
316aa9b1b59SFrank Wang if (!g_dnl_board_usb_cable_connected()) {
317aa9b1b59SFrank Wang puts("Please connect USB cable.\n");
318aa9b1b59SFrank Wang
319aa9b1b59SFrank Wang while (!g_dnl_board_usb_cable_connected()) {
320aa9b1b59SFrank Wang if (ctrlc()) {
321aa9b1b59SFrank Wang puts("\rCTRL+C - Operation aborted.\n");
322aa9b1b59SFrank Wang rc = CMD_RET_SUCCESS;
323aa9b1b59SFrank Wang goto cleanup_register;
324aa9b1b59SFrank Wang }
325aa9b1b59SFrank Wang if (!cable_ready_timeout) {
326aa9b1b59SFrank Wang puts("\rUSB cable not detected.\nCommand exit.\n");
327aa9b1b59SFrank Wang rc = CMD_RET_SUCCESS;
328aa9b1b59SFrank Wang goto cleanup_register;
329aa9b1b59SFrank Wang }
330aa9b1b59SFrank Wang
331aa9b1b59SFrank Wang printf("\rAuto exit in: %.2d s.", cable_ready_timeout);
332aa9b1b59SFrank Wang mdelay(1000);
333aa9b1b59SFrank Wang cable_ready_timeout--;
334aa9b1b59SFrank Wang }
335aa9b1b59SFrank Wang puts("\r\n");
336aa9b1b59SFrank Wang }
337aa9b1b59SFrank Wang
338aa9b1b59SFrank Wang while (1) {
339aa9b1b59SFrank Wang usb_gadget_handle_interrupts(controller_index);
340aa9b1b59SFrank Wang
341aa9b1b59SFrank Wang rc = fsg_main_thread(NULL);
342aa9b1b59SFrank Wang if (rc) {
34336c87911Swilliam.wu if (rc == -ENODEV && rkusb_usb3_capable() &&
34436c87911Swilliam.wu !rkusb_force_usb2_enabled()) {
34536c87911Swilliam.wu printf("wait for usb3 connect timeout\n");
34636c87911Swilliam.wu rkusb_force_to_usb2(true);
34736c87911Swilliam.wu g_dnl_unregister();
34836c87911Swilliam.wu usb_gadget_release(controller_index);
34936c87911Swilliam.wu goto re_enumerate;
35036c87911Swilliam.wu }
35136c87911Swilliam.wu
352aa9b1b59SFrank Wang /* Check I/O error */
353aa9b1b59SFrank Wang if (rc == -EIO)
354aa9b1b59SFrank Wang printf("\rCheck USB cable connection\n");
355aa9b1b59SFrank Wang
356aa9b1b59SFrank Wang /* Check CTRL+C */
357aa9b1b59SFrank Wang if (rc == -EPIPE)
358aa9b1b59SFrank Wang printf("\rCTRL+C - Operation aborted\n");
359aa9b1b59SFrank Wang
360aa9b1b59SFrank Wang rc = CMD_RET_SUCCESS;
361aa9b1b59SFrank Wang goto cleanup_register;
362aa9b1b59SFrank Wang }
36336c87911Swilliam.wu
36436c87911Swilliam.wu if (rkusb_switch_usb3_enabled()) {
36536c87911Swilliam.wu printf("rockusb switch to usb3\n");
36636c87911Swilliam.wu g_dnl_unregister();
36736c87911Swilliam.wu usb_gadget_release(controller_index);
36836c87911Swilliam.wu goto re_enumerate;
36936c87911Swilliam.wu }
370aa9b1b59SFrank Wang }
371aa9b1b59SFrank Wang
372aa9b1b59SFrank Wang cleanup_register:
373aa9b1b59SFrank Wang g_dnl_unregister();
374aa9b1b59SFrank Wang cleanup_board:
375b95d4446SJean-Jacques Hiblot usb_gadget_release(controller_index);
376aa9b1b59SFrank Wang cleanup_rkusb:
377aa9b1b59SFrank Wang rkusb_fini();
378aa9b1b59SFrank Wang
379aa9b1b59SFrank Wang return rc;
380aa9b1b59SFrank Wang }
381aa9b1b59SFrank Wang
382f6aff21fSJoseph Chen U_BOOT_CMD_ALWAYS(rockusb, 4, 1, do_rkusb,
383aa9b1b59SFrank Wang "Use the rockusb Protocol",
384aa9b1b59SFrank Wang "<USB_controller> <devtype> <dev[:part]> e.g. rockusb 0 mmc 0\n"
385aa9b1b59SFrank Wang );
386