aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2017-07-30 08:09:38 +0100
committerDaniel Thompson <daniel.thompson@linaro.org>2017-07-31 17:26:28 +0100
commit07260f253b9d198d0f099a0adcdd4eb24e17f102 (patch)
tree0819b3ea21018a6f0f670a28e1b3671991aea4d5
parentee1ae874e657d4bfc5bc01297d982ac5260a3a39 (diff)
pdl: pdl-2: RDA support
-rw-r--r--pdl/pdl-2/Makefile49
-rw-r--r--pdl/pdl-2/pdl2_command.h26
-rw-r--r--pdl/pdl-2/pdl_command.c571
-rw-r--r--pdl/pdl-2/pdl_emmc.c340
-rw-r--r--pdl/pdl-2/pdl_emmc.h14
-rw-r--r--pdl/pdl-2/pdl_main.c66
-rw-r--r--pdl/pdl-2/pdl_nand.c685
-rw-r--r--pdl/pdl-2/pdl_nand.h31
8 files changed, 1782 insertions, 0 deletions
diff --git a/pdl/pdl-2/Makefile b/pdl/pdl-2/Makefile
new file mode 100644
index 0000000000..f342eddcc4
--- /dev/null
+++ b/pdl/pdl-2/Makefile
@@ -0,0 +1,49 @@
+#
+# (C) Copyright 2000-2007
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB := $(obj)libpdl_stage.o
+CPPFLAGS += -I ../include
+CFLAGS += -I ../include -mno-unaligned-access
+
+COBJS-y += pdl_command.o pdl_main.o pdl_nand.o
+COBJS-$(CONFIG_GENERIC_MMC) += pdl_emmc.o
+
+COBJS := $(COBJS-y)
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/pdl/pdl-2/pdl2_command.h b/pdl/pdl-2/pdl2_command.h
new file mode 100644
index 0000000000..a89efe42fa
--- /dev/null
+++ b/pdl/pdl-2/pdl2_command.h
@@ -0,0 +1,26 @@
+#ifndef _PDL2_COMMAND_H_
+#define _PDL2_COMMAND_H_
+
+int pdl_command_init(void);
+int sys_connect(struct pdl_packet *packet, void *arg);
+int data_start(struct pdl_packet *packet, void *arg);
+int data_midst(struct pdl_packet *packet, void *arg);
+int data_end(struct pdl_packet *packet, void *arg);
+int data_exec(struct pdl_packet *packet, void *arg);
+int read_partition(struct pdl_packet *packet, void *arg);
+int read_partition_table(struct pdl_packet *packet, void *arg);
+int format_flash(struct pdl_packet *packet, void *arg);
+int read_image_attr(struct pdl_packet *packet, void *arg);
+int erase_partition(struct pdl_packet *packet, void *arg);
+int get_pdl_version(struct pdl_packet *packet, void *arg);
+int reset_machine(struct pdl_packet *packet, void *arg);
+int poweroff_machine(struct pdl_packet *packet, void *arg);
+int set_pdl_dbg(struct pdl_packet *packet, void *arg);
+int check_partition_table(struct pdl_packet *packet, void *arg);
+int recv_image_list(struct pdl_packet *packet, void *arg);
+int get_pdl_security(struct pdl_packet *packet, void *arg);
+int hw_test(struct pdl_packet *packet, void *arg);
+int get_pdl_log(struct pdl_packet *packet, void *arg);
+int download_finish(struct pdl_packet *packet, void *arg);
+
+#endif
diff --git a/pdl/pdl-2/pdl_command.c b/pdl/pdl-2/pdl_command.c
new file mode 100644
index 0000000000..a6c06c17b0
--- /dev/null
+++ b/pdl/pdl-2/pdl_command.c
@@ -0,0 +1,571 @@
+#include <common.h>
+#include <version.h>
+#include <asm/types.h>
+#include "packet.h"
+#include <linux/string.h>
+#include <malloc.h>
+#include <asm/arch/mtdparts_def.h>
+#include <asm/arch/rda_sys.h>
+#include <asm/arch/rda_crypto.h>
+#include <asm/arch/hw_test.h>
+#include <asm/arch/prdinfo.h>
+#include "cmd_defs.h"
+#include "pdl_debug.h"
+#include "pdl.h"
+#include "pdl_command.h"
+#include "pdl_nand.h"
+#include "pdl_emmc.h"
+#include <linux/mtd/mtd.h>
+
+#define MAX_PART_NAME 30
+#define MAX_TEST_LIST_SIZE 60
+
+#ifdef CONFIG_PDL_FORCE_HW_TEST_FULL
+static int hw_tested = 0;
+static int hw_test_run(const char *test_list, int fast_mode);
+#endif
+
+uint8_t pdl_extended_status = 0;
+
+static int media_type = MEDIA_NAND;
+int sys_connect(struct pdl_packet *packet, void *arg)
+{
+ pdl_info("connect with pc\n");
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+struct exec_file_info {
+ unsigned long start_address;
+ unsigned long total_size;
+ unsigned long recv_size;
+ unsigned long next_address;
+};
+static struct exec_file_info exec_file;
+static int frame_count = -1;
+
+int data_start(struct pdl_packet *packet, void *arg)
+{
+ unsigned long start_addr = packet->cmd_header.data_addr;
+ unsigned long file_size = packet->cmd_header.data_size;
+ int ret;
+ char part_name[MAX_PART_NAME + 1] = {'\0'};
+
+ strncpy(part_name, (char *)packet->data, MAX_PART_NAME);
+ frame_count = -1;
+
+#ifdef CONFIG_PDL_FORCE_HW_TEST_FULL
+ if(!hw_tested &&
+ strstr(part_name, "system")) {
+ ret = hw_test_run("all", 0);
+ if(ret) {
+ pdl_send_rsp(ret);
+ return 0;
+ }
+ hw_tested = 1;
+ }
+#endif
+
+ if (strlen(part_name) == 0) {
+ pdl_info("invalid partition name\n");
+ pdl_send_rsp(INVALID_PARTITION);
+ return -1;
+ }
+
+
+ /* the image is pdl1.bin or pdl2.bin */
+ memset(&exec_file, 0, sizeof(exec_file));
+ if(strncmp(part_name, "pdl", 3) == 0 ||
+ strncmp(part_name, "PDL", 3) == 0) {
+ exec_file.start_address = start_addr;
+ exec_file.total_size = file_size;
+ exec_file.recv_size = 0;
+ exec_file.next_address = start_addr;
+
+ pdl_send_rsp(ACK);
+ return 0;
+ }
+
+ if (media_type == MEDIA_MMC)
+ ret = emmc_data_start(part_name, file_size);
+ else
+ ret = nand_data_start(part_name, file_size);
+ if (ret) {
+ pdl_send_rsp(ret);
+ return -1;
+ }
+
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int data_midst(struct pdl_packet *packet, void *arg)
+{
+ u32 size = packet->cmd_header.data_size;
+ int ret;
+ u32 frame_num = packet->cmd_header.data_addr;
+
+ /* pdl1.bin or pdl2.bin */
+ if(exec_file.total_size > 0) {
+ if ((exec_file.recv_size + size) > exec_file.total_size) {
+ pdl_send_rsp(INVALID_SIZE);
+ return 0;
+ }
+
+ memcpy((void *)exec_file.next_address, packet->data, size);
+ exec_file.next_address += size;
+ exec_file.recv_size += size;
+
+ pdl_dbg("write to addr %lx, receive size %ld\n", exec_file.next_address,
+ exec_file.recv_size);
+
+ pdl_send_rsp(ACK);
+ return 0;
+ }
+
+ /* check frame number */
+ if(frame_count >= 0) {
+ if(frame_num < frame_count) { /* ignore this frame */
+ pdl_send_rsp(ACK);
+ return 0;
+ }
+ if(frame_num > frame_count) {
+ pdl_send_rsp(PACKET_ERROR);
+ pdl_error("expect frame %d, not %d\n", frame_count, frame_num);
+ return -1;
+ }
+ } else {
+ frame_count = frame_num;
+ }
+
+ pdl_dbg("frame count %u\n", frame_count);
+ frame_count++;
+ if (media_type == MEDIA_MMC)
+ ret = emmc_data_midst(packet->data, size);
+ else
+ ret = nand_data_midst(packet->data, size);
+ if (ret) {
+ pdl_send_rsp(ret);
+ return -1;
+ }
+
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int data_end(struct pdl_packet *packet, void *arg)
+{
+ int ret;
+ uint32_t data_size = packet->cmd_header.data_size;
+ uint32_t data_crc = 0;
+
+ /* pdl1.bin or pdl2.bin */
+ if(exec_file.total_size > 0) {
+ pdl_info("receive pdl1/pdl2 finish\n");
+ pdl_send_rsp(ACK);
+ return 0;
+ }
+
+ frame_count = -1;
+ if(data_size == 4)
+ memcpy((void *)&data_crc, (void *)packet->data, 4);
+
+ if (media_type == MEDIA_MMC)
+ ret = emmc_data_end(data_crc);
+ else
+ ret = nand_data_end(data_crc);
+ if (ret) {
+ pdl_send_rsp(ret);
+ return -1;
+ }
+
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+static unsigned long
+do_go_exec (ulong (*entry)(int, char * const []), int argc, char * const argv[])
+{
+ return entry (argc, argv);
+}
+
+int data_exec(struct pdl_packet *packet, void *arg)
+{
+#ifdef CONFIG_SIGNATURE_CHECK_IMAGE
+ pdl_info("Verify executable 0x%lx, #%ld\n",
+ exec_file.start_address,exec_file.recv_size);
+ if (image_sign_verify((const void *)exec_file.start_address,
+ exec_file.recv_size)) {
+ printf("Verify failed, aborting execute.\n");
+ pdl_send_rsp(VERIFY_ERROR);
+ return 0;
+ }
+#endif
+ pdl_info("Execute download from 0x%lx\n", exec_file.start_address);
+ do_go_exec((void *)exec_file.start_address, 1, NULL);
+ return 0;
+}
+
+int read_partition(struct pdl_packet *packet, void *arg)
+{
+ size_t size = packet->cmd_header.data_size;
+ char part_name[MAX_PART_NAME + 1] = {'\0'};
+ int ret;
+ size_t actual_len = 0;
+ unsigned char *data;
+
+ strncpy(part_name, (char *)packet->data, MAX_PART_NAME);
+ if (strlen(part_name) == 0) {
+ pdl_info("invalid partition name\n");
+ pdl_send_rsp(INVALID_PARTITION);
+ return -1;
+ }
+
+ pdl_info("%s from %s\n", __func__, part_name);
+
+ data = malloc(size);
+ if (!data) {
+ pdl_info("no memory!\n");
+ pdl_send_rsp(NO_MEMORY);
+ return -1;
+ }
+
+ if (media_type == MEDIA_MMC)
+ ret = emmc_read_partition(part_name, data, size,
+ &actual_len);
+ else
+ ret = nand_read_partition(part_name, data, size,
+ &actual_len);
+ if (ret) {
+ pdl_send_rsp(ret);
+ ret = -1;
+ goto out;
+ }
+
+ pdl_send_pkt(data, actual_len);
+ pdl_dbg("send ok\n");
+out:
+ free(data);
+ return ret;
+}
+
+extern int mtdparts_ptbl_check(int *same);
+extern int mmc_check_parts(int *same);
+int check_partition_table(struct pdl_packet *packet, void *arg)
+{
+ int ret;
+ int same = 0;
+ unsigned char data;
+
+ if (media_type == MEDIA_MMC)
+ ret = mmc_check_parts(&same);
+ else
+ ret = mtdparts_ptbl_check(&same);
+
+ if(ret) {
+ pdl_send_rsp(DEVICE_ERROR);
+ return -1;
+ }
+
+ data = (u8)(same & 0xff);
+ pdl_send_pkt(&data, 1);
+ return 0;
+}
+
+int read_partition_table(struct pdl_packet *packet, void *arg)
+{
+ u32 size;
+ const char *parts = getenv("mtdparts");
+
+ pdl_info("%s, parts %s\n", __func__, parts);
+
+ size = strlen(parts);
+ pdl_send_pkt((const u8*)parts, size);
+ return 0;
+}
+extern struct mtd_device *current_mtd_dev;
+
+int format_flash(struct pdl_packet *packet, void *arg)
+{
+ int ret;
+
+ pdl_info("format the whole flash part\n");
+
+ if (media_type == MEDIA_MMC)
+ ret = emmc_format_flash();
+ else
+ ret = nand_format_flash();
+ if (ret) {
+ pdl_send_rsp(ret);
+ return -1;
+ }
+
+#ifdef CONFIG_PDL_FORCE_HW_TEST_FULL
+ if(!hw_tested) {
+ ret = hw_test_run("all", 0);
+ if(ret) {
+ pdl_send_rsp(ret);
+ return 0;
+ }
+ hw_tested = 1;
+ }
+#endif
+
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int read_image_attr(struct pdl_packet *packet, void *arg)
+{
+ const char attrs[] = IMAGE_ATTR;
+
+ pdl_info("fs image attrs: %s\n", attrs);
+
+ pdl_send_pkt((const u8*)attrs, sizeof(attrs));
+ return 0;
+}
+
+int erase_partition(struct pdl_packet *packet, void *arg)
+{
+ int ret;
+ char part_name[MAX_PART_NAME + 1] = {'\0'};
+
+ strncpy(part_name, (char *)packet->data, MAX_PART_NAME);
+
+ if (strlen(part_name) == 0) {
+ pdl_info("invalid partition name\n");
+ pdl_send_rsp(INVALID_PARTITION);
+ return -1;
+ }
+
+ if (media_type == MEDIA_MMC)
+ ret = emmc_erase_partition(part_name);
+ else
+ ret = nand_erase_partition(part_name);
+ if (ret) {
+ pdl_send_rsp(ret);
+ return -1;
+ }
+
+ /* if the part is 'userdata', erase 'fat' part also */
+ if (strcmp(part_name, "userdata") == 0) {
+ strcpy(part_name, "fat");
+ if (media_type == MEDIA_MMC)
+ emmc_erase_partition(part_name);
+ else
+ nand_erase_partition(part_name);
+ }
+
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int get_pdl_version(struct pdl_packet *packet, void *arg)
+{
+ u32 flag = packet->cmd_header.data_addr;
+ u32 size;
+#define MAX_VER_LEN 100
+ char str[MAX_VER_LEN+1];
+
+ memset(str, 0, MAX_VER_LEN + 1);
+ strncpy(str, version_string, MAX_VER_LEN);
+
+#ifdef BUILD_DISPLAY_ID
+ /* return BUILD_DISPLAY_ID instead of pdl version */
+ if(flag == 1)
+ strncpy(str, BUILD_DISPLAY_ID, MAX_VER_LEN);
+#endif
+
+ pdl_info("send version string: [%s]\n", str);
+
+ size = strlen(str) + 1;
+ pdl_send_pkt((const u8*)str, size);
+ return 0;
+}
+
+int reset_machine(struct pdl_packet *packet, void *arg)
+{
+ pdl_info("reset machine....\n");
+
+ u8 reboot_mode = REBOOT_TO_NORMAL_MODE;
+ if (packet->cmd_header.data_size == 1) {
+ reboot_mode = packet->data[0];
+ }
+ switch (reboot_mode) {
+ case REBOOT_TO_NORMAL_MODE:
+ case REBOOT_TO_DOWNLOAD_MODE:
+ case REBOOT_TO_FASTBOOT_MODE:
+ case REBOOT_TO_RECOVERY_MODE:
+ case REBOOT_TO_CALIB_MODE:
+ break;
+ default:
+ reboot_mode = REBOOT_TO_NORMAL_MODE;
+ }
+
+ pdl_info("reboot_mode: %d\n", reboot_mode);
+ pdl_send_rsp(ACK); // cheat and send ack before reset
+
+ rda_reboot(reboot_mode);
+
+ /* should never here */
+ return 0;
+}
+
+int poweroff_machine(struct pdl_packet *packet, void *arg)
+{
+ pdl_info("waiting USB cable plug out...\n");
+ while(usb_cable_connected()){
+ mdelay(20);
+ }
+ pdl_info("machine power off...\n");
+ shutdown_system();
+ return 0;
+}
+
+int recv_image_list(struct pdl_packet *packet, void *arg)
+{
+ pdl_info("receive download image list...\n");
+ pdl_info("\t%s\n", (char *)packet->data);
+ prdinfo_init((char *)packet->data);
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+static int hw_test_run(const char *test_list, int fast_mode)
+{
+ int __attribute__((unused)) test_all = 0;
+
+ if(!test_list)
+ return HW_TEST_ERROR;
+
+ if(strcmp(test_list, "all") == 0)
+ test_all = 1;
+
+ pdl_info("start %s hardware test...[%s]\n", fast_mode ? "fast" : "full", test_list);
+
+#ifdef CONFIG_VPU_STA_TEST
+ if(test_all || strstr(test_list, "vpu")) {
+ vpu_sta_test(fast_mode ? 10 : 200);
+ pdl_info("VPU stable test..pass!\n");
+ }
+#endif
+
+#ifdef CONFIG_VPU_MD5_TEST
+ if(test_all || strstr(test_list, "vpu_md5")) {
+ int ret = vpu_md5_test(fast_mode ? 10 : 200);
+ if(ret)
+ return MD5_ERROR;
+ pdl_info("VPU MD5 test..pass!\n");
+ }
+#endif
+
+#ifdef CONFIG_CPU_TEST
+ if(test_all || strstr(test_list, "cpu")) {
+ cpu_pll_test(fast_mode ? 2000 : 4000);
+ pdl_info("CPU test..pass!\n");
+ }
+#endif
+
+ pdl_info("end %s hardware test.\n", fast_mode ? "fast" : "full");
+ return ACK;
+}
+
+int hw_test(struct pdl_packet *packet, void *arg)
+{
+ unsigned long param_size = packet->cmd_header.data_size;
+ char test_list[MAX_TEST_LIST_SIZE + 1] = {'\0'};
+ int ret = 0;
+
+ if(param_size > 0)
+ strncpy(test_list, (char *)packet->data, MAX_TEST_LIST_SIZE);
+ else
+ strcpy(test_list, "all");
+
+ ret = hw_test_run(test_list, 0);
+ pdl_send_rsp(ret);
+ return 0;
+}
+
+int get_pdl_log(struct pdl_packet *packet, void *arg)
+{
+ u32 size;
+
+ pdl_info("get pdl log\n");
+
+ size = strlen(pdl_log_buff) + 1;
+ pdl_send_pkt((const u8*)pdl_log_buff, size);
+ return 0;
+}
+
+int download_finish(struct pdl_packet *packet, void *arg)
+{
+ u32 ftm_tag = packet->cmd_header.data_addr;
+ pdl_info("download finish!\n");
+
+ prdinfo_set_pdl_result(1, ftm_tag);
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int set_pdl_dbg(struct pdl_packet *packet, void *arg)
+{
+ u32 dbg_settings = packet->cmd_header.data_addr;
+
+ pdl_info("%s, dbg_settings :%x\n", __func__, dbg_settings);
+
+ if (dbg_settings & PDL_DBG_PDL) {
+ pdl_info("open pdl debug\n");
+ pdl_dbg_pdl = 1;
+ }
+
+ if (dbg_settings & PDL_DBG_PDL_VERBOSE) {
+ pdl_info("open pdl verbose debug\n");
+ pdl_dbg_pdl = 1;
+ pdl_vdbg_pdl = 1;
+ }
+
+ if (dbg_settings & PDL_DBG_USB_EP0) {
+ pdl_info("open pdl usb ep0 debug\n");
+ pdl_dbg_usb_ep0 = 1;
+ }
+
+ if (dbg_settings & PDL_DBG_RW_CHECK) {
+ pdl_info("open pdl flash write/read/compare debug\n");
+ pdl_dbg_rw_check = 1;
+ }
+
+ if (dbg_settings & PDL_DBG_USB_SERIAL) {
+ pdl_info("open pdl usb serial debug\n");
+ pdl_dbg_usb_serial = 1;
+ }
+
+ if (dbg_settings & PDL_DBG_FACTORY_PART) {
+ pdl_info("open pdl factory part debug\n");
+ pdl_dbg_factory_part = 1;
+ }
+ if (dbg_settings & PDL_EXTENDED_STATUS) {
+ pdl_info("Enable extended status\n");
+ pdl_extended_status = 1;
+ }
+
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int pdl_command_init(void)
+{
+ int ret;
+
+ media_type = rda_media_get();
+ if (media_type == MEDIA_MMC) {
+ ret = pdl_emmc_init();
+ } else {
+ ret = pdl_nand_init();
+ }
+
+#ifdef CONFIG_PDL_FORCE_HW_TEST
+ ret = hw_test_run("all", 1);
+#endif
+
+ return ret;
+}
diff --git a/pdl/pdl-2/pdl_emmc.c b/pdl/pdl-2/pdl_emmc.c
new file mode 100644
index 0000000000..d07be8c07d
--- /dev/null
+++ b/pdl/pdl-2/pdl_emmc.c
@@ -0,0 +1,340 @@
+#include <common.h>
+#include <asm/types.h>
+#include "packet.h"
+#include <linux/string.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <part.h>
+#include <mmc/sparse.h>
+#include <mmc/mmcpart.h>
+#include <asm/arch/rda_sys.h>
+#include <asm/arch/factory.h>
+#include <asm/arch/prdinfo.h>
+#include "cmd_defs.h"
+#include "pdl_debug.h"
+#include "pdl.h"
+#include "pdl_command.h"
+
+#define MAX_PART_NAME 30
+struct dl_file_info {
+ disk_partition_t *ptn;
+ uchar *start_addr;
+ u64 total_size;
+ u64 recv_size;
+ char part_name[MAX_PART_NAME];
+};
+
+static struct dl_file_info dl_file;
+
+static uchar *download_buf;
+static unsigned long download_max_size;
+static block_dev_desc_t *mmc_blkdev = NULL;
+
+int emmc_data_start(const char *part_name, unsigned long file_size)
+{
+ disk_partition_t *ptn = 0;
+ int blksz_shift;
+
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ /* init/update partition table */
+ mmc_parts_format();
+ /* read again */
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ int i;
+ pdl_info("invalid mtd part:%s ",part_name);
+ for (i = 0; i < MAX_PART_NAME; i++)
+ pdl_info(" 0x%x ", part_name[i]);
+ pdl_info("\n");
+ return INVALID_PARTITION;
+ }
+ }
+
+ blksz_shift = LOG2(ptn->blksz);
+ pdl_info("found part '%s' start: 0x%lx length: 0x%lx bytes\n",
+ ptn->name, ptn->start, (ptn->size << blksz_shift));
+
+ memset(&dl_file, 0, sizeof(dl_file));
+ strncpy(dl_file.part_name, part_name, MAX_PART_NAME);
+
+ if (file_size > (ptn->size << blksz_shift)) {
+ pdl_info("%s, download file too large, the size is:%ld\n",
+ __func__, file_size);
+ pdl_info("but the mmc part %s 's size is %ld\n",
+ ptn->name, ptn->size << blksz_shift);
+ return INVALID_SIZE;
+ }
+
+ dl_file.ptn = ptn;
+ dl_file.total_size = file_size;
+ dl_file.start_addr = download_buf;
+ dl_file.recv_size = 0;
+
+ return 0;
+}
+
+int emmc_data_midst(u8 *data, size_t size)
+{
+ if (!size) {
+ pdl_error("oops, send the zero length packet\n");
+ return INVALID_SIZE;
+ }
+ if (((dl_file.recv_size + size) > dl_file.total_size) ||
+ (dl_file.recv_size + size) > download_max_size) {
+ pdl_error("transfer size error receive (%lld), file (%lld)\n",
+ dl_file.recv_size + size, dl_file.total_size);
+ return INVALID_SIZE;
+ }
+
+ pdl_dbg("writing 0x%x bytes to offset: 0x%p\n",
+ size, dl_file.start_addr);
+
+ memcpy(dl_file.start_addr, data, size);
+
+ dl_file.start_addr += size;
+ dl_file.recv_size += size;
+
+ return 0;
+}
+
+int emmc_data_end(uint32_t crc)
+{
+ sparse_header_t *sparse_header = (sparse_header_t *)download_buf;
+ disk_partition_t *ptn = dl_file.ptn;
+ loff_t write_bytes = dl_file.recv_size;
+ int ret;
+
+ if (!strcmp(dl_file.part_name, "factorydata")) {
+ pdl_info("flash to mmc part %s\n", ptn->name);
+ ret = factory_update_all(download_buf, dl_file.recv_size);
+ if (ret) {
+ pdl_error("oops, flash to mmc error %d\n", ret);
+ return OPERATION_FAILED;
+ }
+ return 0;
+ }
+
+ if (!strcmp(dl_file.part_name, PRDINFO_PART_NAME)) {
+ pdl_info("flash to mmc part %s\n", ptn->name);
+ ret = prdinfo_update_all((char *)download_buf, dl_file.recv_size);
+ if (ret) {
+ pdl_error("oops, flash to mmc error %d\n", ret);
+ return OPERATION_FAILED;
+ }
+ return 0;
+ }
+
+ /* verify data crc before write to mmc */
+ if(crc) {
+ uint32_t new_crc = crc32(0, (unsigned char *)download_buf, write_bytes);
+ if(crc != new_crc) {
+ pdl_info("CRC verify failed, expect %#x, got %#x.\n", crc, new_crc);
+ return CHECKSUM_ERROR;
+ } else {
+ pdl_info("CRC verify success, %#x.\n", crc);
+ }
+ }
+
+ /* Using to judge if ext4 file system */
+ if (sparse_header->magic != SPARSE_HEADER_MAGIC) {
+ pdl_info("flash to mmc part %s\n", ptn->name);
+ ret = partition_write_bytes(mmc_blkdev, ptn,
+ &write_bytes, download_buf);
+ } else {
+ pdl_info("unsparese flash part %s start %lx, size %ld blks\n",
+ ptn->name, ptn->start, ptn->size);
+ ret = partition_unsparse(mmc_blkdev, ptn, download_buf,
+ ptn->start, ptn->size);
+ }
+ if (ret) {
+ pdl_error("oops, flash to mmc error %d\n", ret);
+ return OPERATION_FAILED;
+ }
+
+ prdinfo_set_pdl_image_download_result((char *)ptn->name, 1);
+ pdl_info("END: total flashed %lld to mmc\n", write_bytes);
+ return 0;
+}
+
+
+int emmc_read_partition(const char *part_name, unsigned char *data, size_t size,
+ size_t *actual_len)
+{
+ disk_partition_t *ptn;
+ loff_t read_bytes = size;
+ int ret;
+
+ if(!mmc_blkdev) {
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!mmc_blkdev)
+ return DEVICE_ERROR;
+ }
+
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ /* init/update partition table */
+ mmc_parts_format();
+ /* read again */
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ pdl_error("partition table doesn't exist");
+ return INVALID_PARTITION;
+ }
+ }
+
+ pdl_dbg("%s from %s\n", __func__, part_name);
+
+ if (!strcmp(part_name, "factorydata")) {
+ size = factory_get_all(data);
+ if (size == 0) {
+ pdl_error("read from emmc error\n");
+ return DEVICE_ERROR;
+ }
+ } else {
+ ret = partition_read_bytes(mmc_blkdev, ptn, &read_bytes, data);
+ if(ret < 0) {
+ pdl_error("read from emmc error\n");
+ return DEVICE_ERROR;
+ }
+ }
+ if(actual_len)
+ *actual_len = size;
+
+ pdl_dbg("read actrual %d ok \n", *actual_len);
+ return 0;
+}
+
+int emmc_write_partition(const char *part_name, unsigned char *data, size_t size)
+{
+ disk_partition_t *ptn;
+ loff_t write_bytes = size;
+ int ret;
+
+ if(!mmc_blkdev) {
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!mmc_blkdev)
+ return DEVICE_ERROR;
+ }
+
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ /* init/update partition table */
+ mmc_parts_format();
+ /* read again */
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ pdl_error("partition table doesn't exist");
+ return INVALID_PARTITION;
+ }
+ }
+
+ pdl_dbg("%s write to %s\n", __func__, part_name);
+
+ if (!strcmp(part_name, "factorydata")) {
+ return factory_update_all(data, size);
+ } else {
+ ret = partition_write_bytes(mmc_blkdev, ptn, &write_bytes, data);
+ if(ret < 0) {
+ pdl_error("write to emmc error\n");
+ return DEVICE_ERROR;
+ }
+ }
+
+ pdl_dbg("write actrual %u ok \n", size);
+ return 0;
+}
+
+extern struct mtd_device *current_mtd_dev;
+
+int emmc_format_flash(void)
+{
+ int ret;
+ lbaint_t start = 0;
+ lbaint_t count = 0;
+ disk_partition_t *ptn_old, *ptn_new;
+
+ if (!mmc_blkdev) {
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!mmc_blkdev)
+ return DEVICE_ERROR;
+ }
+ count = mmc_blkdev->lba;
+
+ ptn_old = partition_find_ptn("factorydata");
+ factory_load();
+
+ pdl_info("refresh partition table\n");
+ ret = mmc_parts_format();
+ if (ret) {
+ pdl_error("refresh partition table fail %d\n", ret);
+ return DEVICE_ERROR;
+ }
+
+ /* if 'factorydata' part is not changed, don't
+ erase '0' offset to the end offset of 'factorydata' */
+ ptn_new = partition_find_ptn("factorydata");
+ if (ptn_old && ptn_new) {
+ if(ptn_old->start == ptn_new->start &&
+ ptn_old->size == ptn_new->size)
+ start = ptn_new->start + ptn_new->size;
+ }
+
+ count -= start;
+ pdl_info("format the whole mmc, from %ld, %ld blocks\n", start, count);
+ ret = mmc_blkdev->block_erase(CONFIG_MMC_DEV_NUM, start, count);
+ if (ret != count) {
+ pdl_error("mmc format partition fail %d\n", ret);
+ return DEVICE_ERROR;
+ }
+
+ /* if 'factorydata' was erased, write back */
+ if (!start)
+ factory_burn();
+
+ return 0;
+}
+
+int emmc_erase_partition(const char *part_name)
+{
+ disk_partition_t *ptn;
+ int ret;
+
+ if(!mmc_blkdev) {
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!mmc_blkdev)
+ return DEVICE_ERROR;
+ }
+
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ /* init/update partition table */
+ mmc_parts_format();
+ /* read again */
+ ptn = partition_find_ptn(part_name);
+ if(!ptn) {
+ pdl_error("partition table doesn't exist");
+ return INVALID_PARTITION;
+ }
+ }
+
+ pdl_info("erase the mmc part %s\n", part_name);
+ ret = partition_erase_blks(mmc_blkdev, ptn, &ptn->size);
+ if (ret) {
+ pdl_error("erase parttion fail\n");
+ return DEVICE_ERROR;
+ }
+ return 0;
+}
+
+int pdl_emmc_init(void)
+{
+ download_buf = (unsigned char *)SCRATCH_ADDR;
+ download_max_size = FB_DOWNLOAD_BUF_SIZE;
+ mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME);
+ if (!mmc_blkdev)
+ return DEVICE_ERROR;
+ pdl_dbg("download buffer %p, max size %ld\n", download_buf, download_max_size);
+ return 0;
+}
+
diff --git a/pdl/pdl-2/pdl_emmc.h b/pdl/pdl-2/pdl_emmc.h
new file mode 100644
index 0000000000..de70bdb45c
--- /dev/null
+++ b/pdl/pdl-2/pdl_emmc.h
@@ -0,0 +1,14 @@
+#ifndef __PDL_EMMC_H_
+#define __PDL_EMMC_H_
+
+int emmc_data_start(const char *part_name, unsigned long file_size);
+int emmc_data_midst(u8 *data, size_t size);
+int emmc_data_end(uint32_t crc);
+int emmc_read_partition(const char *part_name, unsigned char *data, size_t size,
+ size_t *actual_len);
+int emmc_write_partition(const char *part_name, unsigned char *data, size_t size);
+int emmc_format_flash(void);
+int emmc_erase_partition(const char *part_name);
+int pdl_emmc_init(void);
+#endif
+
diff --git a/pdl/pdl-2/pdl_main.c b/pdl/pdl-2/pdl_main.c
new file mode 100644
index 0000000000..4bbe2b072f
--- /dev/null
+++ b/pdl/pdl-2/pdl_main.c
@@ -0,0 +1,66 @@
+#include <asm/types.h>
+#include <common.h>
+#include <command.h>
+#include <asm/arch/rda_sys.h>
+#include <malloc.h>
+#include "pdl_command.h"
+#include "packet.h"
+#include "cmd_defs.h"
+#include "pdl_engine.h"
+#include "pdl_debug.h"
+#include "pdl_channel.h"
+#include <usb/usbserial.h>
+
+int pdl_main(void)
+{
+ int ret = 0;
+ pdl_info("start pdl2...\n");
+
+ drv_usbser_init();
+ pdl_usb_channel_register();
+ pdl_init_packet_channel();
+ ret = pdl_command_init();
+ if(ret) {
+ pdl_info("pdl init failed, power down..\n");
+ pdl_send_rsp(ret);
+ shutdown_system();
+ }
+
+ /* hack serial_puts(), to save all prints to log buffer */
+ pdl_init_serial();
+
+ pdl_cmd_register(CONNECT, sys_connect);
+ pdl_cmd_register(START_DATA, data_start);
+ pdl_cmd_register(MID_DATA, data_midst);
+ pdl_cmd_register(END_DATA, data_end);
+ pdl_cmd_register(EXEC_DATA, data_exec);
+ pdl_cmd_register(READ_PARTITION, read_partition);
+ pdl_cmd_register(READ_PARTITION_TABLE, read_partition_table);
+ pdl_cmd_register(FORMAT_FLASH, format_flash);
+ pdl_cmd_register(READ_IMAGE_ATTR, read_image_attr);
+ pdl_cmd_register(ERASE_PARTITION, erase_partition);
+ pdl_cmd_register(NORMAL_RESET, reset_machine);
+ pdl_cmd_register(POWER_OFF, poweroff_machine);
+ pdl_cmd_register(GET_VERSION, get_pdl_version);
+ pdl_cmd_register(SET_PDL_DBG, set_pdl_dbg);
+ pdl_cmd_register(CHECK_PARTITION_TABLE, check_partition_table);
+ pdl_cmd_register(IMAGE_LIST, recv_image_list);
+ pdl_cmd_register(HW_TEST, hw_test);
+ pdl_cmd_register(GET_PDL_LOG, get_pdl_log);
+ pdl_cmd_register(DOWNLOAD_FINISH, download_finish);
+
+ pdl_send_rsp(ACK);
+ pdl_handler(NULL);
+ return 0;
+}
+
+#if defined(CONFIG_CMD_PDL2)
+int do_pdl2(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ pdl_main();
+ return 0;
+}
+
+U_BOOT_CMD(pdl2, 1, 1, do_pdl2,
+ "android fastboot protocol", "flash image to nand");
+#endif
diff --git a/pdl/pdl-2/pdl_nand.c b/pdl/pdl-2/pdl_nand.c
new file mode 100644
index 0000000000..db364e9e0f
--- /dev/null
+++ b/pdl/pdl-2/pdl_nand.c
@@ -0,0 +1,685 @@
+#include <common.h>
+#include <asm/types.h>
+#include "packet.h"
+#include <linux/string.h>
+#include <jffs2/load_kernel.h>
+#include <nand.h>
+#include <malloc.h>
+#include <asm/arch/spl_board_info.h>
+#include <asm/arch/mtdparts_def.h>
+#include <asm/arch/rda_sys.h>
+#include <asm/arch/factory.h>
+#include <asm/arch/prdinfo.h>
+#include "cmd_defs.h"
+#include "pdl_debug.h"
+#include "pdl.h"
+#include "pdl_command.h"
+#include "pdl_nand.h"
+#include <linux/mtd/nand.h>
+#include <mtd/nand/rda_nand.h>
+
+#define MAX_PART_NAME 30
+
+struct dl_file_info {
+ struct mtd_info *nand;
+
+ struct part_info *part;
+ unsigned long start_offset; /* partition start offset on flash */
+ unsigned long end_offset; /* partition end offset on flash */
+ unsigned long write_offset; /* write offset on flash */
+
+ unsigned char *download_bufp; /* download buffer pointer */
+ unsigned long download_size; /* data size in download buffer */
+
+ unsigned long total_size; /* image file size */
+ unsigned long recv_size; /* received data size */
+
+ char part_name[MAX_PART_NAME];
+ char vol_name[MAX_PART_NAME];
+};
+
+static struct dl_file_info dl_file;
+static unsigned char *download_buf;
+static unsigned long download_max_size = (24 * 1024 *1024);
+
+extern int mtdparts_init_default(void);
+extern int mtdparts_init_from_ptbl(void);
+extern int mtdparts_save_ptbl(int need_erase);
+
+static int __erase_partition(nand_info_t *nand,
+ struct part_info *part)
+{
+ nand_erase_options_t opts;
+ int ret;
+
+ pdl_info("%s: erase part '%s', offset 0x%llx size 0x%llx\n", __func__,
+ part->name, part->offset, part->size);
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = (loff_t) part->offset;
+ opts.length = (loff_t) part->size;
+ opts.jffs2 = 0;
+ opts.quiet = 0;
+
+ pdl_dbg("opts off 0x%08x\n", (uint32_t) opts.offset);
+ pdl_dbg("opts size 0x%08x\n", (uint32_t) opts.length);
+ ret = nand_erase_opts(nand, &opts);
+ if (!ret) {
+ part->dirty = 0;
+ }
+ return ret;
+}
+
+static int pdl_ubi_start_write(void)
+{
+ int ret = 0;
+
+ /* prepart to write ubi volume image, system/vendor partition */
+#ifdef MTDPARTS_UBI_DEF
+ pdl_info("init ubi part '%s'\n", dl_file.part_name);
+ ret = ubi_part_scan(dl_file.part_name);
+ if(ret) {
+ pdl_info("ubi init failed.\n");
+ return OPERATION_FAILED;
+ }
+
+ ret = ubi_check_default_vols(MTDPARTS_UBI_DEF);
+ if(ret) {
+ pdl_info("ubi volumes check failed.\n");
+ return OPERATION_FAILED;
+ }
+
+ ret = ubi_prepare_write_vol(dl_file.vol_name, dl_file.total_size);
+#endif
+
+ return ret;
+}
+
+typedef enum {
+ PART_RAW = 0,
+ PART_FACTORYDATA,
+ PART_PRDINFO,
+ PART_UBI_VOL,
+} PART_TYPE;
+static int update_part_type = PART_RAW;
+int nand_data_start(const char *part_name, unsigned long file_size)
+{
+ struct part_info *part;
+ struct mtd_device *dev;
+ u8 part_num;
+ int ret;
+
+ update_part_type = PART_RAW;
+ memset(&dl_file, 0, sizeof(dl_file));
+ strncpy(dl_file.part_name, part_name, MAX_PART_NAME-1);
+ strncpy(dl_file.vol_name, part_name, MAX_PART_NAME-1);
+
+#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */
+ if(strstr(MTDPARTS_UBI_DEF, part_name)) {
+ strncpy(dl_file.part_name, MTDPARTS_UBI_PART_NAME, MAX_PART_NAME-1);
+ strncpy(dl_file.vol_name, part_name, MAX_PART_NAME-1);
+ pdl_info("'%s' is a ubi volume in '%s' part.\n",
+ dl_file.vol_name, dl_file.part_name);
+ update_part_type = PART_UBI_VOL;
+ }
+#endif
+
+ ret = find_dev_and_part(dl_file.part_name, &dev, &part_num, &part);
+ if (ret) {
+ int i;
+ pdl_info("invalid mtd part:%s ", dl_file.part_name);
+ for (i = 0; i < MAX_PART_NAME; i++)
+ pdl_info(" 0x%x ", dl_file.part_name[i]);
+ pdl_info("\n");
+ return (INVALID_PARTITION);
+ }
+
+ pdl_info("found part '%s' offset: 0x%llx length: 0x%llx id: %d is %s\n",
+ part->name, part->offset, part->size, dev->id->num,
+ part->dirty ? "dirty" : "clean");
+ dl_file.part = part;
+ dl_file.end_offset = part->offset + part->size;
+ dl_file.nand = &nand_info[dev->id->num];
+
+ if (file_size > part->size) {
+ pdl_info("%s, download file too large, the size is:%ld\n",
+ __func__, file_size);
+ pdl_info("but the mtd part %s 's size is %llu\n",
+ part->name, part->size);
+ return INVALID_SIZE;
+ }
+
+ if (!strcmp(part->name, "factorydata")) {
+ pdl_dbg("update factorydata\n");
+ update_part_type = PART_FACTORYDATA;
+ } else if (!strcmp(part->name, PRDINFO_PART_NAME)) {
+ pdl_dbg("update prdinfo\n");
+ update_part_type = PART_PRDINFO;
+ }
+ //erase the mtd part;
+ if (part->dirty
+ && (update_part_type == PART_RAW)
+ ) {
+ ret = __erase_partition(dl_file.nand, part);
+ if (ret) {
+ pdl_info("erase mtd partitions error\n");
+ return DEVICE_ERROR;
+ }
+ }
+ /*now we'll write data to the partition, so dirty the part */
+ part->dirty = 1;
+
+ dl_file.total_size = file_size;
+ dl_file.start_offset = part->offset;
+ dl_file.write_offset = part->offset;
+ dl_file.download_bufp = download_buf;
+ dl_file.recv_size = 0;
+ dl_file.download_size = 0;
+
+ if((update_part_type == PART_UBI_VOL) && pdl_ubi_start_write()) {
+ pdl_error("can't write ubi volume %s\n", dl_file.vol_name);
+ return DEVICE_ERROR;
+ }
+
+ return 0;
+}
+
+#ifdef _TGT_AP_DDR_AUTO_CALI_ENABLE
+static void add_ddr_cal_val_to_bootloader(void)
+{
+ struct nand_chip *chip = dl_file.nand->priv;
+ struct rda_nand_info *info = chip->priv;
+ unsigned int cal_addr_flag_offs;
+ spl_bd_t * spl_board_info = (spl_bd_t *)CONFIG_SPL_BOARD_INFO_ADDR;
+ u16 ddr_cal[2] = {0};
+ if (strcmp(dl_file.part->name, "bootloader") == 0){
+ if (spl_board_info->spl_ddr_cal_info.ddr_auto_cal_flag == CONIFG_DDR_CAL_VAL_FLAG)
+ ddr_cal[0] = spl_board_info->spl_ddr_cal_info.ddr_auto_cal_flag;
+ ddr_cal[1] = spl_board_info->spl_ddr_cal_info.ddr_auto_cal_val[0];
+ /*SPL is 2K in every nand page, hardware limit in 8810 chip*/
+ cal_addr_flag_offs = (((spl_board_info->spl_ddr_cal_info.ddr_auto_cal_offs / 2048)
+ * 2048) * info->spl_adjust_ratio) / 2 +
+ (spl_board_info->spl_ddr_cal_info.ddr_auto_cal_offs % 2048);
+ memcpy((void *)(download_buf + cal_addr_flag_offs),
+ (void *)ddr_cal, 4);
+ }
+}
+#endif
+
+static int pdl_nand_data_write(int data_end)
+{
+ unsigned int bad_blocks = 0;
+ unsigned long write_bytes = dl_file.download_size;
+ int ret = 0;
+
+ pdl_info("flash data to '%s', size %lu bytes.\n",
+ dl_file.vol_name, write_bytes);
+
+ /* write ubi volume image, system/vendor partition */
+ if(update_part_type == PART_UBI_VOL) {
+#ifdef MTDPARTS_UBI_DEF
+ ret = ubi_do_write_vol(dl_file.vol_name, download_buf, dl_file.download_size);
+
+ /* clear download buffer position & size */
+ dl_file.download_bufp = download_buf;
+ dl_file.download_size = 0;
+
+ if(data_end)
+ ubi_finish_write_vol(dl_file.vol_name);
+#endif
+
+ } else if (update_part_type == PART_FACTORYDATA) {
+ ret = factory_update_all(download_buf, write_bytes);
+ } else if (update_part_type == PART_PRDINFO) {
+ ret = prdinfo_update_all((char *)download_buf, write_bytes);
+ } else {
+
+#ifdef _TGT_AP_DDR_AUTO_CALI_ENABLE
+ /* add DDR calibration value to bootloader image, it's a hack, FIXME */
+ add_ddr_cal_val_to_bootloader();
+#endif
+
+ ret = nand_write_skip_bad_new(dl_file.nand, dl_file.write_offset,
+ (size_t *)&write_bytes,
+ dl_file.end_offset,
+ download_buf,
+ 0, &bad_blocks);
+
+ /* clear download buffer position & size */
+ dl_file.download_bufp = download_buf;
+ dl_file.download_size = 0;
+ /* update write offset */
+ dl_file.write_offset += write_bytes;
+ dl_file.write_offset += bad_blocks * dl_file.nand->erasesize;
+
+ }
+ if (ret) {
+ pdl_error("oops, flash to nand error %d\n", ret);
+ return OPERATION_FAILED;
+ }
+
+ return 0;
+}
+
+int nand_data_midst(u8 *data, size_t size)
+{
+ int ret;
+ extern uint8_t pdl_extended_status;
+
+ if (!size) {
+ pdl_error("oops, send the zero length packet\n");
+ return INVALID_SIZE;
+ }
+ if (((dl_file.recv_size + size) > dl_file.total_size)) {
+ pdl_error("transfer size error receive (%lu), file (%lu)\n",
+ dl_file.recv_size + size, dl_file.total_size);
+ return INVALID_SIZE;
+ }
+
+ /* if buffer is full */
+ if((dl_file.download_size + size) > download_max_size) {
+ if (pdl_extended_status)
+ pdl_send_rsp(ACK_AGAIN_FLASH);
+ ret = pdl_nand_data_write(0);
+ if(ret) {
+ pdl_info("ERROR: '%s' partition write failed.\n", dl_file.part_name);
+ return ret;
+ }
+ }
+
+ pdl_dbg("writing 0x%x bytes to offset: 0x%08lx\n",
+ size, (unsigned long)dl_file.download_bufp);
+
+ /* save data to buffer */
+ memcpy(dl_file.download_bufp, data, size);
+
+ dl_file.download_bufp += size;
+ dl_file.download_size += size;
+ dl_file.recv_size += size;
+
+ return 0;
+}
+
+static int pdl_nand_data_verify(uint32_t crc)
+{
+ unsigned int bad_blocks = 0;
+ unsigned long read_bytes = 0;
+ unsigned long total_size = dl_file.recv_size;
+ unsigned long read_offset = 0;
+ uint32_t new_crc = 0;
+ int ret = 0;
+
+ if(!crc)
+ return 0;
+
+ /* read back data from nand, for crc verify */
+ pdl_info("Read back data from flash to do CRC verify.\n");
+
+ if(update_part_type == PART_UBI_VOL) {
+#ifdef MTDPARTS_UBI_DEF
+ while(1) {
+ read_bytes = (total_size > download_max_size) ? download_max_size : total_size;
+ ret = ubi_read_vol(dl_file.vol_name, read_offset, download_buf, read_bytes);
+ if(ret)
+ break;
+
+ new_crc = crc32(new_crc, download_buf, read_bytes);
+
+ if(read_bytes >= total_size)
+ break;
+ total_size -= read_bytes;
+ read_offset += read_bytes;
+ }
+#endif
+
+ } else if (update_part_type == PART_RAW) {
+ read_offset = dl_file.start_offset;
+ while(1) {
+ read_bytes = (total_size > download_max_size) ? download_max_size : total_size;
+ ret = nand_read_skip_bad_new(dl_file.nand, read_offset,
+ (size_t *)&read_bytes, download_buf, &bad_blocks);
+ if(ret)
+ break;
+
+ new_crc = crc32(new_crc, download_buf, read_bytes);
+
+ if(read_bytes >= total_size)
+ break;
+ total_size -= read_bytes;
+ read_offset += read_bytes + (bad_blocks * dl_file.nand->erasesize);
+ }
+
+ } else {
+ return 0;
+ }
+
+ if (ret) {
+ pdl_error("oops, read data from nand error %d\n", ret);
+ return OPERATION_FAILED;
+ }
+
+ if(crc != new_crc) {
+ pdl_info("CRC verify failed, expect %#x, got %#x.\n", crc, new_crc);
+ return CHECKSUM_ERROR;
+ } else {
+ pdl_info("CRC verify success, %#x.\n", crc);
+ }
+
+ return 0;
+}
+
+int nand_data_end(uint32_t crc)
+{
+ int ret;
+
+ /* force to verify CRC, ignore bootloader/factorydata */
+ if ((strcmp(dl_file.part->name, "bootloader") == 0 ||
+ strcmp(dl_file.part->name, "factorydata") == 0) ||
+ strcmp(dl_file.part->name, PRDINFO_PART_NAME) == 0) {
+ crc = 0;
+ } else if(!crc) {
+ pdl_info("ERROR: CRC checking is necessary!");
+ pdl_info("ERROR: please use the new version of download tools.\n");
+ return CHECKSUM_ERROR;
+ }
+
+ ret = pdl_nand_data_write(1);
+ if(ret) {
+ pdl_info("ERROR: '%s' partition write failed.\n", dl_file.part_name);
+ return ret;
+ }
+
+ ret = pdl_nand_data_verify(crc);
+ if(ret) {
+ pdl_info("ERROR: '%s' partition data verify failed.\n", dl_file.part_name);
+ return ret;
+ }
+
+ pdl_info("END: total flashed %ld to nand\n", dl_file.recv_size);
+
+ dl_file.download_bufp = download_buf;
+ dl_file.download_size = 0;
+ update_part_type = PART_RAW;
+
+ /* the mtd partition table is saved in 'bootloader' partition,
+ * if 'bootloader' is updated, need to re-write the partition table. */
+ if(strcmp(dl_file.part->name, "bootloader") == 0) {
+ mtdparts_save_ptbl(1);
+ }
+
+ prdinfo_set_pdl_image_download_result(dl_file.part->name, 1);
+ return 0;
+}
+
+
+int nand_read_partition(const char *part_name, u8 *data, size_t size,
+ size_t *actual_len)
+{
+ struct part_info *part = NULL;
+ u8 part_num;
+ struct mtd_info *nand = NULL;
+ struct mtd_device *dev;
+ int ret;
+
+ if(size > download_max_size) {
+ pdl_info("can't read partition '%s', size is too large\n", part_name);
+ return OPERATION_FAILED;
+ }
+
+#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */
+ if(strstr(MTDPARTS_UBI_DEF, part_name)) {
+ size_t vol_size = ubi_sizeof_vol(part_name);
+ size = (size > vol_size) ? vol_size: size;
+ pdl_info("'%s' is a ubi volume in '%s' part, of size %zu.\n",
+ part_name, MTDPARTS_UBI_PART_NAME, vol_size);
+ ret = ubi_part_scan(MTDPARTS_UBI_PART_NAME);
+ if(ret) {
+ pdl_info("ubi init failed.\n");
+ return OPERATION_FAILED;
+ }
+
+ ret = ubi_read_vol(part_name, 0, data, size);
+ if(ret) {
+ pdl_info("read ubi volume error: %s\n", part_name);
+ return DEVICE_ERROR;
+ }
+ if(actual_len)
+ *actual_len = size;
+ pdl_info("read volume %s ok, %u bytes\n", part_name, size);
+ return 0;
+ }
+#endif
+
+ ret = find_dev_and_part(part_name, &dev, &part_num, &part);
+ if (ret) {
+ pdl_error("%s: invalid mtd part '%s'\n",__func__, part_name);
+ return INVALID_PARTITION;
+ }
+
+ pdl_info("%s: read %#x bytes from '%s'\n", __func__, size, part_name);
+ nand = &nand_info[dev->id->num];
+ pdl_dbg("found part '%s' offset: 0x%llx length: %d/%llu id: %d\n",
+ part->name, part->offset, size, part->size, dev->id->num);
+
+ size = (size < part->size) ? size : part->size;
+
+ if (!strcmp(part_name, "factorydata")) {
+ size = factory_get_all(data);
+ if (size <= 0 ) {
+ return DEVICE_ERROR;
+ }
+ } else {
+ ret = nand_read_skip_bad(nand, part->offset, &size,
+ (u_char *)data);
+ if(ret < 0) {
+ pdl_error("%s: read from nand error\n", __func__);
+ return DEVICE_ERROR;
+ }
+ }
+ if(actual_len)
+ *actual_len = size;
+
+ pdl_dbg("%s: done!", __func__);
+ return 0;
+}
+
+int nand_write_partition(const char *part_name, unsigned char *data, size_t size)
+{
+ struct part_info *part = NULL;
+ u8 part_num;
+ struct mtd_info *nand = NULL;
+ struct mtd_device *dev;
+ int ret;
+ u32 bad_blocks = 0;
+
+ ret = find_dev_and_part(part_name, &dev, &part_num, &part);
+ if (ret) {
+ pdl_error("%s: invalid mtd part '%s'\n",__func__, part_name);
+ return INVALID_PARTITION;
+ }
+
+ pdl_info("%s: write %#x bytes to '%s'\n", __func__, size, part_name);
+ nand = &nand_info[dev->id->num];
+ pdl_dbg("found part '%s' offset: 0x%llx length: %d/%llu id: %d\n",
+ part->name, part->offset, size, part->size, dev->id->num);
+
+ size = (size < part->size) ? size : part->size;
+
+ if (!strcmp(part_name, "factorydata")) {
+ return factory_update_all(data, size);
+ } else {
+ ret = nand_write_skip_bad_new(nand, part->offset, &size,
+ part->offset + part->size,
+ (u_char *)data, 0, &bad_blocks);
+ part->dirty = 1;
+ if(ret < 0) {
+ pdl_error("%s: write to nand error.\n", __func__);
+ return DEVICE_ERROR;
+ }
+ }
+
+ pdl_dbg("%s: done!\n", __func__);
+ return 0;
+}
+
+extern struct mtd_device *current_mtd_dev;
+
+extern int mtdparts_ptbl_check_factorydata(int *same);
+int nand_format_flash(void)
+{
+ nand_erase_options_t opts;
+ nand_info_t *nand;
+ u8 nand_num= 0;
+ int same = 0;
+
+ /* re-init partition table by default */
+ if(mtdparts_init_default()) {
+ pdl_info("mtdparts init failed, abandon flash format..\n");
+ return DEVICE_ERROR;
+ }
+
+ pdl_info("format the whole flash part\n");
+ if (!current_mtd_dev) {
+ pdl_info("%s mtd device info error\n", __func__);
+ return DEVICE_ERROR;
+ }
+ /*
+ *erase all flash parts
+ */
+ nand_num = current_mtd_dev->id->num;
+ nand = &nand_info[nand_num];
+ if (!nand) {
+ pdl_info("%s mtd device info error\n", __func__);
+ return DEVICE_ERROR;
+ }
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = 0;
+ opts.length = (loff_t) nand->size;
+ opts.jffs2 = 0;
+ opts.quiet = 0;
+ opts.scrub = 1;
+
+ /* if partition bootloader & factorydata is not changed, don't erase them */
+ mtdparts_ptbl_check_factorydata(&same);
+ if(same) {
+ struct part_info *part;
+ struct mtd_device *dev;
+ u8 part_num;
+ int ret = find_dev_and_part("factorydata", &dev, &part_num, &part);
+ if (!ret) {
+ opts.offset = part->offset + part->size;
+ opts.length -= opts.offset;
+ }
+ }
+ factory_load();
+
+ pdl_dbg("opts off 0x%08x\n", (uint32_t) opts.offset);
+ pdl_dbg("opts size 0x%08x\n", (uint32_t) opts.length);
+ pdl_dbg("nand write size 0x%08x\n", nand->writesize);
+ pdl_info("erase 0x%llx bytes to '%s' offset: 0x%llx\n",
+ opts.length, nand->name, opts.offset);
+ nand_erase_opts(nand, &opts);
+ mtdparts_clear_all_dirty(current_mtd_dev);
+
+ /* enable dirty for bootloader & factorydata part, if need */
+ if(opts.offset > 0) {
+ struct part_info *part;
+ struct mtd_device *dev;
+ u8 part_num;
+ int ret = find_dev_and_part("bootloader", &dev, &part_num, &part);
+ if (!ret) {
+ part->dirty = 1;
+ }
+ ret = find_dev_and_part("factorydata", &dev, &part_num, &part);
+ if (!ret) {
+ part->dirty = 1;
+ }
+ }
+
+ /* if 'factorydata' was erased, write back */
+ if (opts.offset <= 0)
+ factory_burn();
+
+ return 0;
+}
+
+int nand_erase_partition(const char *part_name)
+{
+ struct part_info *part;
+ struct mtd_device *dev;
+ u8 part_num;
+ int ret;
+ struct mtd_info *nand;
+
+#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */
+ if(strstr(MTDPARTS_UBI_DEF, part_name)) {
+ pdl_info("'%s' is a ubi volume in '%s' part, erase volume.\n",
+ part_name, MTDPARTS_UBI_PART_NAME);
+ ret = ubi_part_scan(MTDPARTS_UBI_PART_NAME);
+ if(ret) {
+ pdl_info("ubi init failed.\n");
+ return OPERATION_FAILED;
+ }
+
+ ret = ubi_erase_vol((char *)part_name);
+ if(ret) {
+ pdl_info("erase ubi volume error: %s\n", part_name);
+ return DEVICE_ERROR;
+ }
+ pdl_info("erase ubi volume ok: %s\n", part_name);
+ return 0;
+ }
+#endif
+
+ ret = find_dev_and_part(part_name, &dev, &part_num, &part);
+ if (ret) {
+ pdl_error("%s: invalid mtd part '%s'\n", __func__, part_name);
+ return INVALID_PARTITION;
+ }
+
+ pdl_dbg("%s: erase part '%s'\n", __func__, part_name);
+ pdl_dbg("found part '%s' offset: 0x%llx length: 0x%llx id: %d\n",
+ part->name, part->offset, part->size, dev->id->num);
+ nand = &nand_info[dev->id->num];
+
+ //erase the mtd part;
+ ret = __erase_partition(nand, part);
+ if (ret) {
+ pdl_error("%s: erase part error\n", __func__);
+ return DEVICE_ERROR;
+ }
+
+ pdl_dbg("%s: done!", __func__);
+ return 0;
+}
+
+int pdl_nand_init(void)
+{
+ int ret;
+ download_buf = (unsigned char *)SCRATCH_ADDR;
+ download_max_size = 24*1024*1024;//FB_DOWNLOAD_BUF_SIZE;
+ pdl_dbg("download buffer %p, max size %ld\n", download_buf, download_max_size);
+
+ ret = mtdparts_init_from_ptbl();
+ /* if fail to load partition table, try default */
+ if(ret) {
+ ret = mtdparts_init_default();
+ }
+ if(ret) {
+ return DEVICE_ERROR;
+ }
+
+ /* check page size */
+#ifdef NAND_PAGE_SIZE
+ if(current_mtd_dev) {
+ nand_info_t *nand = &nand_info[current_mtd_dev->id->num];
+ if(nand->writesize != NAND_PAGE_SIZE) {
+ pdl_info("nand page size is %#x, expect %#x, not compatible.\n",
+ nand->writesize, NAND_PAGE_SIZE);
+ return DEVICE_INCOMPATIBLE;
+ }
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/pdl/pdl-2/pdl_nand.h b/pdl/pdl-2/pdl_nand.h
new file mode 100644
index 0000000000..21df6c7ae3
--- /dev/null
+++ b/pdl/pdl-2/pdl_nand.h
@@ -0,0 +1,31 @@
+#ifndef __PDL_NAND_H_
+#define __PDL_NAND_H_
+
+int nand_data_start(const char *part_name, unsigned long file_size);
+int nand_data_midst(u8 *data, size_t size);
+int nand_data_end(uint32_t crc);
+int nand_read_partition(const char *part_name, unsigned char *data,
+ size_t size, size_t *actual_len);
+int nand_write_partition(const char *part_name, unsigned char *data,
+ size_t size);
+int nand_format_flash(void);
+int nand_erase_partition(const char *part_name);
+
+int pdl_nand_init(void);
+
+#ifdef MTDPARTS_UBI_DEF
+int ubi_part_scan(const char *part_name);
+int ubi_check_default_vols(const char *ubi_default_str);
+int ubi_erase_vol(const char *vol_name);
+int ubi_read_vol(const char *vol_name, loff_t offp, void *buf, size_t size);
+int ubi_update_vol(const char *vol_name, void *buf, size_t size);
+size_t ubi_sizeof_vol(const char *vol_name);
+
+/* write a volume by multi parts. */
+int ubi_prepare_write_vol(const char *volume, size_t size);
+int ubi_do_write_vol(const char *volume, void *buf, size_t size);
+int ubi_finish_write_vol(const char *volume);
+#endif
+
+#endif
+