Merge branch 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux
[deliverable/linux.git] / drivers / staging / gdm72xx / usb_boot.c
1 /*
2 * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14 #include <linux/uaccess.h>
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/mm.h>
18 #include <linux/usb.h>
19 #include <linux/unistd.h>
20 #include <linux/slab.h>
21 #include <linux/firmware.h>
22
23 #include <asm/byteorder.h>
24 #include "gdm_usb.h"
25 #include "usb_boot.h"
26
27 #define DN_KERNEL_MAGIC_NUMBER 0x10760001
28 #define DN_ROOTFS_MAGIC_NUMBER 0x10760002
29
30 #define DOWNLOAD_SIZE 1024
31
32 #define MAX_IMG_CNT 16
33 #define FW_DIR "gdm72xx/"
34 #define FW_UIMG "gdmuimg.bin"
35 #define FW_KERN "zImage"
36 #define FW_FS "ramdisk.jffs2"
37
38 struct dn_header {
39 u32 magic_num;
40 u32 file_size;
41 };
42
43 struct img_header {
44 u32 magic_code;
45 u32 count;
46 u32 len;
47 u32 offset[MAX_IMG_CNT];
48 char hostname[32];
49 char date[32];
50 };
51
52 struct fw_info {
53 u32 id;
54 u32 len;
55 u32 kernel_len;
56 u32 rootfs_len;
57 u32 kernel_offset;
58 u32 rootfs_offset;
59 u32 fw_ver;
60 u32 mac_ver;
61 char hostname[32];
62 char userid[16];
63 char date[32];
64 char user_desc[128];
65 };
66
67 static void array_le32_to_cpu(u32 *arr, int num)
68 {
69 int i;
70 for (i = 0; i < num; i++, arr++)
71 *arr = __le32_to_cpu(*arr);
72 }
73
74 static u8 *tx_buf;
75
76 static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len)
77 {
78 int ret;
79 int actual;
80
81 ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len,
82 &actual, 1000);
83
84 if (ret < 0) {
85 printk(KERN_ERR "Error : usb_bulk_msg ( result = %d )\n", ret);
86 return ret;
87 }
88 return 0;
89 }
90
91 static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
92 {
93 int ret;
94 int actual;
95
96 ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
97 &actual, 5000);
98
99 if (ret < 0) {
100 printk(KERN_ERR "Error : usb_bulk_msg(recv) ( result = %d )\n",
101 ret);
102 return ret;
103 }
104 return 0;
105 }
106
107 static int download_image(struct usb_device *usbdev,
108 const struct firmware *firm,
109 loff_t pos, u32 img_len, u32 magic_num)
110 {
111 struct dn_header h;
112 int ret = 0;
113 u32 size;
114
115 size = ALIGN(img_len, DOWNLOAD_SIZE);
116 h.magic_num = __cpu_to_be32(magic_num);
117 h.file_size = __cpu_to_be32(size);
118
119 ret = gdm_wibro_send(usbdev, &h, sizeof(h));
120 if (ret < 0)
121 return ret;
122
123 while (img_len > 0) {
124 if (img_len > DOWNLOAD_SIZE)
125 size = DOWNLOAD_SIZE;
126 else
127 size = img_len; /* the last chunk of data */
128
129 memcpy(tx_buf, firm->data + pos, size);
130 ret = gdm_wibro_send(usbdev, tx_buf, size);
131
132 if (ret < 0)
133 return ret;
134
135 img_len -= size;
136 pos += size;
137 }
138
139 return ret;
140 }
141
142 int usb_boot(struct usb_device *usbdev, u16 pid)
143 {
144 int i, ret = 0;
145 struct img_header hdr;
146 struct fw_info fw_info;
147 loff_t pos = 0;
148 char *img_name = FW_DIR FW_UIMG;
149 const struct firmware *firm;
150
151 ret = request_firmware(&firm, img_name, &usbdev->dev);
152 if (ret < 0) {
153 printk(KERN_ERR
154 "requesting firmware %s failed with error %d\n",
155 img_name, ret);
156 return ret;
157 }
158
159 tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
160 if (tx_buf == NULL) {
161 printk(KERN_ERR "Error: kmalloc\n");
162 return -ENOMEM;
163 }
164
165 if (firm->size < sizeof(hdr)) {
166 printk(KERN_ERR "gdmwm: Cannot read the image info.\n");
167 ret = -EIO;
168 goto out;
169 }
170 memcpy(&hdr, firm->data, sizeof(hdr));
171
172 array_le32_to_cpu((u32 *)&hdr, 19);
173 #if 0
174 if (hdr.magic_code != 0x10767fff) {
175 printk(KERN_ERR "gdmwm: Invalid magic code 0x%08x\n",
176 hdr.magic_code);
177 ret = -EINVAL;
178 goto out;
179 }
180 #endif
181 if (hdr.count > MAX_IMG_CNT) {
182 printk(KERN_ERR "gdmwm: Too many images. %d\n", hdr.count);
183 ret = -EINVAL;
184 goto out;
185 }
186
187 for (i = 0; i < hdr.count; i++) {
188 if (hdr.offset[i] > hdr.len) {
189 printk(KERN_ERR "gdmwm: Invalid offset. "
190 "Entry = %d Offset = 0x%08x "
191 "Image length = 0x%08x\n",
192 i, hdr.offset[i], hdr.len);
193 ret = -EINVAL;
194 goto out;
195 }
196
197 pos = hdr.offset[i];
198 if (firm->size < sizeof(fw_info) + pos) {
199 printk(KERN_ERR "gdmwm: Cannot read the FW info.\n");
200 ret = -EIO;
201 goto out;
202 }
203 memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
204
205 array_le32_to_cpu((u32 *)&fw_info, 8);
206 #if 0
207 if ((fw_info.id & 0xfffff000) != 0x10767000) {
208 printk(KERN_ERR "gdmwm: Invalid FW id. 0x%08x\n",
209 fw_info.id);
210 ret = -EIO;
211 goto out;
212 }
213 #endif
214
215 if ((fw_info.id & 0xffff) != pid)
216 continue;
217
218 pos = hdr.offset[i] + fw_info.kernel_offset;
219 if (firm->size < fw_info.kernel_len + pos) {
220 printk(KERN_ERR "gdmwm: Kernel FW is too small.\n");
221 goto out;
222 }
223
224 ret = download_image(usbdev, firm, pos,
225 fw_info.kernel_len, DN_KERNEL_MAGIC_NUMBER);
226 if (ret < 0)
227 goto out;
228 printk(KERN_INFO "GCT: Kernel download success.\n");
229
230 pos = hdr.offset[i] + fw_info.rootfs_offset;
231 if (firm->size < fw_info.rootfs_len + pos) {
232 printk(KERN_ERR "gdmwm: Filesystem FW is too small.\n");
233 goto out;
234 }
235 ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
236 DN_ROOTFS_MAGIC_NUMBER);
237 if (ret < 0)
238 goto out;
239 printk(KERN_INFO "GCT: Filesystem download success.\n");
240
241 break;
242 }
243
244 if (i == hdr.count) {
245 printk(KERN_ERR "Firmware for gsk%x is not installed.\n", pid);
246 ret = -EINVAL;
247 }
248 out:
249 release_firmware(firm);
250 kfree(tx_buf);
251 return ret;
252 }
253
254 /*#define GDM7205_PADDING 256 */
255 #define DOWNLOAD_CHUCK 2048
256 #define KERNEL_TYPE_STRING "linux"
257 #define FS_TYPE_STRING "rootfs"
258
259 static int em_wait_ack(struct usb_device *usbdev, int send_zlp)
260 {
261 int ack;
262 int ret = -1;
263
264 if (send_zlp) {
265 /*Send ZLP*/
266 ret = gdm_wibro_send(usbdev, NULL, 0);
267 if (ret < 0)
268 goto out;
269 }
270
271 /*Wait for ACK*/
272 ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
273 if (ret < 0)
274 goto out;
275 out:
276 return ret;
277 }
278
279 static int em_download_image(struct usb_device *usbdev, const char *img_name,
280 char *type_string)
281 {
282 char *buf = NULL;
283 loff_t pos = 0;
284 int ret = 0;
285 int len;
286 int img_len;
287 const struct firmware *firm;
288 #if defined(GDM7205_PADDING)
289 const int pad_size = GDM7205_PADDING;
290 #else
291 const int pad_size = 0;
292 #endif
293
294 ret = request_firmware(&firm, img_name, &usbdev->dev);
295 if (ret < 0) {
296 printk(KERN_ERR
297 "requesting firmware %s failed with error %d\n",
298 img_name, ret);
299 return ret;
300 }
301
302 buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
303 if (buf == NULL) {
304 printk(KERN_ERR "Error: kmalloc\n");
305 return -ENOMEM;
306 }
307
308 strcpy(buf+pad_size, type_string);
309 ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size);
310 if (ret < 0)
311 goto out;
312
313 img_len = firm->size;
314
315 if (img_len <= 0) {
316 ret = -1;
317 goto out;
318 }
319
320 while (img_len > 0) {
321 if (img_len > DOWNLOAD_CHUCK)
322 len = DOWNLOAD_CHUCK;
323 else
324 len = img_len; /* the last chunk of data */
325
326 memcpy(buf+pad_size, firm->data + pos, len);
327 ret = gdm_wibro_send(usbdev, buf, len+pad_size);
328
329 if (ret < 0)
330 goto out;
331
332 img_len -= DOWNLOAD_CHUCK;
333 pos += DOWNLOAD_CHUCK;
334
335 ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
336 if (ret < 0)
337 goto out;
338 }
339
340 ret = em_wait_ack(usbdev, 1);
341 if (ret < 0)
342 goto out;
343
344 out:
345 release_firmware(firm);
346 kfree(buf);
347
348 return ret;
349 }
350
351 static int em_fw_reset(struct usb_device *usbdev)
352 {
353 int ret;
354
355 /*Send ZLP*/
356 ret = gdm_wibro_send(usbdev, NULL, 0);
357 return ret;
358 }
359
360 int usb_emergency(struct usb_device *usbdev)
361 {
362 int ret;
363 const char *kern_name = FW_DIR FW_KERN;
364 const char *fs_name = FW_DIR FW_FS;
365
366 ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
367 if (ret < 0)
368 return ret;
369 printk(KERN_INFO "GCT Emergency: Kernel download success.\n");
370
371 ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
372 if (ret < 0)
373 return ret;
374 printk(KERN_INFO "GCT Emergency: Filesystem download success.\n");
375
376 ret = em_fw_reset(usbdev);
377
378 return ret;
379 }
This page took 0.057381 seconds and 5 git commands to generate.