xref: /rk3399_rockchip-uboot/cmd/rockusb.c (revision aa9b1b59c9e943799d4e2c17a3aa622019ebc48f)
1*aa9b1b59SFrank Wang /*
2*aa9b1b59SFrank Wang  * Copyright 2017 Rockchip Electronics Co., Ltd
3*aa9b1b59SFrank Wang  * Frank Wang <frank.wang@rock-chips.com>
4*aa9b1b59SFrank Wang  *
5*aa9b1b59SFrank Wang  * SPDX-License-Identifier:	GPL-2.0+
6*aa9b1b59SFrank Wang  */
7*aa9b1b59SFrank Wang 
8*aa9b1b59SFrank Wang #include <errno.h>
9*aa9b1b59SFrank Wang #include <common.h>
10*aa9b1b59SFrank Wang #include <command.h>
11*aa9b1b59SFrank Wang #include <console.h>
12*aa9b1b59SFrank Wang #include <g_dnl.h>
13*aa9b1b59SFrank Wang #include <part.h>
14*aa9b1b59SFrank Wang #include <usb.h>
15*aa9b1b59SFrank Wang #include <usb_mass_storage.h>
16*aa9b1b59SFrank Wang #include <rockusb.h>
17*aa9b1b59SFrank Wang 
18*aa9b1b59SFrank Wang static struct rockusb rkusb;
19*aa9b1b59SFrank Wang static struct rockusb *g_rkusb;
20*aa9b1b59SFrank Wang 
21*aa9b1b59SFrank Wang static int rkusb_read_sector(struct ums *ums_dev,
22*aa9b1b59SFrank Wang 			     ulong start, lbaint_t blkcnt, void *buf)
23*aa9b1b59SFrank Wang {
24*aa9b1b59SFrank Wang 	struct blk_desc *block_dev = &ums_dev->block_dev;
25*aa9b1b59SFrank Wang 	lbaint_t blkstart = start + ums_dev->start_sector;
26*aa9b1b59SFrank Wang 
27*aa9b1b59SFrank Wang 	return blk_dread(block_dev, blkstart, blkcnt, buf);
28*aa9b1b59SFrank Wang }
29*aa9b1b59SFrank Wang 
30*aa9b1b59SFrank Wang static int rkusb_write_sector(struct ums *ums_dev,
31*aa9b1b59SFrank Wang 			      ulong start, lbaint_t blkcnt, const void *buf)
32*aa9b1b59SFrank Wang {
33*aa9b1b59SFrank Wang 	struct blk_desc *block_dev = &ums_dev->block_dev;
34*aa9b1b59SFrank Wang 	lbaint_t blkstart = start + ums_dev->start_sector;
35*aa9b1b59SFrank Wang 
36*aa9b1b59SFrank Wang 	return blk_dwrite(block_dev, blkstart, blkcnt, buf);
37*aa9b1b59SFrank Wang }
38*aa9b1b59SFrank Wang 
39*aa9b1b59SFrank Wang static int rkusb_erase_sector(struct ums *ums_dev,
40*aa9b1b59SFrank Wang 			      ulong start, lbaint_t blkcnt)
41*aa9b1b59SFrank Wang {
42*aa9b1b59SFrank Wang 	struct blk_desc *block_dev = &ums_dev->block_dev;
43*aa9b1b59SFrank Wang 	lbaint_t blkstart = start + ums_dev->start_sector;
44*aa9b1b59SFrank Wang 
45*aa9b1b59SFrank Wang 	return blk_derase(block_dev, blkstart, blkcnt);
46*aa9b1b59SFrank Wang }
47*aa9b1b59SFrank Wang 
48*aa9b1b59SFrank Wang static void rkusb_fini(void)
49*aa9b1b59SFrank Wang {
50*aa9b1b59SFrank Wang 	int i;
51*aa9b1b59SFrank Wang 
52*aa9b1b59SFrank Wang 	for (i = 0; i < g_rkusb->ums_cnt; i++)
53*aa9b1b59SFrank Wang 		free((void *)g_rkusb->ums[i].name);
54*aa9b1b59SFrank Wang 	free(g_rkusb->ums);
55*aa9b1b59SFrank Wang 	g_rkusb->ums = NULL;
56*aa9b1b59SFrank Wang 	g_rkusb = NULL;
57*aa9b1b59SFrank Wang 	g_rkusb->ums_cnt = 0;
58*aa9b1b59SFrank Wang }
59*aa9b1b59SFrank Wang 
60*aa9b1b59SFrank Wang #define RKUSB_NAME_LEN 16
61*aa9b1b59SFrank Wang 
62*aa9b1b59SFrank Wang static int rkusb_init(const char *devtype, const char *devnums_part_str)
63*aa9b1b59SFrank Wang {
64*aa9b1b59SFrank Wang 	char *s, *t, *devnum_part_str, *name;
65*aa9b1b59SFrank Wang 	struct blk_desc *block_dev;
66*aa9b1b59SFrank Wang 	disk_partition_t info;
67*aa9b1b59SFrank Wang 	int partnum, cnt;
68*aa9b1b59SFrank Wang 	int ret = -1;
69*aa9b1b59SFrank Wang 	struct ums *ums_new;
70*aa9b1b59SFrank Wang 
71*aa9b1b59SFrank Wang 	s = strdup(devnums_part_str);
72*aa9b1b59SFrank Wang 	if (!s)
73*aa9b1b59SFrank Wang 		return -1;
74*aa9b1b59SFrank Wang 
75*aa9b1b59SFrank Wang 	t = s;
76*aa9b1b59SFrank Wang 	g_rkusb->ums_cnt = 0;
77*aa9b1b59SFrank Wang 
78*aa9b1b59SFrank Wang 	for (;;) {
79*aa9b1b59SFrank Wang 		devnum_part_str = strsep(&t, ",");
80*aa9b1b59SFrank Wang 		if (!devnum_part_str)
81*aa9b1b59SFrank Wang 			break;
82*aa9b1b59SFrank Wang 
83*aa9b1b59SFrank Wang 		partnum = blk_get_device_part_str(devtype, devnum_part_str,
84*aa9b1b59SFrank Wang 					&block_dev, &info, 1);
85*aa9b1b59SFrank Wang 		if (partnum < 0)
86*aa9b1b59SFrank Wang 			goto cleanup;
87*aa9b1b59SFrank Wang 
88*aa9b1b59SFrank Wang 		/* f_mass_storage.c assumes SECTOR_SIZE sectors */
89*aa9b1b59SFrank Wang 		if (block_dev->blksz != SECTOR_SIZE)
90*aa9b1b59SFrank Wang 			goto cleanup;
91*aa9b1b59SFrank Wang 
92*aa9b1b59SFrank Wang 		ums_new = realloc(g_rkusb->ums, (g_rkusb->ums_cnt + 1) *
93*aa9b1b59SFrank Wang 				  sizeof(*g_rkusb->ums));
94*aa9b1b59SFrank Wang 		if (!ums_new)
95*aa9b1b59SFrank Wang 			goto cleanup;
96*aa9b1b59SFrank Wang 		g_rkusb->ums = ums_new;
97*aa9b1b59SFrank Wang 		cnt = g_rkusb->ums_cnt;
98*aa9b1b59SFrank Wang 
99*aa9b1b59SFrank Wang 		/* if partnum = 0, expose all partitions */
100*aa9b1b59SFrank Wang 		if (partnum == 0) {
101*aa9b1b59SFrank Wang 			g_rkusb->ums[cnt].start_sector = 0;
102*aa9b1b59SFrank Wang 			g_rkusb->ums[cnt].num_sectors = block_dev->lba;
103*aa9b1b59SFrank Wang 		} else {
104*aa9b1b59SFrank Wang 			g_rkusb->ums[cnt].start_sector = info.start;
105*aa9b1b59SFrank Wang 			g_rkusb->ums[cnt].num_sectors = info.size;
106*aa9b1b59SFrank Wang 		}
107*aa9b1b59SFrank Wang 
108*aa9b1b59SFrank Wang 		g_rkusb->ums[cnt].read_sector = rkusb_read_sector;
109*aa9b1b59SFrank Wang 		g_rkusb->ums[cnt].write_sector = rkusb_write_sector;
110*aa9b1b59SFrank Wang 		g_rkusb->ums[cnt].erase_sector = rkusb_erase_sector;
111*aa9b1b59SFrank Wang 
112*aa9b1b59SFrank Wang 		name = malloc(RKUSB_NAME_LEN);
113*aa9b1b59SFrank Wang 		if (!name)
114*aa9b1b59SFrank Wang 			goto cleanup;
115*aa9b1b59SFrank Wang 		snprintf(name, RKUSB_NAME_LEN, "rkusb disk %d", cnt);
116*aa9b1b59SFrank Wang 		g_rkusb->ums[cnt].name = name;
117*aa9b1b59SFrank Wang 		g_rkusb->ums[cnt].block_dev = *block_dev;
118*aa9b1b59SFrank Wang 
119*aa9b1b59SFrank Wang 		printf("RKUSB: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n",
120*aa9b1b59SFrank Wang 		       g_rkusb->ums_cnt,
121*aa9b1b59SFrank Wang 		       g_rkusb->ums[cnt].block_dev.devnum,
122*aa9b1b59SFrank Wang 		       g_rkusb->ums[cnt].block_dev.hwpart,
123*aa9b1b59SFrank Wang 		       g_rkusb->ums[cnt].start_sector,
124*aa9b1b59SFrank Wang 		       g_rkusb->ums[cnt].num_sectors);
125*aa9b1b59SFrank Wang 
126*aa9b1b59SFrank Wang 		g_rkusb->ums_cnt++;
127*aa9b1b59SFrank Wang 	}
128*aa9b1b59SFrank Wang 
129*aa9b1b59SFrank Wang 	if (g_rkusb->ums_cnt)
130*aa9b1b59SFrank Wang 		ret = 0;
131*aa9b1b59SFrank Wang 
132*aa9b1b59SFrank Wang cleanup:
133*aa9b1b59SFrank Wang 	free(s);
134*aa9b1b59SFrank Wang 	if (ret < 0)
135*aa9b1b59SFrank Wang 		rkusb_fini();
136*aa9b1b59SFrank Wang 
137*aa9b1b59SFrank Wang 	return ret;
138*aa9b1b59SFrank Wang }
139*aa9b1b59SFrank Wang 
140*aa9b1b59SFrank Wang static int do_rkusb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
141*aa9b1b59SFrank Wang {
142*aa9b1b59SFrank Wang 	const char *usb_controller;
143*aa9b1b59SFrank Wang 	const char *devtype;
144*aa9b1b59SFrank Wang 	const char *devnum;
145*aa9b1b59SFrank Wang 	unsigned int controller_index;
146*aa9b1b59SFrank Wang 	int rc;
147*aa9b1b59SFrank Wang 	int cable_ready_timeout __maybe_unused;
148*aa9b1b59SFrank Wang 
149*aa9b1b59SFrank Wang 	if (argc != 4)
150*aa9b1b59SFrank Wang 		return CMD_RET_USAGE;
151*aa9b1b59SFrank Wang 
152*aa9b1b59SFrank Wang 	usb_controller = argv[1];
153*aa9b1b59SFrank Wang 	devtype = argv[2];
154*aa9b1b59SFrank Wang 	devnum	= argv[3];
155*aa9b1b59SFrank Wang 
156*aa9b1b59SFrank Wang 	g_rkusb = &rkusb;
157*aa9b1b59SFrank Wang 	rc = rkusb_init(devtype, devnum);
158*aa9b1b59SFrank Wang 	if (rc < 0)
159*aa9b1b59SFrank Wang 		return CMD_RET_FAILURE;
160*aa9b1b59SFrank Wang 
161*aa9b1b59SFrank Wang 	controller_index = (unsigned int)(simple_strtoul(
162*aa9b1b59SFrank Wang 				usb_controller,	NULL, 0));
163*aa9b1b59SFrank Wang 	if (board_usb_init(controller_index, USB_INIT_DEVICE)) {
164*aa9b1b59SFrank Wang 		error("Couldn't init USB controller.");
165*aa9b1b59SFrank Wang 		rc = CMD_RET_FAILURE;
166*aa9b1b59SFrank Wang 		goto cleanup_rkusb;
167*aa9b1b59SFrank Wang 	}
168*aa9b1b59SFrank Wang 
169*aa9b1b59SFrank Wang 	rc = fsg_init(g_rkusb->ums, g_rkusb->ums_cnt);
170*aa9b1b59SFrank Wang 	if (rc) {
171*aa9b1b59SFrank Wang 		error("fsg_init failed");
172*aa9b1b59SFrank Wang 		rc = CMD_RET_FAILURE;
173*aa9b1b59SFrank Wang 		goto cleanup_board;
174*aa9b1b59SFrank Wang 	}
175*aa9b1b59SFrank Wang 
176*aa9b1b59SFrank Wang 	rc = g_dnl_register("rkusb_ums_dnl");
177*aa9b1b59SFrank Wang 	if (rc) {
178*aa9b1b59SFrank Wang 		error("g_dnl_register failed");
179*aa9b1b59SFrank Wang 		rc = CMD_RET_FAILURE;
180*aa9b1b59SFrank Wang 		goto cleanup_board;
181*aa9b1b59SFrank Wang 	}
182*aa9b1b59SFrank Wang 
183*aa9b1b59SFrank Wang 	/* Timeout unit: seconds */
184*aa9b1b59SFrank Wang 	cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
185*aa9b1b59SFrank Wang 
186*aa9b1b59SFrank Wang 	if (!g_dnl_board_usb_cable_connected()) {
187*aa9b1b59SFrank Wang 		puts("Please connect USB cable.\n");
188*aa9b1b59SFrank Wang 
189*aa9b1b59SFrank Wang 		while (!g_dnl_board_usb_cable_connected()) {
190*aa9b1b59SFrank Wang 			if (ctrlc()) {
191*aa9b1b59SFrank Wang 				puts("\rCTRL+C - Operation aborted.\n");
192*aa9b1b59SFrank Wang 				rc = CMD_RET_SUCCESS;
193*aa9b1b59SFrank Wang 				goto cleanup_register;
194*aa9b1b59SFrank Wang 			}
195*aa9b1b59SFrank Wang 			if (!cable_ready_timeout) {
196*aa9b1b59SFrank Wang 				puts("\rUSB cable not detected.\nCommand exit.\n");
197*aa9b1b59SFrank Wang 				rc = CMD_RET_SUCCESS;
198*aa9b1b59SFrank Wang 				goto cleanup_register;
199*aa9b1b59SFrank Wang 			}
200*aa9b1b59SFrank Wang 
201*aa9b1b59SFrank Wang 			printf("\rAuto exit in: %.2d s.", cable_ready_timeout);
202*aa9b1b59SFrank Wang 			mdelay(1000);
203*aa9b1b59SFrank Wang 			cable_ready_timeout--;
204*aa9b1b59SFrank Wang 		}
205*aa9b1b59SFrank Wang 		puts("\r\n");
206*aa9b1b59SFrank Wang 	}
207*aa9b1b59SFrank Wang 
208*aa9b1b59SFrank Wang 	while (1) {
209*aa9b1b59SFrank Wang 		usb_gadget_handle_interrupts(controller_index);
210*aa9b1b59SFrank Wang 
211*aa9b1b59SFrank Wang 		rc = fsg_main_thread(NULL);
212*aa9b1b59SFrank Wang 		if (rc) {
213*aa9b1b59SFrank Wang 			/* Check I/O error */
214*aa9b1b59SFrank Wang 			if (rc == -EIO)
215*aa9b1b59SFrank Wang 				printf("\rCheck USB cable connection\n");
216*aa9b1b59SFrank Wang 
217*aa9b1b59SFrank Wang 			/* Check CTRL+C */
218*aa9b1b59SFrank Wang 			if (rc == -EPIPE)
219*aa9b1b59SFrank Wang 				printf("\rCTRL+C - Operation aborted\n");
220*aa9b1b59SFrank Wang 
221*aa9b1b59SFrank Wang 			rc = CMD_RET_SUCCESS;
222*aa9b1b59SFrank Wang 			goto cleanup_register;
223*aa9b1b59SFrank Wang 		}
224*aa9b1b59SFrank Wang 	}
225*aa9b1b59SFrank Wang 
226*aa9b1b59SFrank Wang cleanup_register:
227*aa9b1b59SFrank Wang 	g_dnl_unregister();
228*aa9b1b59SFrank Wang cleanup_board:
229*aa9b1b59SFrank Wang 	board_usb_cleanup(controller_index, USB_INIT_DEVICE);
230*aa9b1b59SFrank Wang cleanup_rkusb:
231*aa9b1b59SFrank Wang 	rkusb_fini();
232*aa9b1b59SFrank Wang 
233*aa9b1b59SFrank Wang 	return rc;
234*aa9b1b59SFrank Wang }
235*aa9b1b59SFrank Wang 
236*aa9b1b59SFrank Wang U_BOOT_CMD(rockusb, 4, 1, do_rkusb,
237*aa9b1b59SFrank Wang 	   "Use the rockusb Protocol",
238*aa9b1b59SFrank Wang 	   "<USB_controller> <devtype> <dev[:part]>  e.g. rockusb 0 mmc 0\n"
239*aa9b1b59SFrank Wang );
240