aboutsummaryrefslogtreecommitdiff
path: root/pdl
diff options
context:
space:
mode:
Diffstat (limited to 'pdl')
-rw-r--r--pdl/common/Makefile48
-rw-r--r--pdl/common/packet.c301
-rw-r--r--pdl/common/pdl.c28
-rw-r--r--pdl/common/pdl_channel.c39
-rw-r--r--pdl/common/pdl_channel_usb.c85
-rw-r--r--pdl/common/pdl_debug.c48
-rw-r--r--pdl/common/pdl_engine.c141
-rw-r--r--pdl/include/cmd_defs.h77
-rw-r--r--pdl/include/config.h11
-rw-r--r--pdl/include/packet.h63
-rw-r--r--pdl/include/pdl_channel.h28
-rw-r--r--pdl/include/pdl_command.h15
-rw-r--r--pdl/include/pdl_debug.h28
-rw-r--r--pdl/include/pdl_engine.h12
-rw-r--r--pdl/pdl-1/Makefile48
-rw-r--r--pdl/pdl-1/pdl1_command.h13
-rw-r--r--pdl/pdl-1/pdl_command.c176
-rw-r--r--pdl/pdl-1/pdl_main.c34
-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
26 files changed, 2977 insertions, 0 deletions
diff --git a/pdl/common/Makefile b/pdl/common/Makefile
new file mode 100644
index 0000000000..527445b73a
--- /dev/null
+++ b/pdl/common/Makefile
@@ -0,0 +1,48 @@
+#
+# (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
+
+CPPFLAGS += -I ../include/
+CFLAGS += -I ../include -mno-unaligned-access
+LIB := $(obj)libpdl_common.o
+
+COBJS-y := packet.o pdl_channel.o pdl_channel_usb.o pdl_debug.o pdl_engine.o pdl.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/common/packet.c b/pdl/common/packet.c
new file mode 100644
index 0000000000..669d83cc52
--- /dev/null
+++ b/pdl/common/packet.c
@@ -0,0 +1,301 @@
+//#define DEBUG
+
+#include <asm/types.h>
+#include <common.h>
+#include "packet.h"
+#include "pdl_channel.h"
+#include <linux/string.h>
+#include "pdl_debug.h"
+#include "cmd_defs.h"
+#include <asm/unaligned.h>
+
+static enum {
+ PKT_IDLE = 0,
+ PKT_HEAD,
+ PKT_DATA,
+ PKT_ERROR
+} pkt_state;
+
+const static char *pdl_device_rsp[] = {
+ [ACK] = "device ack",
+ [PACKET_ERROR] = "packet error",
+ [INVALID_CMD] = "invalid cmd",
+ [UNKNOWN_CMD] = "unknown cmd",
+ [INVALID_ADDR] = "invalid address",
+ [INVALID_BAUDRATE] = "invalid baudrate",
+ [INVALID_PARTITION] = "invalid partition",
+ [INVALID_SIZE] = "invalid size",
+ [WAIT_TIMEOUT] = "wait timeout",
+ [VERIFY_ERROR] = "verify error",
+ [CHECKSUM_ERROR] = "checksum error",
+ [OPERATION_FAILED] = "operation failed",
+ [DEVICE_ERROR] = "device error",
+ [NO_MEMORY] = "no memory",
+ [DEVICE_INCOMPATIBLE] = "device incompatible",
+ [HW_TEST_ERROR] = "hardware test error",
+ [MD5_ERROR] = "md5 error",
+ [ACK_AGAIN_ERASE] = "ack again erase",
+ [ACK_AGAIN_FLASH] = "ack again flash",
+ [MAX_RSP] = "max response",
+};
+
+static int pkt_error = 0;
+static unsigned char cmd_buf[PDL_MAX_PKT_SIZE] __attribute__((__aligned__(32)));
+static struct pdl_channel *channel;
+
+int pdl_init_packet_channel(void)
+{
+ channel = pdl_get_channel();
+ return 0;
+}
+
+/*
+ * return value: 1, get packet ok;
+ * 0, not a host packet;
+ * -1, something error, check pkt_error for true cause;
+ * -2, don't get any packet
+ */
+int pdl_get_cmd_packet(struct pdl_packet *pkt)
+{
+ u32 pkt_size = 0;
+ u32 hdrsz = sizeof(struct packet_header);
+ u32 cmd_hdrsz = sizeof(struct command_header);
+ u32 bufsz = 0, totalsz = 0;
+ int i, wait = 0;
+
+ if (!pkt) {
+ pdl_error("packet error\n");
+ return -1;
+ }
+
+ if (!channel->tstc())
+ return -2;
+
+ /* looking for HOST_PACKET_TAG 0xAE */
+ while (1) {
+ bufsz = channel->tstc();
+ if (!bufsz)
+ return -2;
+ channel->read(cmd_buf, bufsz);
+ if (cmd_buf[0] == HOST_PACKET_TAG) {
+ /*pdl_info("Found host packet tag, %u bytes\n", bufsz);*/
+ break;
+ } else {
+ pdl_dbg("Drop a %u bytes packet:", bufsz);
+ for(i = 0; i < 8; i++)
+ pdl_dbg(" %02x", cmd_buf[i]);
+ pdl_dbg("\n");
+ }
+ }
+ totalsz = bufsz;
+
+ /* is a valid packet header? */
+ if (totalsz < hdrsz) {
+ pdl_error("Invaild packet, it's too small (%u bytes)\n", totalsz);
+ pkt_state = PKT_IDLE;
+ return -2;
+ }
+
+ /* packet header check */
+ pkt_state = PKT_HEAD;
+ memcpy(&pkt_size, &cmd_buf[1], 4);
+ if (cmd_buf[5] != HOST_PACKET_FLOWID ||
+ pkt_size > PDL_MAX_PKT_SIZE ||
+ pkt_size < sizeof(u32)) {
+ pdl_error("Invalid packet, flowid 0x%x, pkt_size %u\n",
+ cmd_buf[5], pkt_size);
+ pdl_dbg("packet data:");
+ for (i = 0; i < 8; i++)
+ pdl_dbg(" %02x", cmd_buf[i]);
+ pdl_dbg("\n");
+
+ pkt_state = PKT_IDLE;
+ return -2;
+ }
+ /*pdl_info("Got command packet, packet size %u bytes\n", pkt_size);*/
+
+ /* get all data */
+ if (pkt_size > totalsz - hdrsz) {
+ wait = 0;
+ while (1) {
+ bufsz = channel->tstc();
+ if (bufsz) {
+ u32 last = pkt_size - (totalsz - hdrsz);
+ bufsz = (bufsz > last) ? last : bufsz;
+ channel->read(&cmd_buf[totalsz], bufsz);
+ totalsz += bufsz;
+ wait = 0;
+ if (bufsz == last)
+ break;
+ } else {
+ if (wait++ >= 1000) {
+ pdl_error("wait packet data timeout, got %u bytes, need %u\n",
+ totalsz - hdrsz, pkt_size);
+ pkt_error = PACKET_ERROR;
+ return -1;
+ }
+ udelay(1000);
+ }
+ }
+ }
+
+ pkt_state = PKT_DATA;
+ memset(pkt, 0, sizeof(struct pdl_packet));
+ if (pkt_size < cmd_hdrsz) {
+ memcpy(pkt, &cmd_buf[hdrsz], pkt_size);
+ } else {
+ memcpy(pkt, &cmd_buf[hdrsz], cmd_hdrsz);
+ pkt->data = &cmd_buf[hdrsz+cmd_hdrsz];
+
+ /* packet data crc checking, if have 4 bytes crc in tail */
+ if (pkt_size >= cmd_hdrsz + pkt->cmd_header.data_size + sizeof(u32)) {
+ i = hdrsz + cmd_hdrsz + pkt->cmd_header.data_size;
+ u32 crc = 0, crc_now = 0;
+ memcpy(&crc, &cmd_buf[i], sizeof(u32));
+ crc_now = crc32(0, pkt->data, pkt->cmd_header.data_size);
+ if (crc != crc_now) {
+ pdl_error("packet data crc verify failed, expect %#x, got %#x\n", crc, crc_now);
+ pkt_error = CHECKSUM_ERROR;
+ return -1;
+ }
+ }
+ }
+ return 1;
+}
+
+
+int pdl_get_connect_packet(struct pdl_packet *pkt, u32 timeout)
+{
+ char ch = 0;
+ u8 header_buf[sizeof(struct packet_header)];
+ u32 pkt_size = 0;
+ u32 cnt = timeout; //timeout is in ms;
+
+ while (cnt) {
+ if(!channel->tstc()) {
+ mdelay(1);
+ //pdl_error("try to get\n");
+ cnt--;
+ continue;
+ }
+
+ ch = channel->getchar();
+ if (ch == HOST_PACKET_TAG) {
+ pkt_state = PKT_HEAD;
+ break;
+ } else {
+ pdl_info("oops, get 0x%x\n", ch);
+ continue;
+ }
+ }
+
+ if (cnt == 0 ) {
+ pkt_error = WAIT_TIMEOUT;
+ pdl_error("get connect timeout\n");
+ return -1;
+ }
+ header_buf[0] = ch;
+ while (1) {
+ u8 flowid;
+
+ switch (pkt_state) {
+ case PKT_IDLE:
+ break;
+ case PKT_HEAD:
+ channel->read(&header_buf[1], 5);
+ flowid = header_buf[5];
+ pkt_size = 0;
+ if (flowid == HOST_PACKET_FLOWID) {
+ //ok, this is the header
+ memcpy(&pkt_size, &header_buf[1], 4);
+ pkt_size = le32_to_cpu(pkt_size);
+ pkt_state = PKT_DATA;
+ pdl_vdbg("header right data_size 0x%x\n", pkt_size);
+ } else {
+ pkt_state = PKT_IDLE;
+ return 0;
+ }
+ break;
+ case PKT_DATA:
+ //diff the command data size and packet data size,check if it correct;
+ if (pkt_size > (sizeof(struct command_header) + 1)) {
+ pkt_error = INVALID_SIZE;
+ return -1;
+ }
+ channel->read(cmd_buf, pkt_size);
+ memcpy(pkt, cmd_buf, sizeof(struct command_header));
+
+ pdl_vdbg("pkt cmd type 0x%x data start 0x%x, size %x\n",
+ pkt->cmd_header.cmd_type,
+ pkt->cmd_header.data_addr,
+ pkt->cmd_header.data_size);
+ return 1;
+ case PKT_ERROR:
+ break;
+ }
+ }
+ return 1;
+}
+/*
+static void pdl_put_packet(struct packet *pkt)
+{
+}
+*/
+void pdl_put_cmd_packet(struct pdl_packet *pkt)
+{
+ memset(pkt, 0, sizeof (struct pdl_packet));
+ pkt_state = PKT_IDLE;
+ pkt_error = 0;
+}
+
+int pdl_get_packet_error(void)
+{
+ return pkt_error;
+}
+
+static int pdl_send_data(const u8 *data, u32 size, u8 flowid)
+{
+ struct packet_header *header = (struct packet_header *)cmd_buf;
+ int hdr_sz = sizeof(struct packet_header);
+
+ if(size > PDL_MAX_PKT_SIZE - hdr_sz) {
+ pdl_error("packet size is too large.\n");
+ return -1;
+ }
+
+ memset(cmd_buf, PDL_MAX_PKT_SIZE, 0);
+ memcpy(&cmd_buf[hdr_sz], data, size);
+
+ header->tag = PACKET_TAG;
+ header->flowid = flowid;
+ /* carefully, header->pkt_size may be unaligned */
+ put_unaligned(size, &header->pkt_size); /* header->pkt_size = size; */
+
+ channel->write(cmd_buf, hdr_sz + size);
+ return 0;
+}
+
+void pdl_send_rsp(int rsp)
+{
+ /*
+ * if wait timeout, the client may be exit, we dont send
+ * this error, for the new client will get a extra error
+ * response
+ */
+ if (rsp == WAIT_TIMEOUT)
+ return;
+
+ if (rsp != ACK) {
+ if (rsp < ACK || rsp > MAX_RSP)
+ rsp = DEVICE_ERROR;
+ pdl_info("send rsp '%s'\n", pdl_device_rsp[rsp]);
+ }
+
+ pdl_send_data((const u8 *)&rsp, sizeof(int), (rsp == ACK) ? FLOWID_ACK : FLOWID_ERROR);
+ pdl_dbg("send rsp '%s'\n", pdl_device_rsp[rsp]);
+}
+
+int pdl_send_pkt(const u8 *data, u32 size)
+{
+ return pdl_send_data(data, size, FLOWID_DATA);
+}
diff --git a/pdl/common/pdl.c b/pdl/common/pdl.c
new file mode 100644
index 0000000000..e3bbe27cba
--- /dev/null
+++ b/pdl/common/pdl.c
@@ -0,0 +1,28 @@
+#include <common.h>
+#include <asm/arch/hwcfg.h>
+#include <asm/arch/rda_sys.h>
+#include <pdl.h>
+
+int pdl_dbg_pdl = 0;
+int pdl_vdbg_pdl = 0;
+int pdl_dbg_usb_ep0 = 0;
+int pdl_dbg_usb_serial = 0;
+int pdl_dbg_rw_check = 0;
+int pdl_dbg_factory_part = 0;
+
+/*
+ check if should start pdl utilities
+*/
+int pdl_mode_get(void)
+{
+ u16 swcfg = rda_swcfg_get();
+
+ /*
+ if hwconfig is in force download and swcofing is not
+ in calibration mode
+ */
+ if (rda_bm_is_download() && !(swcfg & RDA_SW_CFG_BIT_4))
+ return 1;
+ else
+ return 0;
+}
diff --git a/pdl/common/pdl_channel.c b/pdl/common/pdl_channel.c
new file mode 100644
index 0000000000..313535423c
--- /dev/null
+++ b/pdl/common/pdl_channel.c
@@ -0,0 +1,39 @@
+
+#include "pdl_channel.h"
+#include "pdl_debug.h"
+
+static struct pdl_channel *serial_channel = NULL;
+
+void pdl_channel_register(struct pdl_channel *channel)
+{
+ if (channel) {
+ serial_channel = channel;
+ } else {
+ pdl_error("channel cannot register\n");
+ }
+
+ return;
+}
+
+struct pdl_channel *pdl_get_channel(void)
+{
+ return serial_channel;
+}
+
+static struct pdl_channel *debug_channel = NULL;
+
+void pdl_debug_channel_register(struct pdl_channel *channel)
+{
+ if (channel) {
+ debug_channel = channel;
+ } else {
+ pdl_error("debug channel cannot register\n");
+ }
+
+ return;
+}
+
+struct pdl_channel *pdl_get_debug_channel(void)
+{
+ return debug_channel;
+}
diff --git a/pdl/common/pdl_channel_usb.c b/pdl/common/pdl_channel_usb.c
new file mode 100644
index 0000000000..1bd7d55527
--- /dev/null
+++ b/pdl/common/pdl_channel_usb.c
@@ -0,0 +1,85 @@
+#include <usb/usbserial.h>
+#include "pdl_channel.h"
+#include "pdl_debug.h"
+
+static int usbser_ch0_tstc (void)
+{
+ return usbser_tstc(USB_ACM_CHAN_0);
+}
+
+static int usbser_ch0_getc (void)
+{
+ return usbser_getc(USB_ACM_CHAN_0);
+}
+
+static void usbser_ch0_putc (const char c)
+{
+ usbser_putc(USB_ACM_CHAN_0, c);
+}
+
+static int usbser_ch0_read(unsigned char *_buf, unsigned int len)
+{
+ return usbser_read(USB_ACM_CHAN_0, _buf, len);
+}
+
+static int usbser_ch0_write(const unsigned char *_buf, unsigned int len)
+{
+ return usbser_write(USB_ACM_CHAN_0, _buf, len);
+}
+
+static struct pdl_channel usb_channel = {
+ .getchar = usbser_ch0_getc,
+ .putchar = usbser_ch0_putc,
+ .tstc = usbser_ch0_tstc,
+ .read = usbser_ch0_read,
+ .write = usbser_ch0_write,
+ .priv = NULL
+};
+
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+
+static int usbser_ch1_tstc (void)
+{
+ return usbser_tstc(USB_ACM_CHAN_1);
+}
+
+static int usbser_ch1_getc (void)
+{
+ return usbser_getc(USB_ACM_CHAN_1);
+}
+
+static void usbser_ch1_putc (const char c)
+{
+ usbser_putc(USB_ACM_CHAN_1, c);
+}
+
+static int usbser_ch1_read(unsigned char *_buf, unsigned int len)
+{
+ return usbser_read(USB_ACM_CHAN_1, _buf, len);
+}
+
+static int usbser_ch1_write(const unsigned char *_buf, unsigned int len)
+{
+ return usbser_write(USB_ACM_CHAN_1, _buf, len);
+}
+
+static struct pdl_channel usb_channel2 = {
+ .getchar = usbser_ch1_getc,
+ .putchar = usbser_ch1_putc,
+ .tstc = usbser_ch1_tstc,
+ .read = usbser_ch1_read,
+ .write = usbser_ch1_write,
+ .priv = NULL,
+};
+
+#endif //CONFIG_USB_ACM_TWO_CHANS
+
+void pdl_usb_channel_register(void)
+{
+#ifdef CONFIG_USB_ACM_TWO_CHANS
+ pdl_debug_channel_register(&usb_channel2);
+#endif
+
+ pdl_channel_register(&usb_channel);
+}
+
diff --git a/pdl/common/pdl_debug.c b/pdl/common/pdl_debug.c
new file mode 100644
index 0000000000..07b812bce0
--- /dev/null
+++ b/pdl/common/pdl_debug.c
@@ -0,0 +1,48 @@
+#include <common.h>
+#include <malloc.h>
+#include <stdio_dev.h>
+#include "pdl.h"
+#include "pdl_debug.h"
+#include "pdl_channel.h"
+
+char *pdl_log_buff = NULL;
+static void pdl_serial_puts(const char *s)
+{
+ struct pdl_channel *dbg_ch = pdl_get_debug_channel();
+
+ /* save print string to log buffer */
+ if(pdl_log_buff) {
+ int a = strlen(pdl_log_buff);
+ int b = strlen(s);
+
+ if(a + b < PDL_LOG_MAX_SIZE)
+ strcpy(&pdl_log_buff[a], s);
+ }
+
+ serial_puts(s);
+
+ if(pdl_dbg_usb_serial && dbg_ch)
+ dbg_ch->write((const unsigned char *)s, strlen(s)+1);
+}
+
+struct stdio_dev *search_device(int flags, const char *name);
+void pdl_init_serial(void)
+{
+ struct stdio_dev *dev = search_device (DEV_FLAGS_OUTPUT, "serial");
+ if(!dev)
+ return;
+ dev->puts = pdl_serial_puts;
+
+ if(!pdl_log_buff) {
+ pdl_log_buff = malloc(PDL_LOG_MAX_SIZE);
+ pdl_log_buff[0] = '\0';
+ }
+}
+void pdl_release_serial(void)
+{
+ struct stdio_dev *dev = search_device (DEV_FLAGS_OUTPUT, "serial");
+ if(!dev)
+ return;
+ dev->puts = serial_puts;
+}
+
diff --git a/pdl/common/pdl_engine.c b/pdl/common/pdl_engine.c
new file mode 100644
index 0000000000..2e4c5d3745
--- /dev/null
+++ b/pdl/common/pdl_engine.c
@@ -0,0 +1,141 @@
+//#define DEBUG
+#include <common.h>
+#include <asm/types.h>
+#include "cmd_defs.h"
+#include <asm/arch/rda_sys.h>
+#include "packet.h"
+#include "pdl_engine.h"
+#include "pdl_debug.h"
+
+static struct pdl_cmd cmd_handler_table[MAX_CMD];
+
+#define cmd_is_valid(cmd) ((cmd >= MIN_CMD) && (cmd < MAX_CMD))
+
+static const char *pdl_commands[MAX_CMD] = {
+ [CONNECT] = "connect",
+ [ERASE_FLASH] = "erase flash",
+ [ERASE_PARTITION] = "erase partition",
+ [ERASE_ALL] = "erase all",
+ [START_DATA] = "start data",
+ [MID_DATA] = "midst data",
+ [END_DATA] = "end data",
+ [EXEC_DATA] = "execute data",
+ [READ_FLASH] = "read flash",
+ [READ_PARTITION] = "read partition",
+ [NORMAL_RESET] = "normal reset",
+ [READ_CHIPID] = "read chipid",
+ [SET_BAUDRATE] = "set baudrate",
+ [FORMAT_FLASH] = "format flash",
+ [READ_PARTITION_TABLE] = "read partition table",
+ [READ_IMAGE_ATTR] = "read image attribute",
+ [GET_VERSION] = "get version",
+ [SET_FACTMODE] = "set factory mode",
+ [SET_CALIBMODE] = "set calibration mode",
+ [SET_PDL_DBG] = "set pdl debuglevel",
+ [CHECK_PARTITION_TABLE] = "check partition table",
+ [POWER_OFF] = "power off",
+ [IMAGE_LIST] = "download image list",
+ [GET_SECURITY] = "get security capabilities",
+ [HW_TEST] = "hardware test",
+ [GET_PDL_LOG] = "get pdl log",
+ [DOWNLOAD_FINISH] = "download finish",
+};
+
+int pdl_cmd_register(int cmd_type, pdl_cmd_handler handler)
+{
+ if (!cmd_is_valid(cmd_type))
+ return -1;
+
+ cmd_handler_table[cmd_type].handler = handler;
+ return 0;
+}
+
+int pdl_handle_connect(u32 timeout)
+{
+ struct pdl_packet pkt;
+ int cmd;
+ int res;
+
+ res = pdl_get_connect_packet(&pkt, timeout);
+ if (res != 1) {
+ return -1;
+ }
+
+ cmd = pkt.cmd_header.cmd_type;
+ if (cmd == CONNECT)
+ cmd_handler_table[cmd].handler(&pkt, 0);
+ else
+ goto err;
+
+ pdl_put_cmd_packet(&pkt);
+ return 0;
+err:
+ pdl_put_cmd_packet(&pkt);
+ return -1;
+}
+
+#define CONNECT_TIMEOUT 180000 /*3 minutes*/
+static void pdl_command_loop(void)
+{
+ struct pdl_packet pkt;
+ int res;
+ u32 cmd;
+ int error;
+ int cnt = CONNECT_TIMEOUT;
+ int check_connect = 1;
+
+ while (1) {
+ res = pdl_get_cmd_packet(&pkt);
+ if (res == -2) {
+ if (!check_connect) {
+ mdelay(1);
+ continue;
+ } else if (--cnt > 0) {
+ mdelay(1);
+ continue;
+ }
+
+ if (cnt <= 0) {
+ pdl_error("PDL get command timeout...\n");
+ break;
+ }
+ }
+ if (check_connect)
+ check_connect = 0;
+
+ if (res == 0)
+ continue;
+
+ //if error send response to pc.
+ if (res == -1) {
+ error = pdl_get_packet_error();
+ pdl_send_rsp(error);
+ pdl_put_cmd_packet(&pkt);
+ continue;
+ }
+ cmd = pkt.cmd_header.cmd_type;
+
+ if (!cmd_is_valid(cmd)) {
+ pdl_send_rsp(INVALID_CMD);
+ pdl_put_cmd_packet(&pkt);
+ continue;
+ }
+
+ pdl_dbg("get and exec cmd '%s'\n", pdl_commands[cmd]);
+
+ if (cmd_handler_table[cmd].handler)
+ res = cmd_handler_table[cmd].handler(&pkt, 0);
+ pdl_put_cmd_packet(&pkt);
+ }
+
+ pdl_info("shutdown machine...\n");
+ shutdown_system();
+}
+
+int pdl_handler(void *arg)
+{
+ for (;;) {
+ pdl_command_loop();
+ }
+ return 0;
+}
diff --git a/pdl/include/cmd_defs.h b/pdl/include/cmd_defs.h
new file mode 100644
index 0000000000..1ca9adcabc
--- /dev/null
+++ b/pdl/include/cmd_defs.h
@@ -0,0 +1,77 @@
+#ifndef _CMD_DEFS_H_
+#define _CMD_DEFS_H_
+#include "packet.h"
+
+enum pdl_cmd_type{
+ MIN_CMD = 0,
+ CONNECT = MIN_CMD,
+ ERASE_FLASH,
+ ERASE_PARTITION,
+ ERASE_ALL,
+ START_DATA,
+ MID_DATA,
+ END_DATA,
+ EXEC_DATA,
+ READ_FLASH,
+ READ_PARTITION,
+ NORMAL_RESET,
+ READ_CHIPID,
+ SET_BAUDRATE,
+ FORMAT_FLASH,
+ READ_PARTITION_TABLE,
+ READ_IMAGE_ATTR,
+ GET_VERSION,
+ SET_FACTMODE,
+ SET_CALIBMODE,
+ SET_PDL_DBG,
+ CHECK_PARTITION_TABLE,
+ POWER_OFF,
+ IMAGE_LIST,
+ GET_SWCFG_REG,
+ SET_SWCFG_REG,
+ GET_HWCFG_REG,
+ SET_HWCFG_REG,
+ EXIT_AND_RELOAD,
+ GET_SECURITY,
+ HW_TEST,
+ GET_PDL_LOG,
+ DOWNLOAD_FINISH,
+ MAX_CMD
+};
+
+enum pdl_rsp {
+ ACK = 0,
+ //from PC command
+ PACKET_ERROR,
+ INVALID_CMD,
+ UNKNOWN_CMD,
+ INVALID_ADDR,
+ INVALID_BAUDRATE,
+ INVALID_PARTITION,
+ INVALID_SIZE,
+ WAIT_TIMEOUT,
+ //from phone
+ VERIFY_ERROR,
+ CHECKSUM_ERROR,
+ OPERATION_FAILED,
+ //phone internal
+ DEVICE_ERROR, //DDR,NAND init errors
+ NO_MEMORY,
+ DEVICE_INCOMPATIBLE,
+ HW_TEST_ERROR,
+ MD5_ERROR,
+ ACK_AGAIN_ERASE,
+ ACK_AGAIN_FLASH,
+ MAX_RSP
+};
+
+#define PDL_DBG_PDL (0x1<<0)
+#define PDL_DBG_USB_EP0 (0x1<<1)
+#define PDL_DBG_USB_SERIAL (0x1<<2)
+#define PDL_DBG_RW_CHECK (0x1<<3)
+#define PDL_DBG_FACTORY_PART (0x1<<4)
+#define PDL_DBG_PDL_VERBOSE (0x1<<5)
+#define PDL_EXTENDED_STATUS (0x1<<7)
+
+#endif
+
diff --git a/pdl/include/config.h b/pdl/include/config.h
new file mode 100644
index 0000000000..b2d37705ab
--- /dev/null
+++ b/pdl/include/config.h
@@ -0,0 +1,11 @@
+#ifndef __PDL_CONFIG_H__
+#define __PDL_CONFIG_H__
+
+#ifdef CONFIG_SPL_BUILD
+# define CONFIG_RDA_PDL1
+#else
+# define CONFIG_RDA_PDL2
+#endif
+
+#endif // __PDL_CONFIG_H__
+
diff --git a/pdl/include/packet.h b/pdl/include/packet.h
new file mode 100644
index 0000000000..c261eba161
--- /dev/null
+++ b/pdl/include/packet.h
@@ -0,0 +1,63 @@
+#ifndef _PACKET_H_
+#define _PACKET_H_
+#include "config.h"
+
+/* host define, for receiving packet */
+#define HOST_PACKET_TAG 0xAE
+#define HOST_PACKET_FLOWID 0xFF
+
+/* client define, for sending packet */
+#define PACKET_TAG 0xAE
+#define FLOWID_DATA 0xBB
+#define FLOWID_ACK 0xFF
+#define FLOWID_ERROR 0xEE
+
+/*
+ * for yaffs image, the packet length must be aligned to the (page_size+oob)
+ */
+#if defined(CONFIG_RDA_PDL2)
+# define PDL_MAX_DATA_SIZE 270336 //(256*(1024 + 32)) == (64 * 4224)
+#elif defined(CONFIG_RDA_PDL1)
+# define PDL_MAX_DATA_SIZE (4*1024)
+#else
+#error "no valid pdl config"
+#endif
+
+
+struct packet_header {
+ u8 tag;
+ u32 pkt_size;
+ u8 flowid;
+}__attribute__((packed));
+
+struct command_header {
+ u32 cmd_type;
+ u32 data_addr;
+ u32 data_size;
+};
+
+#define PDL_MAX_PKT_SIZE (PDL_MAX_DATA_SIZE + \
+ sizeof(struct command_header) + sizeof(struct packet_header))
+
+struct pdl_packet {
+ struct command_header cmd_header;
+ u8 *data;
+};
+
+
+struct packet{
+ struct packet_header pkt_header;
+ struct pdl_packet *pdl_pkt;
+ int state;
+};
+
+int pdl_get_cmd_packet(struct pdl_packet *pkt);
+void pdl_put_cmd_packet(struct pdl_packet *pkt);
+int pdl_get_connect_packet(struct pdl_packet *pkt, u32 timeout);
+
+void pdl_send_rsp(int rsp);
+int pdl_send_pkt(const u8 *data, u32 size);
+int pdl_get_packet_error(void);
+int pdl_init_packet_channel(void);
+#endif
+
diff --git a/pdl/include/pdl_channel.h b/pdl/include/pdl_channel.h
new file mode 100644
index 0000000000..731878d729
--- /dev/null
+++ b/pdl/include/pdl_channel.h
@@ -0,0 +1,28 @@
+#ifndef _PDL_CHANNEL_H_
+#define _PDL_CHANNEL_H_
+
+struct pdl_channel {
+ int (*read) (unsigned char *buf, unsigned int len);
+ int (*getchar) (void);
+ int (*tstc) (void);
+ int (*write) (const unsigned char *buf, unsigned int len);
+ void (*putchar) (const char ch);
+ void *priv;
+};
+
+/* get the current pdl data channel */
+struct pdl_channel *pdl_get_channel(void);
+/* register the pdl data channel */
+void pdl_channel_register(struct pdl_channel *channel);
+
+/* get the current pdl debug channel */
+struct pdl_channel *pdl_get_debug_channel(void);
+/* register the pdl debug channel */
+void pdl_debug_channel_register(struct pdl_channel *channel);
+
+/* register the usb serial channel 0 as the pdl data channel.
+ if defined, register the usb serial channel 1 as the pdl debug channel. */
+void pdl_usb_channel_register(void);
+
+#endif
+
diff --git a/pdl/include/pdl_command.h b/pdl/include/pdl_command.h
new file mode 100644
index 0000000000..08e8b66bed
--- /dev/null
+++ b/pdl/include/pdl_command.h
@@ -0,0 +1,15 @@
+#ifndef _PDL_COMMAND_H_
+#define _PDL_COMMAND_H_
+#include "config.h"
+#include "packet.h"
+
+
+#if defined(CONFIG_RDA_PDL1)
+#include "../pdl-1/pdl1_command.h"
+#elif defined(CONFIG_RDA_PDL2)
+#include "../pdl-2/pdl2_command.h"
+#else
+#error "no valid pdl"
+#endif
+
+#endif
diff --git a/pdl/include/pdl_debug.h b/pdl/include/pdl_debug.h
new file mode 100644
index 0000000000..d908e7a28f
--- /dev/null
+++ b/pdl/include/pdl_debug.h
@@ -0,0 +1,28 @@
+#ifndef _PDL_DEBUG_H_
+#define _PDL_DEBUG_H_
+
+#include <serial.h>
+
+#define PDL_LOG_MAX_SIZE (512*1024)
+
+extern char *pdl_log_buff;
+void pdl_init_serial(void);
+void pdl_release_serial(void);
+
+#define pdl_error printf
+#define pdl_info printf
+
+extern int pdl_dbg_pdl;
+#define pdl_dbg(str...) \
+ do { \
+ if (pdl_dbg_pdl) \
+ printf(str); \
+ } while(0)
+
+extern int pdl_vdbg_pdl;
+#define pdl_vdbg(str...) \
+ do { \
+ if (pdl_vdbg_pdl) \
+ printf(str); \
+ } while(0)
+#endif
diff --git a/pdl/include/pdl_engine.h b/pdl/include/pdl_engine.h
new file mode 100644
index 0000000000..84f07797f6
--- /dev/null
+++ b/pdl/include/pdl_engine.h
@@ -0,0 +1,12 @@
+#ifndef _PDL_ENGINE_H_
+#define _PDL_ENGINE_H_
+
+typedef int (*pdl_cmd_handler)(struct pdl_packet *pkt, void *arg);
+struct pdl_cmd {
+ pdl_cmd_handler handler;
+};
+
+int pdl_cmd_register(int cmd_type, pdl_cmd_handler handler);
+int pdl_handler(void *arg);
+int pdl_handle_connect(u32 timeout);
+#endif
diff --git a/pdl/pdl-1/Makefile b/pdl/pdl-1/Makefile
new file mode 100644
index 0000000000..ec7032182d
--- /dev/null
+++ b/pdl/pdl-1/Makefile
@@ -0,0 +1,48 @@
+#
+# (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-$(CONFIG_SPL_RDA_PDL) += pdl_command.o pdl_main.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-1/pdl1_command.h b/pdl/pdl-1/pdl1_command.h
new file mode 100644
index 0000000000..12407f6034
--- /dev/null
+++ b/pdl/pdl-1/pdl1_command.h
@@ -0,0 +1,13 @@
+#ifndef _PDL1_COMMAND_H_
+#define _PDL1_COMMAND_H_
+
+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 get_pdl_version(struct pdl_packet *packet, void *arg);
+int reset_machine(struct pdl_packet *packet, void *arg);
+int get_pdl_security(struct pdl_packet *packet, void *arg);
+
+#endif
diff --git a/pdl/pdl-1/pdl_command.c b/pdl/pdl-1/pdl_command.c
new file mode 100644
index 0000000000..3eb4769410
--- /dev/null
+++ b/pdl/pdl-1/pdl_command.c
@@ -0,0 +1,176 @@
+#include <common.h>
+#include "packet.h"
+#include <linux/string.h>
+#include "cmd_defs.h"
+#include "pdl_debug.h"
+#include <asm/arch/rda_sys.h>
+#include <asm/arch/rda_crypto.h>
+
+struct dl_file_info {
+ unsigned long start_address;
+ unsigned long total_size;
+ unsigned long recv_size;
+ unsigned long next_address;
+};
+
+static struct dl_file_info exec_file;
+
+
+int sys_connect(struct pdl_packet *packet, void *arg)
+{
+ pdl_info("connect with pc\n");
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+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;
+
+#if 0
+ if ((start_addr < MEMORY_START) || (start_addr >= MEMORY_START + MEMORY_SIZE)) {
+ pdl_send_rsp(INVALID_ADDR);
+ return 0;
+ }
+
+ if ((start_addr + file_size) > (MEMORY_START + MEMORY_SIZE)) {
+ pdl_send_rsp(INVALID_SIZE);
+ return 0;
+ }
+#endif
+ pdl_info("start addr %lx, file size %lx\n", start_addr, file_size);
+
+ exec_file.start_address = start_addr;
+ exec_file.total_size = file_size;
+ exec_file.recv_size = 0;
+ exec_file.next_address = start_addr;
+
+// memset((void*)start_addr, 0, file_size);
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int data_midst(struct pdl_packet *packet, void *arg)
+{
+ short data_len = packet->cmd_header.data_size;
+
+ if ((exec_file.recv_size + data_len) > exec_file.total_size) {
+ pdl_send_rsp(INVALID_SIZE);
+ return 0;
+ }
+
+ memcpy((void *)exec_file.next_address, packet->data, data_len);
+ exec_file.next_address += data_len;
+ exec_file.recv_size += data_len;
+
+ pdl_dbg("write to addr %lx, receive size %ld\n", exec_file.next_address,
+ exec_file.recv_size);
+ pdl_send_rsp(ACK);
+ return 0;
+}
+
+int data_end(struct pdl_packet *packet, void *arg)
+{
+ pdl_info("receive pdl2 finish\n");
+ 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 get_pdl_version(struct pdl_packet *packet, void *arg)
+{
+ static const unsigned char pdl1_version[] = "PDL1";
+
+ pdl_info("get pdl version\n");
+
+ pdl_send_pkt(pdl1_version, sizeof(pdl1_version));
+ return 0;
+}
+
+#include <asm/arch/rom_api_trampolin.h>
+
+int get_pdl_security(struct pdl_packet *packet, void *arg)
+{
+ struct {
+ char buffer[16];
+ struct chip_id chip_id;
+ struct chip_security_context chip_security_context;
+ struct chip_unique_id chip_unique_id;
+ struct pubkey pubkey;
+ u8 random[32];
+ } response;
+
+ pdl_info("get pdl security\n");
+
+ memset(&response, 0, sizeof(response));
+
+ if (memcmp(romapi->magic, "RDA API", 8) == 0) {
+ int n;
+ get_chip_id(&response.chip_id);
+ get_chip_unique(&response.chip_unique_id);
+ n = get_chip_security_context(&response.chip_security_context,
+ &response.pubkey);
+ get_chip_true_random(response.random, 32);
+ sprintf(response.buffer, "RDASEC: v%d/%d", romapi->version, n);
+ }
+ else
+ sprintf(response.buffer, "RDASEC: none");
+
+ pdl_send_pkt((const u8 *)&response, sizeof(response));
+
+ return 0;
+}
+
+
+int reset_machine(struct pdl_packet *packet, void *arg)
+{
+ u8 reboot_mode = REBOOT_TO_NORMAL_MODE;
+
+ pdl_info("reset machine....\n");
+
+ 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;
+}
+
diff --git a/pdl/pdl-1/pdl_main.c b/pdl/pdl-1/pdl_main.c
new file mode 100644
index 0000000000..242745297d
--- /dev/null
+++ b/pdl/pdl-1/pdl_main.c
@@ -0,0 +1,34 @@
+#include <common.h>
+#include "pdl_command.h"
+#include "packet.h"
+#include "cmd_defs.h"
+#include "pdl_engine.h"
+#include "pdl_debug.h"
+#include "pdl_channel.h"
+
+int pdl_main(void)
+{
+ pdl_info("start PDL1 main loop\n");
+ pdl_usb_channel_register();
+ pdl_init_packet_channel();
+
+ 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(NORMAL_RESET, reset_machine);
+ pdl_cmd_register(GET_VERSION, get_pdl_version);
+ pdl_cmd_register(GET_SECURITY, get_pdl_security);
+
+#if 0
+ serial_puts("wait for connect....\n");
+ if (pdl_handle_connect(CONNECT_TIMEOUT) != 0){
+ pdl_error("cannot handle connect\n");
+ return -1;
+ }
+#endif
+ pdl_send_rsp(ACK);
+ pdl_handler(NULL);
+ return 0;
+}
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
+