1247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn/*
2247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
3247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn *
4247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * This software is licensed under the terms of the GNU General Public
5247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * License version 2, as published by the Free Software Foundation, and
6247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * may be copied, distributed, and modified under those terms.
7247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn *
8247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * This program is distributed in the hope that it will be useful,
9247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * but WITHOUT ANY WARRANTY; without even the implied warranty of
10247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn * GNU General Public License for more details.
12247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn */
13247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
14247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <linux/uaccess.h>
15247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <linux/module.h>
16247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <linux/kernel.h>
17247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <linux/mm.h>
18247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <linux/usb.h>
19247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <linux/unistd.h>
20247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <linux/slab.h>
211839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin#include <linux/firmware.h>
22247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
23247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include <asm/byteorder.h>
24247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include "gdm_usb.h"
25247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#include "usb_boot.h"
26247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
27443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin#define DN_KERNEL_MAGIC_NUMBER	0x10760001
28443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin#define DN_ROOTFS_MAGIC_NUMBER	0x10760002
29247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
30443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin#define DOWNLOAD_SIZE		1024
31247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
32247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#define MAX_IMG_CNT		16
331839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin#define FW_DIR			"gdm72xx/"
341839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin#define FW_UIMG			"gdmuimg.bin"
353afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin#define FW_KERN			"zImage"
363afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin#define FW_FS			"ramdisk.jffs2"
37247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
38247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstruct dn_header {
39a8a175d9fc2ddb513baf5f043c1e9de512f886f7Ben Chan	__be32	magic_num;
40a8a175d9fc2ddb513baf5f043c1e9de512f886f7Ben Chan	__be32	file_size;
41247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn};
42247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
43247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstruct img_header {
44443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	magic_code;
45443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	count;
46443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	len;
47443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	offset[MAX_IMG_CNT];
48247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	char	hostname[32];
49247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	char	date[32];
50247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn};
51247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
52247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstruct fw_info {
53443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	id;
54443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	len;
55443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	kernel_len;
56443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	rootfs_len;
57443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	kernel_offset;
58443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	rootfs_offset;
59443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	fw_ver;
60443242d2feb68c90fbd96de212d76d7858ac0834Macpaul Lin	u32	mac_ver;
61247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	char	hostname[32];
62247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	char	userid[16];
63247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	char	date[32];
64247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	char	user_desc[128];
65247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn};
66247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
67247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstatic void array_le32_to_cpu(u32 *arr, int num)
68247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
69247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int i;
7039c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas
71247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	for (i = 0; i < num; i++, arr++)
72a8a175d9fc2ddb513baf5f043c1e9de512f886f7Ben Chan		le32_to_cpus(arr);
73247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
74247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
75247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstatic u8 *tx_buf;
76247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
77247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstatic int gdm_wibro_send(struct usb_device *usbdev, void *data, int len)
78247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
79247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int ret;
80247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int actual;
81247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
82247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len,
8339c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas			   &actual, 1000);
84247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
85247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0) {
863800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_err(&usbdev->dev, "Error : usb_bulk_msg ( result = %d )\n",
873800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			ret);
88247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		return ret;
89247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
90247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	return 0;
91247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
92247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
93247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstatic int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
94247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
95247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int ret;
96247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int actual;
97247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
98247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
9939c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas			   &actual, 5000);
100247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
101247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0) {
1023800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_err(&usbdev->dev,
1033800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			"Error : usb_bulk_msg(recv) ( result = %d )\n", ret);
104247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		return ret;
105247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
106247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	return 0;
107247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
108247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
109a3709f7a143d9522ede046d23ed46b7f4fe67102Davide Gianfortestatic int download_image(struct usb_device *usbdev,
110a3709f7a143d9522ede046d23ed46b7f4fe67102Davide Gianforte			  const struct firmware *firm,
11139c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas			  loff_t pos, u32 img_len, u32 magic_num)
112247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
113247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	struct dn_header h;
114247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int ret = 0;
115247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	u32 size;
116247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
1171839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	size = ALIGN(img_len, DOWNLOAD_SIZE);
118a8a175d9fc2ddb513baf5f043c1e9de512f886f7Ben Chan	h.magic_num = cpu_to_be32(magic_num);
119a8a175d9fc2ddb513baf5f043c1e9de512f886f7Ben Chan	h.file_size = cpu_to_be32(size);
120247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
121247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	ret = gdm_wibro_send(usbdev, &h, sizeof(h));
122247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0)
1231839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		return ret;
124247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
1251839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	while (img_len > 0) {
1261839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		if (img_len > DOWNLOAD_SIZE)
1271839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin			size = DOWNLOAD_SIZE;
1281839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		else
1291839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin			size = img_len;	/* the last chunk of data */
130247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
1311839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		memcpy(tx_buf, firm->data + pos, size);
1321839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		ret = gdm_wibro_send(usbdev, tx_buf, size);
133247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
134247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if (ret < 0)
1351839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin			return ret;
136247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
1371839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		img_len -= size;
1381839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		pos += size;
139247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
140247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
141247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	return ret;
142247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
143247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
144247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnint usb_boot(struct usb_device *usbdev, u16 pid)
145247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
146247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int i, ret = 0;
147247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	struct img_header hdr;
148247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	struct fw_info fw_info;
149247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	loff_t pos = 0;
1501839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	char *img_name = FW_DIR FW_UIMG;
1511839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	const struct firmware *firm;
1521839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin
1531839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	ret = request_firmware(&firm, img_name, &usbdev->dev);
1541839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	if (ret < 0) {
1553800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_err(&usbdev->dev,
1563800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			"requesting firmware %s failed with error %d\n",
1571839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin			img_name, ret);
1581839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		return ret;
1591839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	}
160247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
161247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
16278110bb8dc4a7ff331bfa3cfe7d4e287cfb3f22bJoe Perches	if (tx_buf == NULL)
163247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		return -ENOMEM;
164247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
1651839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	if (firm->size < sizeof(hdr)) {
1663800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_err(&usbdev->dev, "Cannot read the image info.\n");
167247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		ret = -EIO;
168247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		goto out;
169247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
1701839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	memcpy(&hdr, firm->data, sizeof(hdr));
171247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
172247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	array_le32_to_cpu((u32 *)&hdr, 19);
173b268666f6b25e4b09a612d69f4cb9a2089d7adb5Michalis Pappas
174247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (hdr.count > MAX_IMG_CNT) {
1753800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_err(&usbdev->dev, "Too many images. %d\n", hdr.count);
176247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		ret = -EINVAL;
177247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		goto out;
178247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
179247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
180247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	for (i = 0; i < hdr.count; i++) {
181247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if (hdr.offset[i] > hdr.len) {
1823800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			dev_err(&usbdev->dev,
1833800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki				"Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n",
184247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn				i, hdr.offset[i], hdr.len);
185247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			ret = -EINVAL;
186247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			goto out;
187247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		}
188247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
189247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		pos = hdr.offset[i];
1901839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		if (firm->size < sizeof(fw_info) + pos) {
1913800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			dev_err(&usbdev->dev, "Cannot read the FW info.\n");
192247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			ret = -EIO;
193247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			goto out;
194247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		}
1951839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
196247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
197247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		array_le32_to_cpu((u32 *)&fw_info, 8);
198247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
199247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if ((fw_info.id & 0xffff) != pid)
200247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			continue;
201247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
202247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		pos = hdr.offset[i] + fw_info.kernel_offset;
2031839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		if (firm->size < fw_info.kernel_len + pos) {
2043800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			dev_err(&usbdev->dev, "Kernel FW is too small.\n");
2051839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin			goto out;
2061839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		}
2071839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin
20839c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas		ret = download_image(usbdev, firm, pos, fw_info.kernel_len,
20939c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas				     DN_KERNEL_MAGIC_NUMBER);
210247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if (ret < 0)
211247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			goto out;
2123800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_info(&usbdev->dev, "GCT: Kernel download success.\n");
213247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
214247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		pos = hdr.offset[i] + fw_info.rootfs_offset;
2151839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		if (firm->size < fw_info.rootfs_len + pos) {
2163800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			dev_err(&usbdev->dev, "Filesystem FW is too small.\n");
2171839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin			goto out;
2181839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		}
2191839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin		ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
22039c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas				     DN_ROOTFS_MAGIC_NUMBER);
221247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if (ret < 0)
222247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			goto out;
2233800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_info(&usbdev->dev, "GCT: Filesystem download success.\n");
224247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
225247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		break;
226247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
227247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
228247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (i == hdr.count) {
2293800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_err(&usbdev->dev, "Firmware for gsk%x is not installed.\n",
2303800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			pid);
231247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		ret = -EINVAL;
232247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
233247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnout:
2341839c7ebd9363cc73b55ce48d4c3c6823c2fdd42Macpaul Lin	release_firmware(firm);
235247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	kfree(tx_buf);
236247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	return ret;
237247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
238247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
239247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn/*#define GDM7205_PADDING		256 */
240247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#define DOWNLOAD_CHUCK			2048
241247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#define KERNEL_TYPE_STRING		"linux"
242247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn#define FS_TYPE_STRING			"rootfs"
243247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
244247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstatic int em_wait_ack(struct usb_device *usbdev, int send_zlp)
245247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
246247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int ack;
247247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int ret = -1;
248247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
249247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (send_zlp) {
250247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		/*Send ZLP*/
251247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		ret = gdm_wibro_send(usbdev, NULL, 0);
252247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if (ret < 0)
253247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			goto out;
254247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
255247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
256247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	/*Wait for ACK*/
257247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
258247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0)
259247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		goto out;
260247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnout:
261247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	return ret;
262247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
263247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
2643afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Linstatic int em_download_image(struct usb_device *usbdev, const char *img_name,
26539c511f8cb90be18f12575e5bdaf566a5e9a56b5Michalis Pappas			     char *type_string)
266247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
267247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	char *buf = NULL;
268247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	loff_t pos = 0;
269247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int ret = 0;
2703afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	int len;
2713afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	int img_len;
2723afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	const struct firmware *firm;
273247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	#if defined(GDM7205_PADDING)
274247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	const int pad_size = GDM7205_PADDING;
275247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	#else
276247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	const int pad_size = 0;
277247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	#endif
278247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
2793afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	ret = request_firmware(&firm, img_name, &usbdev->dev);
2803afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	if (ret < 0) {
2813800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki		dev_err(&usbdev->dev,
2823800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki			"requesting firmware %s failed with error %d\n",
2833afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin			img_name, ret);
2843afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		return ret;
285247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
286247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
287247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
28878110bb8dc4a7ff331bfa3cfe7d4e287cfb3f22bJoe Perches	if (buf == NULL)
289247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		return -ENOMEM;
290247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
291247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	strcpy(buf+pad_size, type_string);
292247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size);
293247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0)
294247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		goto out;
295247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
2963afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	img_len = firm->size;
297247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
2983afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	if (img_len <= 0) {
2993afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		ret = -1;
3003afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		goto out;
3013afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	}
3023afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin
3033afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	while (img_len > 0) {
3043afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		if (img_len > DOWNLOAD_CHUCK)
3053afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin			len = DOWNLOAD_CHUCK;
3063afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		else
3073afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin			len = img_len; /* the last chunk of data */
3083afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin
3093afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		memcpy(buf+pad_size, firm->data + pos, len);
310247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		ret = gdm_wibro_send(usbdev, buf, len+pad_size);
3113afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin
312247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if (ret < 0)
313247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			goto out;
314247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
3153afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		img_len -= DOWNLOAD_CHUCK;
3163afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		pos += DOWNLOAD_CHUCK;
3173afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin
318247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
319247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		if (ret < 0)
320247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn			goto out;
321247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	}
322247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
323247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	ret = em_wait_ack(usbdev, 1);
324247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0)
325247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn		goto out;
326247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
327247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnout:
3283afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	release_firmware(firm);
329247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	kfree(buf);
330247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
331247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	return ret;
332247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
333247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
334247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnstatic int em_fw_reset(struct usb_device *usbdev)
335247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
336247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	/*Send ZLP*/
3378a5e7b012d761e85ed8703412e53d1ef997b43ffGengis Dave	return gdm_wibro_send(usbdev, NULL, 0);
338247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
339247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
340247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahnint usb_emergency(struct usb_device *usbdev)
341247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn{
342247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	int ret;
3433afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	const char *kern_name = FW_DIR FW_KERN;
3443afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	const char *fs_name = FW_DIR FW_FS;
345247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
3463afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
347247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0)
3483afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		return ret;
3493800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki	dev_err(&usbdev->dev, "GCT Emergency: Kernel download success.\n");
350247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
3513afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin	ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
352247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	if (ret < 0)
3533afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin		return ret;
3543800178b94d218cbfe5decf60f48742a73b62301YAMANE Toshiaki	dev_info(&usbdev->dev, "GCT Emergency: Filesystem download success.\n");
355247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn
356247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	ret = em_fw_reset(usbdev);
3573afcb91c418a9e50cfb79d7b6e28907d782e69d9Macpaul Lin
358247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn	return ret;
359247e9cffdce024fec5f55f76a8592f2fa8b3aa7bSage Ahn}
360