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