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