xref: /rk3399_ARM-atf/plat/st/common/stm32cubeprogrammer_usb.c (revision afad5214a79259f56bc2003b00859abfe8a18d4d)
1*afad5214SPatrick Delaunay /*
2*afad5214SPatrick Delaunay  * Copyright (c) 2021, STMicroelectronics - All Rights Reserved
3*afad5214SPatrick Delaunay  *
4*afad5214SPatrick Delaunay  * SPDX-License-Identifier: BSD-3-Clause
5*afad5214SPatrick Delaunay  */
6*afad5214SPatrick Delaunay 
7*afad5214SPatrick Delaunay #include <assert.h>
8*afad5214SPatrick Delaunay #include <errno.h>
9*afad5214SPatrick Delaunay 
10*afad5214SPatrick Delaunay #include <tools_share/firmware_image_package.h>
11*afad5214SPatrick Delaunay 
12*afad5214SPatrick Delaunay #include <stm32cubeprogrammer.h>
13*afad5214SPatrick Delaunay #include <usb_dfu.h>
14*afad5214SPatrick Delaunay 
15*afad5214SPatrick Delaunay /* Undefined download address */
16*afad5214SPatrick Delaunay #define UNDEFINED_DOWN_ADDR	0xFFFFFFFF
17*afad5214SPatrick Delaunay 
18*afad5214SPatrick Delaunay struct dfu_state {
19*afad5214SPatrick Delaunay 	uint8_t phase;
20*afad5214SPatrick Delaunay 	uintptr_t base;
21*afad5214SPatrick Delaunay 	size_t len;
22*afad5214SPatrick Delaunay 	uintptr_t address;
23*afad5214SPatrick Delaunay 	/* working buffer */
24*afad5214SPatrick Delaunay 	uint8_t buffer[UCHAR_MAX];
25*afad5214SPatrick Delaunay };
26*afad5214SPatrick Delaunay 
27*afad5214SPatrick Delaunay static struct dfu_state dfu_state;
28*afad5214SPatrick Delaunay 
29*afad5214SPatrick Delaunay /* minimal size of Get Pḧase = offset for additionnl information */
30*afad5214SPatrick Delaunay #define	GET_PHASE_LEN	9
31*afad5214SPatrick Delaunay 
32*afad5214SPatrick Delaunay #define DFU_ERROR(...) \
33*afad5214SPatrick Delaunay 	{ \
34*afad5214SPatrick Delaunay 		ERROR(__VA_ARGS__); \
35*afad5214SPatrick Delaunay 		if (dfu->phase != PHASE_RESET) { \
36*afad5214SPatrick Delaunay 			snprintf((char *)&dfu->buffer[GET_PHASE_LEN], \
37*afad5214SPatrick Delaunay 				 sizeof(dfu->buffer) - GET_PHASE_LEN, \
38*afad5214SPatrick Delaunay 				 __VA_ARGS__); \
39*afad5214SPatrick Delaunay 			dfu->phase = PHASE_RESET; \
40*afad5214SPatrick Delaunay 			dfu->address = UNDEFINED_DOWN_ADDR; \
41*afad5214SPatrick Delaunay 			dfu->len = 0; \
42*afad5214SPatrick Delaunay 		} \
43*afad5214SPatrick Delaunay 	}
44*afad5214SPatrick Delaunay 
45*afad5214SPatrick Delaunay static bool is_valid_header(fip_toc_header_t *header)
46*afad5214SPatrick Delaunay {
47*afad5214SPatrick Delaunay 	if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0U)) {
48*afad5214SPatrick Delaunay 		return true;
49*afad5214SPatrick Delaunay 	}
50*afad5214SPatrick Delaunay 
51*afad5214SPatrick Delaunay 	return false;
52*afad5214SPatrick Delaunay }
53*afad5214SPatrick Delaunay 
54*afad5214SPatrick Delaunay static int dfu_callback_upload(uint8_t alt, uintptr_t *buffer, uint32_t *len,
55*afad5214SPatrick Delaunay 			       void *user_data)
56*afad5214SPatrick Delaunay {
57*afad5214SPatrick Delaunay 	int result = 0;
58*afad5214SPatrick Delaunay 	uint32_t length = 0;
59*afad5214SPatrick Delaunay 	struct dfu_state *dfu = (struct dfu_state *)user_data;
60*afad5214SPatrick Delaunay 
61*afad5214SPatrick Delaunay 	switch (usb_dfu_get_phase(alt)) {
62*afad5214SPatrick Delaunay 	case PHASE_CMD:
63*afad5214SPatrick Delaunay 		/* Get Pḧase */
64*afad5214SPatrick Delaunay 		dfu->buffer[0] = dfu->phase;
65*afad5214SPatrick Delaunay 		dfu->buffer[1] = (uint8_t)(dfu->address);
66*afad5214SPatrick Delaunay 		dfu->buffer[2] = (uint8_t)(dfu->address >> 8);
67*afad5214SPatrick Delaunay 		dfu->buffer[3] = (uint8_t)(dfu->address >> 16);
68*afad5214SPatrick Delaunay 		dfu->buffer[4] = (uint8_t)(dfu->address >> 24);
69*afad5214SPatrick Delaunay 		dfu->buffer[5] = 0x00;
70*afad5214SPatrick Delaunay 		dfu->buffer[6] = 0x00;
71*afad5214SPatrick Delaunay 		dfu->buffer[7] = 0x00;
72*afad5214SPatrick Delaunay 		dfu->buffer[8] = 0x00;
73*afad5214SPatrick Delaunay 		length = GET_PHASE_LEN;
74*afad5214SPatrick Delaunay 		if (dfu->phase == PHASE_FLASHLAYOUT &&
75*afad5214SPatrick Delaunay 		    dfu->address == UNDEFINED_DOWN_ADDR) {
76*afad5214SPatrick Delaunay 			INFO("Send detach request\n");
77*afad5214SPatrick Delaunay 			dfu->buffer[length++] = 0x01;
78*afad5214SPatrick Delaunay 		}
79*afad5214SPatrick Delaunay 		if (dfu->phase == PHASE_RESET) {
80*afad5214SPatrick Delaunay 			/* error information is added by DFU_ERROR macro */
81*afad5214SPatrick Delaunay 			length += strnlen((char *)&dfu->buffer[GET_PHASE_LEN],
82*afad5214SPatrick Delaunay 					  sizeof(dfu->buffer) - GET_PHASE_LEN)
83*afad5214SPatrick Delaunay 				  - 1;
84*afad5214SPatrick Delaunay 		}
85*afad5214SPatrick Delaunay 		break;
86*afad5214SPatrick Delaunay 
87*afad5214SPatrick Delaunay 	default:
88*afad5214SPatrick Delaunay 		DFU_ERROR("phase ID :%i, alternate %i for phase %i\n",
89*afad5214SPatrick Delaunay 			  dfu->phase, alt, usb_dfu_get_phase(alt));
90*afad5214SPatrick Delaunay 		result = -EIO;
91*afad5214SPatrick Delaunay 		break;
92*afad5214SPatrick Delaunay 	}
93*afad5214SPatrick Delaunay 
94*afad5214SPatrick Delaunay 	if (result == 0) {
95*afad5214SPatrick Delaunay 		*len = length;
96*afad5214SPatrick Delaunay 		*buffer = (uintptr_t)dfu->buffer;
97*afad5214SPatrick Delaunay 	}
98*afad5214SPatrick Delaunay 
99*afad5214SPatrick Delaunay 	return result;
100*afad5214SPatrick Delaunay }
101*afad5214SPatrick Delaunay 
102*afad5214SPatrick Delaunay static int dfu_callback_download(uint8_t alt, uintptr_t *buffer, uint32_t *len,
103*afad5214SPatrick Delaunay 				 void *user_data)
104*afad5214SPatrick Delaunay {
105*afad5214SPatrick Delaunay 	struct dfu_state *dfu = (struct dfu_state *)user_data;
106*afad5214SPatrick Delaunay 
107*afad5214SPatrick Delaunay 	if ((dfu->phase != usb_dfu_get_phase(alt)) ||
108*afad5214SPatrick Delaunay 	    (dfu->address == UNDEFINED_DOWN_ADDR)) {
109*afad5214SPatrick Delaunay 		DFU_ERROR("phase ID :%i, alternate %i, address %x\n",
110*afad5214SPatrick Delaunay 			  dfu->phase, alt, (uint32_t)dfu->address);
111*afad5214SPatrick Delaunay 		return -EIO;
112*afad5214SPatrick Delaunay 	}
113*afad5214SPatrick Delaunay 
114*afad5214SPatrick Delaunay 	VERBOSE("Download %d %lx %x\n", alt, dfu->address, *len);
115*afad5214SPatrick Delaunay 	*buffer = dfu->address;
116*afad5214SPatrick Delaunay 	dfu->address += *len;
117*afad5214SPatrick Delaunay 
118*afad5214SPatrick Delaunay 	if (dfu->address - dfu->base > dfu->len) {
119*afad5214SPatrick Delaunay 		return  -EIO;
120*afad5214SPatrick Delaunay 	}
121*afad5214SPatrick Delaunay 
122*afad5214SPatrick Delaunay 	return 0;
123*afad5214SPatrick Delaunay }
124*afad5214SPatrick Delaunay 
125*afad5214SPatrick Delaunay static int dfu_callback_manifestation(uint8_t alt, void *user_data)
126*afad5214SPatrick Delaunay {
127*afad5214SPatrick Delaunay 	struct dfu_state *dfu = (struct dfu_state *)user_data;
128*afad5214SPatrick Delaunay 
129*afad5214SPatrick Delaunay 	if (dfu->phase != usb_dfu_get_phase(alt)) {
130*afad5214SPatrick Delaunay 		ERROR("Manifestation phase ID :%i, alternate %i, address %lx\n",
131*afad5214SPatrick Delaunay 		      dfu->phase, alt, dfu->address);
132*afad5214SPatrick Delaunay 		return -EIO;
133*afad5214SPatrick Delaunay 	}
134*afad5214SPatrick Delaunay 
135*afad5214SPatrick Delaunay 	INFO("phase ID :%i, Manifestation %d at %lx\n",
136*afad5214SPatrick Delaunay 	     dfu->phase, alt, dfu->address);
137*afad5214SPatrick Delaunay 
138*afad5214SPatrick Delaunay 	switch (dfu->phase) {
139*afad5214SPatrick Delaunay 	case PHASE_SSBL:
140*afad5214SPatrick Delaunay 		if (!is_valid_header((fip_toc_header_t *)dfu->base)) {
141*afad5214SPatrick Delaunay 			DFU_ERROR("FIP Header check failed for phase %d\n", alt);
142*afad5214SPatrick Delaunay 			return -EIO;
143*afad5214SPatrick Delaunay 		}
144*afad5214SPatrick Delaunay 		VERBOSE("FIP header looks OK.\n");
145*afad5214SPatrick Delaunay 
146*afad5214SPatrick Delaunay 		/* Configure End with request detach */
147*afad5214SPatrick Delaunay 		dfu->phase = PHASE_FLASHLAYOUT;
148*afad5214SPatrick Delaunay 		dfu->address = UNDEFINED_DOWN_ADDR;
149*afad5214SPatrick Delaunay 		dfu->len = 0;
150*afad5214SPatrick Delaunay 		break;
151*afad5214SPatrick Delaunay 	default:
152*afad5214SPatrick Delaunay 		DFU_ERROR("Unknown phase\n");
153*afad5214SPatrick Delaunay 	}
154*afad5214SPatrick Delaunay 
155*afad5214SPatrick Delaunay 	return 0;
156*afad5214SPatrick Delaunay }
157*afad5214SPatrick Delaunay 
158*afad5214SPatrick Delaunay /* Open a connection to the USB device */
159*afad5214SPatrick Delaunay static const struct usb_dfu_media usb_dfu_fops = {
160*afad5214SPatrick Delaunay 	.upload = dfu_callback_upload,
161*afad5214SPatrick Delaunay 	.download = dfu_callback_download,
162*afad5214SPatrick Delaunay 	.manifestation = dfu_callback_manifestation,
163*afad5214SPatrick Delaunay };
164*afad5214SPatrick Delaunay 
165*afad5214SPatrick Delaunay int stm32cubeprog_usb_load(struct usb_handle *usb_core_handle,
166*afad5214SPatrick Delaunay 			   uintptr_t base,
167*afad5214SPatrick Delaunay 			   size_t len)
168*afad5214SPatrick Delaunay {
169*afad5214SPatrick Delaunay 	int ret;
170*afad5214SPatrick Delaunay 
171*afad5214SPatrick Delaunay 	usb_core_handle->user_data = (void *)&dfu_state;
172*afad5214SPatrick Delaunay 
173*afad5214SPatrick Delaunay 	INFO("DFU USB START...\n");
174*afad5214SPatrick Delaunay 	ret = usb_core_start(usb_core_handle);
175*afad5214SPatrick Delaunay 	if (ret != USBD_OK) {
176*afad5214SPatrick Delaunay 		return -EIO;
177*afad5214SPatrick Delaunay 	}
178*afad5214SPatrick Delaunay 
179*afad5214SPatrick Delaunay 	dfu_state.phase = PHASE_SSBL;
180*afad5214SPatrick Delaunay 	dfu_state.address = base;
181*afad5214SPatrick Delaunay 	dfu_state.base = base;
182*afad5214SPatrick Delaunay 	dfu_state.len = len;
183*afad5214SPatrick Delaunay 
184*afad5214SPatrick Delaunay 	ret = usb_dfu_loop(usb_core_handle, &usb_dfu_fops);
185*afad5214SPatrick Delaunay 	if (ret != USBD_OK) {
186*afad5214SPatrick Delaunay 		return -EIO;
187*afad5214SPatrick Delaunay 	}
188*afad5214SPatrick Delaunay 
189*afad5214SPatrick Delaunay 	INFO("DFU USB STOP...\n");
190*afad5214SPatrick Delaunay 	ret = usb_core_stop(usb_core_handle);
191*afad5214SPatrick Delaunay 	if (ret != USBD_OK) {
192*afad5214SPatrick Delaunay 		return -EIO;
193*afad5214SPatrick Delaunay 	}
194*afad5214SPatrick Delaunay 
195*afad5214SPatrick Delaunay 	return 0;
196*afad5214SPatrick Delaunay }
197