]>
git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/staging/gdm72xx/usb_boot.c
2 * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
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.
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.
14 #include <linux/uaccess.h>
15 #include <linux/module.h>
16 #include <linux/kernel.h>
18 #include <linux/usb.h>
19 #include <linux/unistd.h>
20 #include <linux/slab.h>
21 #include <linux/firmware.h>
23 #include <asm/byteorder.h>
27 #define DN_KERNEL_MAGIC_NUMBER 0x10760001
28 #define DN_ROOTFS_MAGIC_NUMBER 0x10760002
30 #define DOWNLOAD_SIZE 1024
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"
47 u32 offset
[MAX_IMG_CNT
];
67 static void array_le32_to_cpu(u32
*arr
, int num
)
71 for (i
= 0; i
< num
; i
++, arr
++)
77 static int gdm_wibro_send(struct usb_device
*usbdev
, void *data
, int len
)
82 ret
= usb_bulk_msg(usbdev
, usb_sndbulkpipe(usbdev
, 1), data
, len
,
86 dev_err(&usbdev
->dev
, "Error : usb_bulk_msg ( result = %d )\n",
93 static int gdm_wibro_recv(struct usb_device
*usbdev
, void *data
, int len
)
98 ret
= usb_bulk_msg(usbdev
, usb_rcvbulkpipe(usbdev
, 2), data
, len
,
102 dev_err(&usbdev
->dev
,
103 "Error : usb_bulk_msg(recv) ( result = %d )\n", ret
);
109 static int download_image(struct usb_device
*usbdev
,
110 const struct firmware
*firm
,
111 loff_t pos
, u32 img_len
, u32 magic_num
)
117 size
= ALIGN(img_len
, DOWNLOAD_SIZE
);
118 h
.magic_num
= cpu_to_be32(magic_num
);
119 h
.file_size
= cpu_to_be32(size
);
121 ret
= gdm_wibro_send(usbdev
, &h
, sizeof(h
));
125 while (img_len
> 0) {
126 if (img_len
> DOWNLOAD_SIZE
)
127 size
= DOWNLOAD_SIZE
;
129 size
= img_len
; /* the last chunk of data */
131 memcpy(tx_buf
, firm
->data
+ pos
, size
);
132 ret
= gdm_wibro_send(usbdev
, tx_buf
, size
);
144 int usb_boot(struct usb_device
*usbdev
, u16 pid
)
147 struct img_header hdr
;
148 struct fw_info fw_info
;
150 char *img_name
= FW_DIR FW_UIMG
;
151 const struct firmware
*firm
;
153 ret
= request_firmware(&firm
, img_name
, &usbdev
->dev
);
155 dev_err(&usbdev
->dev
,
156 "requesting firmware %s failed with error %d\n",
161 tx_buf
= kmalloc(DOWNLOAD_SIZE
, GFP_KERNEL
);
165 if (firm
->size
< sizeof(hdr
)) {
166 dev_err(&usbdev
->dev
, "Cannot read the image info.\n");
170 memcpy(&hdr
, firm
->data
, sizeof(hdr
));
172 array_le32_to_cpu((u32
*)&hdr
, 19);
174 if (hdr
.count
> MAX_IMG_CNT
) {
175 dev_err(&usbdev
->dev
, "Too many images. %d\n", hdr
.count
);
180 for (i
= 0; i
< hdr
.count
; i
++) {
181 if (hdr
.offset
[i
] > hdr
.len
) {
182 dev_err(&usbdev
->dev
,
183 "Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n",
184 i
, hdr
.offset
[i
], hdr
.len
);
190 if (firm
->size
< sizeof(fw_info
) + pos
) {
191 dev_err(&usbdev
->dev
, "Cannot read the FW info.\n");
195 memcpy(&fw_info
, firm
->data
+ pos
, sizeof(fw_info
));
197 array_le32_to_cpu((u32
*)&fw_info
, 8);
199 if ((fw_info
.id
& 0xffff) != pid
)
202 pos
= hdr
.offset
[i
] + fw_info
.kernel_offset
;
203 if (firm
->size
< fw_info
.kernel_len
+ pos
) {
204 dev_err(&usbdev
->dev
, "Kernel FW is too small.\n");
208 ret
= download_image(usbdev
, firm
, pos
, fw_info
.kernel_len
,
209 DN_KERNEL_MAGIC_NUMBER
);
212 dev_info(&usbdev
->dev
, "GCT: Kernel download success.\n");
214 pos
= hdr
.offset
[i
] + fw_info
.rootfs_offset
;
215 if (firm
->size
< fw_info
.rootfs_len
+ pos
) {
216 dev_err(&usbdev
->dev
, "Filesystem FW is too small.\n");
219 ret
= download_image(usbdev
, firm
, pos
, fw_info
.rootfs_len
,
220 DN_ROOTFS_MAGIC_NUMBER
);
223 dev_info(&usbdev
->dev
, "GCT: Filesystem download success.\n");
228 if (i
== hdr
.count
) {
229 dev_err(&usbdev
->dev
, "Firmware for gsk%x is not installed.\n",
234 release_firmware(firm
);
239 /*#define GDM7205_PADDING 256 */
240 #define DOWNLOAD_CHUCK 2048
241 #define KERNEL_TYPE_STRING "linux"
242 #define FS_TYPE_STRING "rootfs"
244 static int em_wait_ack(struct usb_device
*usbdev
, int send_zlp
)
251 ret
= gdm_wibro_send(usbdev
, NULL
, 0);
257 ret
= gdm_wibro_recv(usbdev
, &ack
, sizeof(ack
));
264 static int em_download_image(struct usb_device
*usbdev
, const char *img_name
,
272 const struct firmware
*firm
;
273 #if defined(GDM7205_PADDING)
274 const int pad_size
= GDM7205_PADDING
;
276 const int pad_size
= 0;
279 ret
= request_firmware(&firm
, img_name
, &usbdev
->dev
);
281 dev_err(&usbdev
->dev
,
282 "requesting firmware %s failed with error %d\n",
287 buf
= kmalloc(DOWNLOAD_CHUCK
+ pad_size
, GFP_KERNEL
);
291 strcpy(buf
+pad_size
, type_string
);
292 ret
= gdm_wibro_send(usbdev
, buf
, strlen(type_string
)+pad_size
);
296 img_len
= firm
->size
;
303 while (img_len
> 0) {
304 if (img_len
> DOWNLOAD_CHUCK
)
305 len
= DOWNLOAD_CHUCK
;
307 len
= img_len
; /* the last chunk of data */
309 memcpy(buf
+pad_size
, firm
->data
+ pos
, len
);
310 ret
= gdm_wibro_send(usbdev
, buf
, len
+pad_size
);
315 img_len
-= DOWNLOAD_CHUCK
;
316 pos
+= DOWNLOAD_CHUCK
;
318 ret
= em_wait_ack(usbdev
, ((len
+pad_size
) % 512 == 0));
323 ret
= em_wait_ack(usbdev
, 1);
328 release_firmware(firm
);
334 static int em_fw_reset(struct usb_device
*usbdev
)
337 return gdm_wibro_send(usbdev
, NULL
, 0);
340 int usb_emergency(struct usb_device
*usbdev
)
343 const char *kern_name
= FW_DIR FW_KERN
;
344 const char *fs_name
= FW_DIR FW_FS
;
346 ret
= em_download_image(usbdev
, kern_name
, KERNEL_TYPE_STRING
);
349 dev_err(&usbdev
->dev
, "GCT Emergency: Kernel download success.\n");
351 ret
= em_download_image(usbdev
, fs_name
, FS_TYPE_STRING
);
354 dev_info(&usbdev
->dev
, "GCT Emergency: Filesystem download success.\n");
356 ret
= em_fw_reset(usbdev
);