delousing target_core_file a bit
[linux-drm-fsl-dcu.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/version.h>
17 #include <linux/kernel.h>
18 #include <linux/mm.h>
19 #include <linux/usb.h>
20 #include <linux/unistd.h>
21 #include <linux/slab.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 DH2B(x)         __cpu_to_be32(x)
33 #define DL2H(x)         __le32_to_cpu(x)
34
35 #define MIN(a, b)       ((a) > (b) ? (b) : (a))
36
37 #define MAX_IMG_CNT             16
38 #define UIMG_PATH               "/lib/firmware/gdm72xx/gdmuimg.bin"
39 #define KERN_PATH               "/lib/firmware/gdm72xx/zImage"
40 #define FS_PATH                 "/lib/firmware/gdm72xx/ramdisk.jffs2"
41
42 struct dn_header {
43         u32     magic_num;
44         u32     file_size;
45 };
46
47 struct img_header {
48         u32             magic_code;
49         u32             count;
50         u32             len;
51         u32             offset[MAX_IMG_CNT];
52         char    hostname[32];
53         char    date[32];
54 };
55
56 struct fw_info {
57         u32             id;
58         u32             len;
59         u32             kernel_len;
60         u32             rootfs_len;
61         u32             kernel_offset;
62         u32             rootfs_offset;
63         u32             fw_ver;
64         u32             mac_ver;
65         char    hostname[32];
66         char    userid[16];
67         char    date[32];
68         char    user_desc[128];
69 };
70
71 static void array_le32_to_cpu(u32 *arr, int num)
72 {
73         int i;
74         for (i = 0; i < num; i++, arr++)
75                 *arr = DL2H(*arr);
76 }
77
78 static u8 *tx_buf;
79
80 static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len)
81 {
82         int ret;
83         int actual;
84
85         ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len,
86                         &actual, 1000);
87
88         if (ret < 0) {
89                 printk(KERN_ERR "Error : usb_bulk_msg ( result = %d )\n", ret);
90                 return ret;
91         }
92         return 0;
93 }
94
95 static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
96 {
97         int ret;
98         int actual;
99
100         ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
101                         &actual, 5000);
102
103         if (ret < 0) {
104                 printk(KERN_ERR "Error : usb_bulk_msg(recv) ( result = %d )\n",
105                         ret);
106                 return ret;
107         }
108         return 0;
109 }
110
111 static int download_image(struct usb_device *usbdev, struct file *filp,
112                                 loff_t *pos, u32 img_len, u32 magic_num)
113 {
114         struct dn_header h;
115         int ret = 0;
116         u32 size;
117         int len, readn;
118
119         size = (img_len + DOWNLOAD_SIZE - 1) & ~(DOWNLOAD_SIZE - 1);
120         h.magic_num = DH2B(magic_num);
121         h.file_size = DH2B(size);
122
123         ret = gdm_wibro_send(usbdev, &h, sizeof(h));
124         if (ret < 0)
125                 goto out;
126
127         readn = 0;
128         while ((len = filp->f_op->read(filp, tx_buf, DOWNLOAD_SIZE, pos))) {
129
130                 if (len < 0) {
131                         ret = -1;
132                         goto out;
133                 }
134                 readn += len;
135
136                 ret = gdm_wibro_send(usbdev, tx_buf, DOWNLOAD_SIZE);
137                 if (ret < 0)
138                         goto out;
139                 if (readn >= img_len)
140                         break;
141         }
142
143         if (readn < img_len) {
144                 printk(KERN_ERR "gdmwm: Cannot read to the requested size. "
145                         "Read = %d Requested = %d\n", readn, img_len);
146                 ret = -EIO;
147         }
148 out:
149
150         return ret;
151 }
152
153 int usb_boot(struct usb_device *usbdev, u16 pid)
154 {
155         int i, ret = 0;
156         struct file *filp = NULL;
157         struct inode *inode = NULL;
158         static mm_segment_t fs;
159         struct img_header hdr;
160         struct fw_info fw_info;
161         loff_t pos = 0;
162         char *img_name = UIMG_PATH;
163         int len;
164
165         tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
166         if (tx_buf == NULL) {
167                 printk(KERN_ERR "Error: kmalloc\n");
168                 return -ENOMEM;
169         }
170
171         fs = get_fs();
172         set_fs(get_ds());
173
174         filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0);
175         if (IS_ERR(filp)) {
176                 printk(KERN_ERR "Can't find %s.\n", img_name);
177                 ret = -ENOENT;
178                 goto restore_fs;
179         }
180
181         inode = filp->f_dentry->d_inode;
182         if (!S_ISREG(inode->i_mode)) {
183                 printk(KERN_ERR "Invalid file type: %s\n", img_name);
184                 ret = -EINVAL;
185                 goto out;
186         }
187
188         len = filp->f_op->read(filp, (u8 *)&hdr, sizeof(hdr), &pos);
189         if (len != sizeof(hdr)) {
190                 printk(KERN_ERR "gdmwm: Cannot read the image info.\n");
191                 ret = -EIO;
192                 goto out;
193         }
194
195         array_le32_to_cpu((u32 *)&hdr, 19);
196 #if 0
197         if (hdr.magic_code != 0x10767fff) {
198                 printk(KERN_ERR "gdmwm: Invalid magic code 0x%08x\n",
199                         hdr.magic_code);
200                 ret = -EINVAL;
201                 goto out;
202         }
203 #endif
204         if (hdr.count > MAX_IMG_CNT) {
205                 printk(KERN_ERR "gdmwm: Too many images. %d\n", hdr.count);
206                 ret = -EINVAL;
207                 goto out;
208         }
209
210         for (i = 0; i < hdr.count; i++) {
211                 if (hdr.offset[i] > hdr.len) {
212                         printk(KERN_ERR "gdmwm: Invalid offset. "
213                                 "Entry = %d Offset = 0x%08x "
214                                 "Image length = 0x%08x\n",
215                                 i, hdr.offset[i], hdr.len);
216                         ret = -EINVAL;
217                         goto out;
218                 }
219
220                 pos = hdr.offset[i];
221                 len = filp->f_op->read(filp, (u8 *)&fw_info, sizeof(fw_info),
222                                         &pos);
223                 if (len != sizeof(fw_info)) {
224                         printk(KERN_ERR "gdmwm: Cannot read the FW info.\n");
225                         ret = -EIO;
226                         goto out;
227                 }
228
229                 array_le32_to_cpu((u32 *)&fw_info, 8);
230 #if 0
231                 if ((fw_info.id & 0xfffff000) != 0x10767000) {
232                         printk(KERN_ERR "gdmwm: Invalid FW id. 0x%08x\n",
233                                 fw_info.id);
234                         ret = -EIO;
235                         goto out;
236                 }
237 #endif
238
239                 if ((fw_info.id & 0xffff) != pid)
240                         continue;
241
242                 pos = hdr.offset[i] + fw_info.kernel_offset;
243                 ret = download_image(usbdev, filp, &pos, fw_info.kernel_len,
244                                 DN_KERNEL_MAGIC_NUMBER);
245                 if (ret < 0)
246                         goto out;
247                 printk(KERN_INFO "GCT: Kernel download success.\n");
248
249                 pos = hdr.offset[i] + fw_info.rootfs_offset;
250                 ret = download_image(usbdev, filp, &pos, fw_info.rootfs_len,
251                                 DN_ROOTFS_MAGIC_NUMBER);
252                 if (ret < 0)
253                         goto out;
254                 printk(KERN_INFO "GCT: Filesystem download success.\n");
255
256                 break;
257         }
258
259         if (i == hdr.count) {
260                 printk(KERN_ERR "Firmware for gsk%x is not installed.\n", pid);
261                 ret = -EINVAL;
262         }
263 out:
264         filp_close(filp, NULL);
265
266 restore_fs:
267         set_fs(fs);
268         kfree(tx_buf);
269         return ret;
270 }
271
272 /*#define GDM7205_PADDING               256 */
273 #define DOWNLOAD_CHUCK                  2048
274 #define KERNEL_TYPE_STRING              "linux"
275 #define FS_TYPE_STRING                  "rootfs"
276
277 static int em_wait_ack(struct usb_device *usbdev, int send_zlp)
278 {
279         int ack;
280         int ret = -1;
281
282         if (send_zlp) {
283                 /*Send ZLP*/
284                 ret = gdm_wibro_send(usbdev, NULL, 0);
285                 if (ret < 0)
286                         goto out;
287         }
288
289         /*Wait for ACK*/
290         ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
291         if (ret < 0)
292                 goto out;
293 out:
294         return ret;
295 }
296
297 static int em_download_image(struct usb_device *usbdev, char *path,
298                                 char *type_string)
299 {
300         struct file *filp;
301         struct inode *inode;
302         static mm_segment_t fs;
303         char *buf = NULL;
304         loff_t pos = 0;
305         int ret = 0;
306         int len, readn = 0;
307         #if defined(GDM7205_PADDING)
308         const int pad_size = GDM7205_PADDING;
309         #else
310         const int pad_size = 0;
311         #endif
312
313         fs = get_fs();
314         set_fs(get_ds());
315
316         filp = filp_open(path, O_RDONLY | O_LARGEFILE, 0);
317         if (IS_ERR(filp)) {
318                 printk(KERN_ERR "Can't find %s.\n", path);
319                 set_fs(fs);
320                 ret = -ENOENT;
321                 goto restore_fs;
322         }
323
324         inode = filp->f_dentry->d_inode;
325         if (!S_ISREG(inode->i_mode)) {
326                 printk(KERN_ERR "Invalid file type: %s\n", path);
327                 ret = -EINVAL;
328                 goto out;
329         }
330
331         buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
332         if (buf == NULL) {
333                 printk(KERN_ERR "Error: kmalloc\n");
334                 return -ENOMEM;
335         }
336
337         strcpy(buf+pad_size, type_string);
338         ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size);
339         if (ret < 0)
340                 goto out;
341
342         while ((len = filp->f_op->read(filp, buf+pad_size, DOWNLOAD_CHUCK,
343                                         &pos))) {
344                 if (len < 0) {
345                         ret = -1;
346                         goto out;
347                 }
348                 readn += len;
349
350                 ret = gdm_wibro_send(usbdev, buf, len+pad_size);
351                 if (ret < 0)
352                         goto out;
353
354                 ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
355                 if (ret < 0)
356                         goto out;
357         }
358
359         ret = em_wait_ack(usbdev, 1);
360         if (ret < 0)
361                 goto out;
362
363 out:
364         filp_close(filp, NULL);
365
366 restore_fs:
367         set_fs(fs);
368
369         kfree(buf);
370
371         return ret;
372 }
373
374 static int em_fw_reset(struct usb_device *usbdev)
375 {
376         int ret;
377
378         /*Send ZLP*/
379         ret = gdm_wibro_send(usbdev, NULL, 0);
380         return ret;
381 }
382
383 int usb_emergency(struct usb_device *usbdev)
384 {
385         int ret;
386
387         ret = em_download_image(usbdev, KERN_PATH, KERNEL_TYPE_STRING);
388         if (ret < 0)
389                 goto out;
390         printk(KERN_INFO "GCT Emergency: Kernel download success.\n");
391
392         ret = em_download_image(usbdev, FS_PATH, FS_TYPE_STRING);
393         if (ret < 0)
394                 goto out;
395         printk(KERN_INFO "GCT Emergency: Filesystem download success.\n");
396
397         ret = em_fw_reset(usbdev);
398 out:
399         return ret;
400 }