diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/cmd_fastboot.c | 1114 | ||||
-rw-r--r-- | common/cmd_mtdparts.c | 336 | ||||
-rw-r--r-- | common/cmd_nand.c | 149 | ||||
-rw-r--r-- | common/lcd.c | 2 |
5 files changed, 1567 insertions, 35 deletions
diff --git a/common/Makefile b/common/Makefile index f3ad4bdd63b..bb23c0af006 100644 --- a/common/Makefile +++ b/common/Makefile @@ -90,6 +90,7 @@ COBJS-$(CONFIG_CMD_EEPROM) += cmd_eeprom.o COBJS-$(CONFIG_CMD_ELF) += cmd_elf.o COBJS-$(CONFIG_SYS_HUSH_PARSER) += cmd_exit.o COBJS-$(CONFIG_CMD_EXT2) += cmd_ext2.o +COBJS-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o COBJS-$(CONFIG_CMD_FAT) += cmd_fat.o COBJS-$(CONFIG_CMD_FDC)$(CONFIG_CMD_FDOS) += cmd_fdc.o COBJS-$(CONFIG_OF_LIBFDT) += cmd_fdt.o fdt_support.o diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 00000000000..97d1c232170 --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,1114 @@ +/* + * Copyright (C) 2010 Texas Instruments + * + * Author : Mohammed Afzal M A <afzal@ti.com> + * + * 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 + * + * + * Fastboot is implemented using gadget stack, many of the ideas are + * derived from fastboot implemented in OmapZoom by + * Tom Rix <Tom.Rix@windriver.com>, and portion of the code has been + * ported from OmapZoom. + * + * Part of OmapZoom was copied from Android project, Android source + * (legacy bootloader) was used indirectly here by using OmapZoom. + * + * This is Android's Copyright: + * + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <common.h> +#include <command.h> +#include <fastboot.h> +#include <asm/sizes.h> +#include <jffs2/load_kernel.h> + +#define ERR +#define WARN +#define INFO +//#define DEBUG + +void output_lcd_string(char *p); + +#ifdef DEBUG +#define FBTDBG(fmt,args...)\ + printf("DEBUG: [%s]: %d: \n"fmt, __FUNCTION__, __LINE__,##args) +#else +#define FBTDBG(fmt,args...) do{}while(0) +#endif + +#ifdef INFO +#define FBTINFO(fmt,args...)\ + printf("INFO: [%s]: "fmt, __FUNCTION__, ##args) +#else +#define FBTINFO(fmt,args...) do{}while(0) +#endif + +#ifdef WARN +#define FBTWARN(fmt,args...)\ + printf("WARNING: [%s]: "fmt, __FUNCTION__, ##args) +#else +#define FBTWARN(fmt,args...) do{}while(0) +#endif + +#ifdef ERR +#define FBTERR(fmt,args...)\ + printf("ERROR: [%s]: "fmt, __FUNCTION__, ##args) +#else +#define FBTERR(fmt,args...) do{}while(0) +#endif + +#ifdef FASTBOOT_PORT_OMAPZOOM_NAND_FLASHING +#include <nand.h> +#include <environment.h> +#endif + +/* USB specific */ + +#include <usb_defs.h> + +#if defined(CONFIG_PPC) +#include <usb/mpc8xx_udc.h> +#elif defined(CONFIG_OMAP1510) +#include <usb/omap1510_udc.h> +#elif defined(CONFIG_MUSB_UDC) +#include <usb/musb_udc.h> +#elif defined(CONFIG_PXA27X) +#include <usb/pxa27x_udc.h> +#elif defined(CONFIG_SPEAR3XX) || defined(CONFIG_SPEAR600) +#include <usb/spr_udc.h> +#endif + +#define STR_LANG 0x00 +#define STR_MANUFACTURER 0x01 +#define STR_PRODUCT 0x02 +#define STR_SERIAL 0x03 +#define STR_CONFIGURATION 0x04 +#define STR_INTERFACE 0x05 +#define STR_COUNT 0x06 + +#define CONFIG_USBD_CONFIGURATION_STR "Android Fastboot Configuration" +#define CONFIG_USBD_INTERFACE_STR "Android Fastboot Interface" + +#define USBFBT_BCD_DEVICE 0x00 +#define USBFBT_MAXPOWER 0x32 + +#define NUM_CONFIGS 1 +#define NUM_INTERFACES 1 +#define NUM_ENDPOINTS 2 + +#define RX_EP_INDEX 1 +#define TX_EP_INDEX 2 + +struct _fbt_config_desc { + struct usb_configuration_descriptor configuration_desc; + struct usb_interface_descriptor interface_desc; + struct usb_endpoint_descriptor endpoint_desc[NUM_ENDPOINTS]; +}; + +static int fbt_handle_response(void); + +/* defined and used by gadget/ep0.c */ +extern struct usb_string_descriptor **usb_strings; + +static struct cmd_fastboot_interface priv; + +/* USB Descriptor Strings */ +static char serial_number[28]; /* what should be the length ?, 28 ? */ +static char product_name[32]; +static u8 wstr_lang[4] = {4,USB_DT_STRING,0x9,0x4}; +static u8 wstr_manufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)]; +static u8 wstr_product[2 + 2*(sizeof(product_name) - 1)]; +static u8 wstr_serial[2 + 2*(sizeof(serial_number) - 1)]; +static u8 wstr_configuration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)]; +static u8 wstr_interface[2 + 2*(sizeof(CONFIG_USBD_INTERFACE_STR)-1)]; + +/* USB descriptors */ +static struct usb_device_descriptor device_descriptor = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(USB_BCD_VERSION), + .bDeviceClass = 0xFF, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE, + .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID), + .bcdDevice = cpu_to_le16(USBFBT_BCD_DEVICE), + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, + .bNumConfigurations = NUM_CONFIGS +}; + +static struct _fbt_config_desc fbt_config_desc = { + .configuration_desc = { + .bLength = sizeof(struct usb_configuration_descriptor), + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(sizeof(struct _fbt_config_desc)), + .bNumInterfaces = NUM_INTERFACES, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIGURATION, + .bmAttributes = BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED, + .bMaxPower = USBFBT_MAXPOWER, + }, + .interface_desc = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0x2, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, + .iInterface = STR_INTERFACE, + }, + .endpoint_desc = { + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + /* XXX: can't the address start from 0x1, currently + seeing problem with "epinfo" */ + .bEndpointAddress = RX_EP_INDEX | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bInterval = 0xFF, + }, + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + /* XXX: can't the address start from 0x1, currently + seeing problem with "epinfo" */ + .bEndpointAddress = TX_EP_INDEX | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bInterval = 0xFF, + }, + }, +}; + +static struct usb_interface_descriptor interface_descriptors[NUM_INTERFACES]; +static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS]; + +static struct usb_string_descriptor *fbt_string_table[STR_COUNT]; +static struct usb_device_instance device_instance[1]; +static struct usb_bus_instance bus_instance[1]; +static struct usb_configuration_instance config_instance[NUM_CONFIGS]; +static struct usb_interface_instance interface_instance[NUM_INTERFACES]; +static struct usb_alternate_instance alternate_instance[NUM_INTERFACES]; +static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS + 1]; + +/* FASBOOT specific */ + +#define GETVARLEN 30 +#define SECURE "no" + +/* U-boot version */ +extern char version_string[]; + +static struct cmd_fastboot_interface priv = +{ + .transfer_buffer = (void *)CONFIG_FASTBOOT_TRANSFER_BUFFER, + .transfer_buffer_size = CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE, +}; + +static int fbt_init_endpoints (void); + +#ifdef FASTBOOT_PORT_OMAPZOOM_NAND_FLASHING +/* Use do_bootm and do_go for fastboot's 'boot' command */ +extern int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); +/* Use do_env_set and do_env_save to permenantly save data */ +extern int do_env_save (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); +extern int do_env_set ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); +extern int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]); +extern int do_flerase (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +#endif /* FASTBOOT_PORT_OMAPZOOM_NAND_FLASHING */ + +static int fbt_lastidle; + +static void fbt_pokeidle(void) +{ + fbt_lastidle = get_ticks(); +} + +static int fbt_getidle(void) +{ + return get_ticks() - fbt_lastidle; +} + +/* USB specific */ + +/* utility function for converting char* to wide string used by USB */ +static void str2wide (char *str, u16 * wide) +{ + int i; + for (i = 0; i < strlen (str) && str[i]; i++){ + #if defined(__LITTLE_ENDIAN) + wide[i] = (u16) str[i]; + #elif defined(__BIG_ENDIAN) + wide[i] = ((u16)(str[i])<<8); + #else + #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined" + #endif + } +} + +/* fastboot_init has to be called before this fn to get correct serial string */ +static int fbt_init_strings(void) +{ + struct usb_string_descriptor *string; + const char *model = getenv("logic_model"); + fbt_string_table[STR_LANG] = + (struct usb_string_descriptor*)wstr_lang; + + string = (struct usb_string_descriptor *) wstr_manufacturer; + string->bLength = sizeof(wstr_manufacturer); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_MANUFACTURER, string->wData); + fbt_string_table[STR_MANUFACTURER] = string; + + string = (struct usb_string_descriptor *) wstr_product; + string->bLength = sizeof(wstr_product); + string->bDescriptorType = USB_DT_STRING; + + if(model) + { + strncpy (product_name, model, sizeof(product_name)); + } else { + // Otherwise, populate generically. + strncpy (product_name, "LogicPD SOM", sizeof(product_name)); + } + product_name[sizeof(product_name)-1] = '\0'; + str2wide (product_name, string->wData); + fbt_string_table[STR_PRODUCT] = string; + + string = (struct usb_string_descriptor *) wstr_serial; + string->bLength = sizeof(wstr_serial); + string->bDescriptorType = USB_DT_STRING; + str2wide (serial_number, string->wData); + fbt_string_table[STR_SERIAL] = string; + + string = (struct usb_string_descriptor *) wstr_configuration; + string->bLength = sizeof(wstr_configuration); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_CONFIGURATION_STR, string->wData); + fbt_string_table[STR_CONFIGURATION] = string; + + string = (struct usb_string_descriptor *) wstr_interface; + string->bLength = sizeof(wstr_interface); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_INTERFACE_STR, string->wData); + fbt_string_table[STR_INTERFACE] = string; + + /* Now, initialize the string table for ep0 handling */ + usb_strings = fbt_string_table; + + return 0; +} + +static void fbt_event_handler (struct usb_device_instance *device, + usb_device_event_t event, int data) +{ + switch (event) { + case DEVICE_RESET: + case DEVICE_BUS_INACTIVE: + output_lcd_string("/pAA/kFastboot is waiting for connection from PC/pBA/k/pCA/k"); + + priv.configured = 0; + + // Clear active transfers and flags. + priv.flag = 0; + priv.d_size = 0; + priv.d_bytes = 0; + priv.u_size = 0; + priv.u_bytes = 0; + break; + case DEVICE_CONFIGURED: + priv.configured = 1; + break; + + case DEVICE_ADDRESS_ASSIGNED: + output_lcd_string("/pAA/kFastboot is connected to PC"); + + fbt_init_endpoints (); + + default: + break; + } +} + +/* fastboot_init has to be called before this fn to get correct serial string */ +static int fbt_init_instances(void) +{ + int i; + + /* initialize device instance */ + memset (device_instance, 0, sizeof (struct usb_device_instance)); + device_instance->device_state = STATE_INIT; + device_instance->device_descriptor = &device_descriptor; + device_instance->event = fbt_event_handler; + device_instance->cdc_recv_setup = NULL; + device_instance->bus = bus_instance; + device_instance->configurations = NUM_CONFIGS; + device_instance->configuration_instance_array = config_instance; + + /* XXX: what is this bus instance for ?, can't it be removed by moving + endpoint_array and serial_number_str is moved to device instance */ + /* initialize bus instance */ + memset (bus_instance, 0, sizeof (struct usb_bus_instance)); + bus_instance->device = device_instance; + bus_instance->endpoint_array = endpoint_instance; + /* XXX: what is the relevance of max_endpoints & maxpacketsize ? */ + bus_instance->max_endpoints = 1; + bus_instance->maxpacketsize = 64; + bus_instance->serial_number_str = serial_number; + + /* configuration instance */ + memset (config_instance, 0, + sizeof (struct usb_configuration_instance)); + config_instance->interfaces = NUM_INTERFACES; + config_instance->configuration_descriptor = + (struct usb_configuration_descriptor *)&fbt_config_desc; + config_instance->interface_instance_array = interface_instance; + + /* XXX: is alternate instance required in case of no alternate ? */ + /* interface instance */ + memset (interface_instance, 0, + sizeof (struct usb_interface_instance)); + interface_instance->alternates = 1; + interface_instance->alternates_instance_array = alternate_instance; + + /* alternates instance */ + memset (alternate_instance, 0, + sizeof (struct usb_alternate_instance)); + alternate_instance->interface_descriptor = interface_descriptors; + alternate_instance->endpoints = NUM_ENDPOINTS; + alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs; + + /* endpoint instances */ + memset (&endpoint_instance[0], 0, + sizeof (struct usb_endpoint_instance)); + endpoint_instance[0].endpoint_address = 0; + endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL; + endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL; + /* XXX: following statement to done along with other endpoints + at another place ? */ + udc_setup_ep (device_instance, 0, &endpoint_instance[0]); + + for (i = 1; i <= NUM_ENDPOINTS; i++) { + memset (&endpoint_instance[i], 0, + sizeof (struct usb_endpoint_instance)); + + endpoint_instance[i].endpoint_address = + ep_descriptor_ptrs[i - 1]->bEndpointAddress; + + endpoint_instance[i].rcv_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].rcv_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].tx_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + urb_link_init (&endpoint_instance[i].rcv); + urb_link_init (&endpoint_instance[i].rdy); + urb_link_init (&endpoint_instance[i].tx); + urb_link_init (&endpoint_instance[i].done); + + if (endpoint_instance[i].endpoint_address & USB_DIR_IN) + endpoint_instance[i].tx_urb = + usbd_alloc_urb (device_instance, + &endpoint_instance[i]); + else + endpoint_instance[i].rcv_urb = + usbd_alloc_urb (device_instance, + &endpoint_instance[i]); + } + + return 0; +} + +/* XXX: ep_descriptor_ptrs can be removed by making better use of + fbt_config_desc.endpoint_desc */ +static int fbt_init_endpoint_ptrs(void) +{ + ep_descriptor_ptrs[0] = &fbt_config_desc.endpoint_desc[0]; + ep_descriptor_ptrs[1] = &fbt_config_desc.endpoint_desc[1]; + + return 0; +} + +static int fbt_init_endpoints(void) +{ + int i; + + /* XXX: should it be moved to some other function ? */ + bus_instance->max_endpoints = NUM_ENDPOINTS + 1; + + /* XXX: is this for loop required ?, yes for MUSB it is */ + for (i = 1; i <= NUM_ENDPOINTS; i++) { + + /* configure packetsize based on HS negotiation status */ + if (device_instance->speed == USB_SPEED_FULL) { + FBTINFO("setting up FS USB device ep%x\n", + endpoint_instance[i].endpoint_address); + ep_descriptor_ptrs[i - 1]->wMaxPacketSize = + CONFIG_USBD_FASTBOOT_BULK_PKTSIZE_FS; + } else if (device_instance->speed == USB_SPEED_HIGH) { + FBTINFO("setting up HS USB device ep%x\n", + endpoint_instance[i].endpoint_address); + ep_descriptor_ptrs[i - 1]->wMaxPacketSize = + CONFIG_USBD_FASTBOOT_BULK_PKTSIZE_HS; + } + + endpoint_instance[i].tx_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + endpoint_instance[i].rcv_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + + udc_setup_ep (device_instance, i, &endpoint_instance[i]); + + } + + return 0; +} + +static struct urb *next_urb (struct usb_device_instance *device, + struct usb_endpoint_instance *endpoint) +{ + struct urb *current_urb = NULL; + int space; + + /* If there's a queue, then we should add to the last urb */ + if (!endpoint->tx_queue) { + current_urb = endpoint->tx_urb; + } else { + /* Last urb from tx chain */ + current_urb = + p2surround (struct urb, link, endpoint->tx.prev); + } + + /* Make sure this one has enough room */ + space = current_urb->buffer_length - current_urb->actual_length; + if (space > 0) { + return current_urb; + } else { /* No space here */ + /* First look at done list */ + current_urb = first_urb_detached (&endpoint->done); + if (!current_urb) { + current_urb = usbd_alloc_urb (device, endpoint); + } + + urb_append (&endpoint->tx, current_urb); + endpoint->tx_queue++; + } + return current_urb; +} + +/* FASBOOT specific */ +static void set_env(char *var, char *val) +{ + char *setenv[4] = { "setenv", NULL, NULL, NULL, }; + + setenv[1] = var; + setenv[2] = val; + + do_env_set(NULL, 0, 3, setenv); +} + +int do_lcd_percent (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]); + +void set_lcd_percent_string(char *string) +{ + char *lcd_percent[] = { "lcd_percent", string}; + + do_lcd_percent (NULL, 0, ARRAY_SIZE(lcd_percent), lcd_percent); +} + +static void set_serial_number(void) +{ + char *dieid = getenv("logic_serial"); + if (dieid == NULL) { + priv.serial_no = "00123"; + } else { + int len; + + memset(&serial_number[0], 0, 28); + len = strlen(dieid); + if (len > 28) + len = 26; + + strncpy(&serial_number[0], dieid, len); + + priv.serial_no = &serial_number[0]; + } +} + +static int fbt_fastboot_init(void) +{ + priv.flag = 0; + priv.d_size = 0; + priv.d_bytes = 0; + priv.u_size = 0; + priv.u_bytes = 0; + priv.exit = 0; + + priv.product_name = FASTBOOT_PRODUCT_NAME; + set_serial_number(); + + return 0; +} + +#ifdef FASTBOOT_PORT_OMAPZOOM_NAND_FLASHING + +static int get_part_size(const char *partname, u8 *dev_type) +{ +#ifdef CONFIG_CMD_MTDPARTS + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + int ret; + + ret = mtdparts_init(); + if (ret) + return ret; + + ret = find_dev_and_part(partname, &dev, &pnum, &part, 0); + if (ret) + return ret; + + if(dev_type) + *dev_type = dev->id->type; + + return part->size; +#else + return 0; +#endif +} + +static int fbt_nandpart_erase(char *part) +{ + char *erase[] = { "nand", "erase.part", part}; + return do_nand(NULL, 0, ARRAY_SIZE(erase), erase); +} + +static int fbt_nandpart_write(char *part, void *address, int size) +{ + char addr_str[32], size_str[32]; + char *write[] = { "nand", "write.auto", addr_str, part, size_str }; + + // Prep command line arguments + sprintf(addr_str, "0x%x", (unsigned int)address); + sprintf(size_str, "0x%x", size); + + return do_nand(NULL, 0, ARRAY_SIZE(write), write); +} + +static int fbt_handle_erase(char *cmdbuf) +{ + int status; + + if((status = fbt_nandpart_erase(cmdbuf + 6))) + sprintf(priv.response, "FAILfailed to erase partition"); + else + sprintf(priv.response, "OKAY"); + return status; +} + +static int fbt_handle_flash(char *cmdbuf) +{ + int status = 0; + + if (priv.d_bytes) { + u8 dev_type = 0; + char *part = cmdbuf + 6; + int size = get_part_size(part, &dev_type); + + if(size < 0) + { + sprintf(priv.response, "FAILpartition does not exist"); + return status; + } else if(priv.d_bytes > size) + { + sprintf(priv.response, "FAILimage too large for partition"); + return status; + } + + FBTINFO("Flashing '%s'\n", part); + switch(dev_type) + { + case MTD_DEV_TYPE_NOR: + FBTINFO("Fastboot currently doesn't support NOR\n"); + sprintf(priv.response, "FAILUnsupported partition type (nor)"); + return 1; + break; + case MTD_DEV_TYPE_NAND: + if(fbt_nandpart_erase(part) || + fbt_nandpart_write(part, priv.transfer_buffer, priv.d_bytes)) + { + FBTINFO("Failed flashing '%s'\n", part); + sprintf(priv.response, "FAILFailed erasing or writing partition"); + return 1; + } + break; + case MTD_DEV_TYPE_ONENAND: + FBTINFO("Fastboot currently doesn't support ONENAND.\n"); + sprintf(priv.response, "FAILUnsupported partition type (onenand)"); + return 1; + } + FBTINFO("Done flashing '%s'\n", part); + sprintf(priv.response, "OKAY"); + } else { + sprintf(priv.response, "FAILno image downloaded"); + } + + return status; +} +#endif /* FASTBOOT_PORT_OMAPZOOM_NAND_FLASHING */ + + +static int fbt_handle_getvar(char *cmdbuf) +{ + strcpy(priv.response, "OKAY"); + if(!strcmp(cmdbuf + strlen("getvar:"), "version")) { + FBTDBG("getvar version\n"); + strcpy(priv.response + 4, FASTBOOT_VERSION); + } else if(!strcmp(cmdbuf + strlen("getvar:"), "version-bootloader")) { + strncpy(priv.response + 4, version_string, + min(strlen(version_string), GETVARLEN)); + } else if(!strcmp(cmdbuf + strlen("getvar:"), "secure")) { + strcpy(priv.response + 4, SECURE); + } else if(!strcmp(cmdbuf + strlen("getvar:"), "product")) { + if (priv.product_name) + strcpy(priv.response + 4, priv.product_name); + } else if(!strcmp(cmdbuf + strlen("getvar:"), "serialno")) { + if (priv.serial_no) + strcpy(priv.response + 4, priv.serial_no); + } + return 0; +} + +static int fbt_handle_reboot(char *cmdbuf) +{ + strcpy(priv.response,"OKAY"); + priv.flag |= FASTBOOT_FLAG_RESPONSE; + fbt_handle_response(); + udelay (1000000); /* 1 sec */ + + do_reset (NULL, 0, 0, NULL); + + return 0; +} + +static int fbt_handle_boot(char *cmdbuf) +{ + if ((priv.d_bytes) && + (CONFIG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE < priv.d_bytes)) { + char start[32]; + char *bootm[3] = { "bootm", NULL, NULL, }; + char *go[3] = { "go", NULL, NULL, }; + + /* + * Use this later to determine if a command line was passed + * for the kernel. + */ + struct fastboot_boot_img_hdr *fb_hdr = + (struct fastboot_boot_img_hdr *) priv.transfer_buffer; + + /* Skip the mkbootimage header */ + image_header_t *hdr = (image_header_t *) + &priv.transfer_buffer[CONFIG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE]; + + bootm[1] = go[1] = start; + sprintf (start, "0x%x", (unsigned int)hdr); + + /* Execution should jump to kernel so send the response + now and wait a bit. */ + sprintf(priv.response, "OKAY"); + priv.flag |= FASTBOOT_FLAG_RESPONSE; + fbt_handle_response(); + output_lcd_string("/pCA/k/pBA/kFastboot is booting uploaded image"); + udelay (1000000); /* 1 sec */ + udc_disconnect(); + + if (ntohl(hdr->ih_magic) == IH_MAGIC) { + /* Looks like a kernel.. */ + FBTINFO("Booting kernel..\n"); + + /* + * Check if the user sent a bootargs down. + * If not, do not override what is already there + */ + if (strlen ((char *) &fb_hdr->cmdline[0])) + set_env ("bootargs", (char *) &fb_hdr->cmdline[0]); + + do_bootm (NULL, 0, 2, bootm); + } else { + /* Raw image, maybe another uboot */ + FBTINFO("Booting raw image..\n"); + + do_go (NULL, 0, 2, go); + } + + FBTERR("booting failed, reset the board\n"); + } + sprintf(priv.response, "FAILinvalid boot image"); + + return 0; +} + +/* XXX: Replace magic number & strings with macros */ +static int fbt_rx_process(unsigned char *buffer, int length) +{ + /* Generic failed response */ + strcpy(priv.response, "FAIL"); + + if (!priv.d_size) { + /* command */ + char *cmdbuf = (char *) buffer; + + FBTDBG("command\n"); + output_lcd_string("/pCA/k/pBA/kFastboot is processing command "); + output_lcd_string(cmdbuf); + + set_lcd_percent_string(""); + + if(memcmp(cmdbuf, "getvar:", 7) == 0) { + FBTDBG("getvar\n"); + fbt_handle_getvar(cmdbuf); + } + + if(memcmp(cmdbuf, "erase:", 6) == 0) { + FBTDBG("erase\n"); + set_lcd_percent_string("/pCA/kErasing is /P% done"); + fbt_handle_erase(cmdbuf); + } + + if(memcmp(cmdbuf, "flash:", 6) == 0) { + FBTDBG("flash\n"); + set_lcd_percent_string("/pCA/kFlashing is /P% done"); + fbt_handle_flash(cmdbuf); + } + + if((memcmp(cmdbuf, "reboot", 6) == 0) || + (memcmp(cmdbuf, "reboot-bootloader", 17) == 0)) { + FBTDBG("reboot/reboot-bootloader\n"); + fbt_handle_reboot(cmdbuf); + } + + if(memcmp(cmdbuf, "continue", 8) == 0) { + FBTDBG("continue\n"); + strcpy(priv.response,"OKAY"); + priv.exit = 1; + } + + if(memcmp(cmdbuf, "boot", 4) == 0) { + FBTDBG("boot\n"); + output_lcd_string("/pAA/kFastboot is continuing boot (timeout)/pBA/k/pCA/k"); + fbt_handle_boot(cmdbuf); + } + + if(memcmp(cmdbuf, "download:", 9) == 0) { + FBTDBG("download\n"); + + /* XXX: need any check for size & bytes ? */ + priv.d_size = + simple_strtoul (cmdbuf + 9, NULL, 16); + priv.d_bytes = 0; + + FBTINFO ("starting download of %d bytes\n", + priv.d_size); + + if (priv.d_size == 0) { + strcpy(priv.response, "FAILdata invalid size"); + } else if (priv.d_size > + priv.transfer_buffer_size) { + priv.d_size = 0; + strcpy(priv.response, "FAILdata too large"); + } else { + sprintf(priv.response, "DATA%08x", priv.d_size); + } + + set_lcd_percent_string("/pCA/kDownload /P% done"); + lcd_percent_init(priv.d_size); + } + + if((memcmp(cmdbuf, "upload:", 7) == 0) || + (memcmp(cmdbuf, "uploadraw", 10) == 0)) { + FBTDBG("upload/uploadraw\n"); + + set_lcd_percent_string("/pCA/kUpload /P% done"); + sprintf(priv.response, "FAILUpload not supported"); + } + priv.flag |= FASTBOOT_FLAG_RESPONSE; + } else { + if (length) { + unsigned int xfr_size; + + xfr_size = priv.d_size - priv.d_bytes; + if (xfr_size > length) + xfr_size = length; + memcpy(priv.transfer_buffer + priv.d_bytes, + buffer, xfr_size); + priv.d_bytes += xfr_size; + + lcd_percent_update(priv.d_bytes); + +#ifdef INFO + /* Inform via prompt that download is happening */ + if (! (priv.d_bytes % (16 * 0x1000))) + printf("."); + if (! (priv.d_bytes % (80 * 16 * 0x1000))) + printf("\n"); +#endif + if (priv.d_bytes >= priv.d_size) { + priv.d_size = 0; + strcpy(priv.response, "OKAY"); + priv.flag |= FASTBOOT_FLAG_RESPONSE; +#ifdef INFO + printf(".\n"); +#endif + FBTINFO("downloaded %d bytes\n", priv.d_bytes); + } + } else + FBTWARN("empty buffer download\n"); + } + + return 0; +} + +static int fbt_handle_rx(void) +{ + struct usb_endpoint_instance *ep = &endpoint_instance[RX_EP_INDEX]; + + /* XXX: Or update status field, if so, + "usbd_rcv_complete" [gadget/core.c] also need to be modified */ + if (ep->rcv_urb->actual_length) { + FBTDBG("rx length: %u\n", ep->rcv_urb->actual_length); + fbt_rx_process(ep->rcv_urb->buffer, ep->rcv_urb->actual_length); + /* Required to poison rx urb buffer as in omapzoom ?, + yes, as fastboot command are sent w/o NULL termination. + Attempt is made here to reduce poison length, may be safer + to posion the whole buffer, also it is assumed that at + the time of creation of urb it is poisoned */ + memset(ep->rcv_urb->buffer, 0, ep->rcv_urb->actual_length); + ep->rcv_urb->actual_length = 0; + } + + return 0; +} + +static int fbt_response_process(void) +{ + struct usb_endpoint_instance *ep = &endpoint_instance[TX_EP_INDEX]; + struct urb *current_urb = NULL; + unsigned char *dest = NULL; + int n, ret = 0; + + current_urb = next_urb (device_instance, ep); + if (!current_urb) { + FBTERR("%s: current_urb NULL", __func__); + return -1; + } + + dest = current_urb->buffer + current_urb->actual_length; + n = MIN (64, strlen(priv.response)); + memcpy(dest, priv.response, n); + current_urb->actual_length += n; + FBTDBG("response urb length: %u\n", current_urb->actual_length); + if (ep->last == 0) { + ret = udc_endpoint_write (ep); + return ret; + } + + return ret; +} + +static int fbt_handle_response(void) +{ + if (priv.flag & FASTBOOT_FLAG_RESPONSE) { + if(!strncmp(priv.response, "FAIL", 4)) + { + output_lcd_string("/pCA/kCommand failed"); + } else + if(!strncmp(priv.response, "OKAY", 4)) + { + output_lcd_string("/pCA/k/pBA/kFastboot is idle"); + } + fbt_response_process(); + priv.flag &= ~FASTBOOT_FLAG_RESPONSE; + } + + return 0; +} + +static int fbt_tx_process(void) +{ + struct usb_endpoint_instance *ep = &endpoint_instance[TX_EP_INDEX]; + struct urb *current_urb = NULL; + unsigned char *dest = NULL; + int n = 0, ret = 0; + + current_urb = next_urb (device_instance, ep); + if (!current_urb) { + FBTERR("%s: current_urb NULL", __func__); + return -1; + } + + dest = current_urb->buffer + current_urb->actual_length; + n = MIN (64, priv.u_size - priv.u_bytes); + memcpy(dest, priv.transfer_buffer + priv.u_bytes, n); + current_urb->actual_length += n; + if (ep->last == 0) { + ret = udc_endpoint_write (ep); + /* XXX: "ret = n" should be done iff n bytes has been + * transmitted, "udc_endpoint_write" to be changed for it, + * now it always return 0. + */ + return n; + } + + return ret; +} + +static int fbt_handle_tx(void) +{ + if (priv.u_size) { + int bytes_written = fbt_tx_process(); + + if (bytes_written > 0) { + /* XXX: is this the right way to update priv.u_bytes ?, + * may be "udc_endpoint_write()" can be modified to + * return number of bytes transmitted or error and + * update based on hence obtained value + */ + priv.u_bytes += bytes_written; + + lcd_percent_update(priv.u_bytes); +#ifdef INFO + /* Inform via prompt that upload is happening */ + if (! (priv.d_bytes % (16 * 0x1000))) + printf("."); + if (! (priv.d_bytes % (80 * 16 * 0x1000))) + printf("\n"); +#endif + if (priv.u_bytes >= priv.u_size) +#ifdef INFO + printf(".\n"); +#endif + priv.u_size = priv.u_bytes = 0; + FBTINFO("data upload finished\n"); + } else { + FBTERR("bytes_written: %d\n", bytes_written); + return -1; + } + + } + + return 0; +} + +/* command */ +int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret = -1; + int timeout = 0; + + ret = fbt_fastboot_init(); + ret = fbt_init_endpoint_ptrs(); + + if ((ret = udc_init()) < 0) { + FBTERR("%s: MUSB UDC init failure\n", __func__); + return ret; + } + + ret = fbt_init_strings(); + ret = fbt_init_instances(); + + udc_startup_events (device_instance); + udc_connect(); + + FBTINFO("fastboot initialized\n"); + output_lcd_string("/pAA/kFastboot is waiting for connection from PC"); + + if(argc > 1) + { + timeout = simple_strtoul(argv[1], NULL, 10) * get_tbclk(); + fbt_pokeidle(); + } + + while(1) { + udc_irq(); + if (priv.configured) { + fbt_pokeidle(); + fbt_handle_rx(); + fbt_handle_response(); + fbt_handle_tx(); + } + priv.exit |= ctrlc(); + if (priv.exit) { + output_lcd_string("/pAA/kFastboot is continuing boot/pBA/k/pCA/k"); + set_lcd_percent_string(""); + FBTINFO("fastboot end\n"); + break; + } + if(timeout && fbt_getidle() > timeout) + { + output_lcd_string("/pAA/kFastboot is continuing boot (timeout)/pBA/k/pCA/k"); + FBTINFO("Timed out\n"); + break; + } + } + + udc_disconnect(); + + return ret; +} + +U_BOOT_CMD(fastboot, 2, 1, do_fastboot, + "fastboot [timeout in seconds] - use USB Fastboot protocol\n", NULL); diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 32d6c899d14..a32762f5f21 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -132,6 +132,12 @@ static const char *const mtdids_default = MTDIDS_DEFAULT; static const char *const mtdids_default = NULL; #endif +#if defined(MTDFLAGS_DEFAULT) +staitc const char *const mtdflags_default = MTDFLAGS_DEFAULT; +#else +staitc const char *const mtdflags_default = NULL; +#endif + #if defined(MTDPARTS_DEFAULT) static const char *const mtdparts_default = MTDPARTS_DEFAULT; #else @@ -145,6 +151,10 @@ char *get_mtdids_default(void) { return (char *)mtdids_default; } +char *get_mtdflags_default(void) +{ + return (char *)mtdflags_default; +} #endif /* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */ @@ -153,7 +163,9 @@ char *get_mtdids_default(void) #define PARTITION_MAXLEN 16 static char last_ids[MTDIDS_MAXLEN]; static char last_parts[MTDPARTS_MAXLEN]; +static char last_flags[MTDPARTS_MAXLEN]; static char last_partition[PARTITION_MAXLEN]; +static int mtdflags_defaultmask; /* low level jffs2 cache cleaning routine */ extern void jffs2_free_cache(struct part_info *part); @@ -173,6 +185,7 @@ static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part /* command line only routines */ static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len); static int device_del(struct mtd_device *dev); +static char *mtdflags_tostring(int flags, int parenth); /** * Parses a string into a number. The number stored at ptr is @@ -1233,6 +1246,79 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; } +/** + * Process all devices and generate corresponding mtdparts string describing + * all partitions on all devices. + * + * @param buf output buffer holding generated mtdparts string (output) + * @param buflen buffer size + * @return 0 on success, 1 otherwise + */ +static int generate_mtdflags(char *buf, u32 buflen) +{ + struct list_head *pentry, *dentry; + struct mtd_device *dev; + struct part_info *part; + char *p = buf; + u32 maxlen = buflen - 1; + int needs_semicolon=0; + + debug("--- generate_mtdflags ---\n"); + + if (list_empty(&devices)) { + buf[0] = '\0'; + return 0; + } + + buf[0] = '\0'; + + if(mtdflags_defaultmask) + { + p = stpncpy(p, "default=", maxlen - (p - buf)); + p = stpncpy(p, mtdflags_tostring(mtdflags_defaultmask, 0), maxlen - (p - buf)); + needs_semicolon = 1; + } + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + if(part->mtdflags_mask) + { + // Append the name + if(needs_semicolon) + p = stpncpy(p, ";", maxlen - (p - buf)); + p = stpncpy(p, part->name, maxlen - (p - buf)); + p = stpncpy(p, "=", maxlen - (p - buf)); + p = stpncpy(p, mtdflags_tostring(part->mtdflags_mask, 0), maxlen - (p - buf)); + needs_semicolon=1; + } + } + } + + /* we still have at least one char left, as we decremented maxlen at + * the begining */ + *p = '\0'; + + return 0; +} + + +static int generate_mtdflags_save(char *buf, u32 buflen) +{ + int ret; + + ret = generate_mtdflags(buf, buflen); + + if ((buf[0] != '\0') && (ret == 0)) + setenv("mtdflags", buf); + else + setenv("mtdflags", NULL); + + return ret; +} + #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) /** * Get the net size (w/o bad blocks) of the given partition. @@ -1277,7 +1363,7 @@ static void print_partition_table(void) printf("\ndevice %s%d <%s>, # parts = %d\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id, dev->num_parts); - printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n"); + printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\tnand_flags\n"); list_for_each(pentry, &dev->parts) { u32 net_size; @@ -1286,21 +1372,24 @@ static void print_partition_table(void) part = list_entry(pentry, struct part_info, link); net_size = net_part_size(mtd, part); size_note = part->size == net_size ? " " : " (!)"; - printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n", + printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\t\t%s\n", part_num, part->name, part->size, net_size, size_note, part->offset, - part->mask_flags); + part->mask_flags, + mtdflags_tostring(part->mtdflags_mask, 1) + ); #else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ printf("\ndevice %s%d <%s>, # parts = %d\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id, dev->num_parts); - printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n"); + printf(" #: name\t\tsize\t\toffset\t\tmask_flags\tnand_flags\n"); list_for_each(pentry, &dev->parts) { part = list_entry(pentry, struct part_info, link); - printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", + printf("%2d: %-20s0x%08x\t0x%08x\t%d\t\t%s\n", part_num, part->name, part->size, - part->offset, part->mask_flags); + part->offset, part->mask_flags, + mtdflags_tostring(part->mtdflags_mask, 1)); #endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ part_num++; } @@ -1317,7 +1406,7 @@ static void print_partition_table(void) static void list_partitions(void) { struct part_info *part; - char *mtdids_default, *mtdparts_default; + char *mtdids_default, *mtdparts_default, *mtdflags_default; debug("\n---list_partitions---\n"); print_partition_table(); @@ -1345,9 +1434,12 @@ static void list_partitions(void) * printbuffer. Use puts() to prevent system crashes. */ mtdparts_default = get_mtdparts_default(); - puts("mtdparts: "); - puts(mtdparts_default ? mtdparts_default : "none"); - puts("\n"); + printf("mtdparts: %s\n", + mtdparts_default ? mtdparts_default : "none"); + + mtdflags_default = get_mtdflags_default(); + printf("mtdflags: %s\n", + mtdflags_default ? mtdflags_default : "none"); } /** @@ -1432,6 +1524,7 @@ static int delete_partition(const char *id) u8 pnum; struct mtd_device *dev; struct part_info *part; + int ret; if (find_dev_and_part(id, &dev, &pnum, &part, 0) == 0) { @@ -1443,10 +1536,14 @@ static int delete_partition(const char *id) return 1; if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + ret = 1; printf("generated mtdparts too long, reseting to null\n"); - return 1; } - return 0; + if (generate_mtdflags_save(last_flags, ARRAY_SIZE(last_flags)) != 0) { + ret = 1; + printf("generated mtdflags too long, resetting to null\n"); + } + return ret; } printf("partition %s not found\n", id); @@ -1511,6 +1608,7 @@ static int spread_partitions(void) struct mtd_info *mtd; int part_num; uint64_t cur_offs; + int ret = 0; list_for_each(dentry, &devices) { dev = list_entry(dentry, struct mtd_device, link); @@ -1541,10 +1639,14 @@ static int spread_partitions(void) index_partitions(); if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + ret = 1; printf("generated mtdparts too long, reseting to null\n"); - return 1; } - return 0; + if (generate_mtdflags_save(last_flags, ARRAY_SIZE(last_flags)) != 0) { + ret = 1; + printf("generated mtdflags too long, resetting to null\n"); + } + return ret; } #endif /* CONFIG_CMD_MTDPARTS_SPREAD */ @@ -1604,6 +1706,160 @@ static int parse_mtdparts(const char *const mtdparts) return 0; } +/* + * mtdflags - partitionid1=flag1,flag2,flag3;partitionid2=flag1,flag2,flag3;... + + +setenv mtdflags 'default=ecc_chip;x-loader=ecc_hw,repeat;system=yaffs;userdata=yaffs;cache=yaffs' +mtdparts ecc x-loader + + */ +const char *const mtdflags_strings[] = { + [MTDFLAGS_ECC_SW] = "ecc_sw", + [MTDFLAGS_ECC_HW] = "ecc_hw", + [MTDFLAGS_ECC_CHIP] = "ecc_chip", + [MTDFLAGS_ECC_BCH] = "ecc_bch", + [MTDFLAGS_YAFFS] = "yaffs", + [MTDFLAGS_REPEAT] = "repeat", +}; + +static int validate_mtdflags(int mask) +{ + int ecccnt = 0; + if((1 << MTDFLAGS_ECC_SW) & mask) + ecccnt++; + if((1 << MTDFLAGS_ECC_HW) & mask) + ecccnt++; + if((1 << MTDFLAGS_ECC_CHIP) & mask) + ecccnt++; + if((1 << MTDFLAGS_ECC_BCH) & mask) + ecccnt++; + + return ecccnt <= 1; +} + +static int parse_mtdflags_findmask(const char *const flags, int len) +{ + int i; + + for(i=0;i<ARRAY_SIZE(mtdflags_strings);++i) + { + if(mtdflags_strings[i] && + strlen(mtdflags_strings[i]) == len && + strncmp(mtdflags_strings[i], flags, len) == 0) + { + return 1 << i; + } + } + return 0; +} + +static char *mtdflags_tostring(int flags, int include_defaults) +{ + static char str[64]; + char *cur = str; + int i; + int comma = 0; + int from_common = mtdflags_defaultmask; + + if(flags & MTDFLAGS_DEFAULT_PERMITTED) + { + from_common = 0; + } + + str[0] = '\0'; + + for(i=0;i < ARRAY_SIZE(mtdflags_strings);++i) + { + if(include_defaults && ((1 << i) & from_common)) + { + if(comma) + cur = stpncpy(cur, ",", ARRAY_SIZE(str) - (cur - str)); + cur = stpncpy(cur, "(", ARRAY_SIZE(str) - (cur - str)); + cur = stpncpy(cur, mtdflags_strings[i], ARRAY_SIZE(str) - (cur - str)); + cur = stpncpy(cur, ")", ARRAY_SIZE(str) - (cur - str)); + comma=1; + } + if((1 << i) & flags) + { + if(comma) + cur = stpncpy(cur, ",", ARRAY_SIZE(str) - (cur - str)); + cur = stpncpy(cur, mtdflags_strings[i], ARRAY_SIZE(str) - (cur - str)); + comma=1; + } + } + str[ARRAY_SIZE(str) - 1] = '\0'; + return str; +} + +static int parse_mtdflags(const char *const mtdflags) +{ + const char *part, *part_end; + const char *flags, *flags_end; + + if(!mtdflags) + return 1; + + part = flags_end = mtdflags; + + while(*part != '\0') + { + // Bring the pointer "part" past all blank items + for(part=flags_end;*part && (*part == ';' || *part == ' ');++part); + // Bring the pointer "flags" past the equals sign + for(part_end=part;*part_end && *part_end != '=';++part_end); + for(flags=part_end;*flags && *flags == '=';++flags); + // Bring the pointer "p" to the next partition + for(flags_end=flags;*flags_end && *flags_end != ';';++flags_end); + + // Now, make sure it's sane + if(part != part_end && flags != flags_end && part != flags_end) + { + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *parti; + int mask = 0; + + while(flags < flags_end) + { + const char *cur = flags; + + for(;flags < flags_end && *flags != ',';++flags); + mask |= parse_mtdflags_findmask(cur, flags - cur); + for(;flags < flags_end && *flags == ',';++flags); + } + if(!validate_mtdflags(mask)) + { + return 1; + } + if(strncmp(part, "default", 7) == 0) + { + mtdflags_defaultmask = mask & MTDFLAGS_DEFAULT_PERMITTED; + continue; + } + + list_for_each(dentry, &devices) + { + dev = list_entry(dentry, struct mtd_device, link); + list_for_each(pentry, &dev->parts) + { + parti = list_entry(pentry, struct part_info, link); + if(strlen(parti->name) == part_end - part && + strncmp(parti->name, part, part_end - part) == 0) + { + parti->mtdflags_mask = mask; + goto cont; + } + } + } + return 1; + cont: + continue; + } + } + return 0; +} + /** * Parse provided string describing mtdids mapping (see file header for mtdids * variable format). Allocate memory for each entry and add all found entries @@ -1724,11 +1980,11 @@ static int parse_mtdids(const char *const ids) int mtdparts_init(void) { static int initialized = 0; - const char *ids, *parts; + const char *ids, *parts, *flags; const char *current_partition; int ids_changed; char tmp_ep[PARTITION_MAXLEN]; - char *mtdids_default, *mtdparts_default; + char *mtdids_default; debug("\n---mtdparts_init---\n"); if (!initialized) { @@ -1743,6 +1999,7 @@ int mtdparts_init(void) /* get variables */ ids = getenv("mtdids"); parts = getenv("mtdparts"); + flags = getenv("mtdflags"); current_partition = getenv("partition"); /* save it for later parsing, cannot rely on current partition pointer @@ -1802,10 +2059,19 @@ int mtdparts_init(void) } /* parse partitions if either mtdparts or mtdids were updated */ - if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) { + if (parts && (last_parts[0] == '\0' || + strcmp(last_parts, parts) != 0 || + ids_changed || + (flags && strcmp(last_flags, flags) != 0))) { if (parse_mtdparts(parts) != 0) return 1; + if(flags && parse_mtdflags(flags)) + { + printf("Bad mtdflags.\n"); + return 1; + } + if (list_empty(&devices)) { printf("mtdparts_init: no valid partitions\n"); return 1; @@ -1813,6 +2079,10 @@ int mtdparts_init(void) /* ok it's good, save new parts */ strncpy(last_parts, parts, MTDPARTS_MAXLEN); + if(flags) + strncpy(last_flags, flags, MTDPARTS_MAXLEN); + else + last_flags[0] = '\0'; /* reset first partition from first dev from the list as current */ current_mtd_dev = list_entry(devices.next, struct mtd_device, link); @@ -1853,6 +2123,14 @@ int mtdparts_init(void) return 0; } +int mtd_part_getmask(struct part_info *part) +{ + if(part->mtdflags_mask & MTDFLAGS_DEFAULT_PERMITTED) + return part->mtdflags_mask; + else + return part->mtdflags_mask | mtdflags_defaultmask; +} + /** * Return pointer to the partition of a requested number from a requested * device. @@ -1948,14 +2226,17 @@ int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) */ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *mtdids_default, *mtdparts_default; + char *mtdids_default, *mtdparts_default, *mtdflags_default; + int ret = 0; if (argc == 2) { if (strcmp(argv[1], "default") == 0) { mtdids_default = get_mtdids_default(); mtdparts_default = get_mtdparts_default(); + mtdflags_default = get_mtdflags_default(); setenv("mtdids", (char *)mtdids_default); setenv("mtdparts", (char *)mtdparts_default); + setenv("mtdflags", (char *)mtdflags_default); setenv("partition", NULL); mtdparts_init(); @@ -2044,11 +2325,15 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + ret = 1; printf("generated mtdparts too long, reseting to null\n"); - return 1; + } + if (generate_mtdflags_save(last_flags, ARRAY_SIZE(last_flags)) != 0) { + ret = 1; + printf("generated mtdflags too long, resetting to null\n"); } - return 0; + return ret; } /* mtdparts del part-id */ @@ -2119,6 +2404,17 @@ U_BOOT_CMD( "<offset> := partition start offset within the device\n" "<name> := '(' NAME ')'\n" "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)" + "mtdflags=<flag-def>[;<flag-def>...]\n\n" + "<flag-def> := <part-def>=<flag>[,<flag>...]\n" + "Flags affecting ECC modes (only applicable when \"nandecc auto\" is selected):\n" + " ecc_sw = Select SW ECC for NAND writes\n" + " ecc_hw = Select HW ECC for NAND writes\n" + " ecc_chip = Select ON-Chip ECC for NAND writes\n" + " ecc_bch = Select SW BCH ECC for NAND writes\n" + "Flags affecting nand write.auto\n" + " <blank> = Use nand write\n" + " yaffs = Use nand write.yaffs\n" + " repeat = Use nand.write.repeat\n" ); /***************************************************/ diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 9b7a0a73e3a..509c85e3036 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -28,10 +28,74 @@ #include <jffs2/jffs2.h> #include <nand.h> +#include <asm/arch-omap3/sys_proto.h> + #if defined(CONFIG_CMD_MTDPARTS) #include "mtd_parts.h" #endif +extern int omap_nand_ecc_auto; + +static int switch_ecc_bypart(const char *id) +{ + struct mtd_info *mtd_info; + struct nand_chip *nand; + struct mtd_device *mtd; + struct part_info *part; + int flags = 0; + u8 part_num; + + if(mtdparts_init() == 0 && find_dev_and_part(id, &mtd, &part_num, &part, 0) == 0) + { + if(mtd->id->type != MTD_DEV_TYPE_NAND) + { + printf("Automatic ECC selection only works against NAND partitions.\n"); + return -1; + } + + if(mtd->id->num < 0 || + mtd->id->num >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) + { + printf("\nNo NAND device found\n"); + return -1; + } + + // Use the default flags if needed for decisions below + flags = mtd_part_getmask(part); + mtd_info = &nand_info[mtd->id->num]; + nand = mtd_info->priv; + + if(flags & (1 << MTDFLAGS_ECC_SW)) + { + if(nand->ecc.mode != NAND_ECC_SOFT) + omap_nand_switch_ecc(OMAP_ECC_SOFT); + } else if(flags & (1 << MTDFLAGS_ECC_HW)) + { + if(nand->ecc.mode != NAND_ECC_HW) + omap_nand_switch_ecc(OMAP_ECC_HW); + } else if(flags & (1 << MTDFLAGS_ECC_CHIP)) + { + if(!nand->has_chip_ecc) + { + printf("NAND Chip doesn't support in-chip ECC\n"); + return -1; + } + if(nand->ecc.mode != NAND_ECC_CHIP) + omap_nand_switch_ecc(OMAP_ECC_CHIP); + printf("Here\n"); + } else if(flags & (1 << MTDFLAGS_ECC_BCH)) + { + if(nand->ecc.mode != NAND_ECC_SOFT_BCH) + omap_nand_switch_ecc(OMAP_ECC_SOFT_BCH); + } else { + printf("Unknown ECC mode for %s\n", id); + } + } + + return flags; +} + static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) { int i; @@ -182,10 +246,13 @@ static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize) } static int arg_off_size(int argc, char *const argv[], int *idx, - loff_t *off, loff_t *size) + loff_t *off, loff_t *size, loff_t *maxsize) { + loff_t int_maxsize; int ret; - loff_t maxsize; + + if(maxsize == NULL) + maxsize = &int_maxsize; if (argc == 0) { *off = 0; @@ -193,12 +260,12 @@ static int arg_off_size(int argc, char *const argv[], int *idx, goto print; } - ret = arg_off(argv[0], idx, off, &maxsize); + ret = arg_off(argv[0], idx, off, maxsize); if (ret) return ret; if (argc == 1) { - *size = maxsize; + *size = *maxsize; goto print; } @@ -207,7 +274,7 @@ static int arg_off_size(int argc, char *const argv[], int *idx, return -1; } - if (*size > maxsize) { + if (*size > *maxsize) { puts("Size exceeds partition or device limit\n"); return -1; } @@ -450,6 +517,11 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) return 0; } + if (strcmp(cmd, "ecc") == 0 && argc == 3) { + switch_ecc_bypart(argv[2]); + return 0; + } + /* * Syntax is: * 0 1 2 3 4 @@ -490,7 +562,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) printf("\nNAND %s: ", cmd); /* skip first two or three arguments, look for offset and size */ - if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0) + if (arg_off_size(argc - o, argv + o, &dev, &off, &size, NULL) != 0) return 1; nand = &nand_info[dev]; @@ -545,7 +617,9 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) } if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { + int rw_mode = 0; size_t rwsize; + loff_t maxsize; int read; if (argc < 4) @@ -555,14 +629,25 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); - if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size) != 0) + if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size, &maxsize) != 0) return 1; nand = &nand_info[dev]; rwsize = size; s = strchr(cmd, '.'); - if (!s || !strcmp(s, ".jffs2") || + if(s && !strcmp(s, ".auto")) + { + int flags = switch_ecc_bypart(argv[3]); + if(flags == -1) + return 1; + rw_mode = 1; + if((flags & (1 << MTDFLAGS_YAFFS)) && !read) + rw_mode = 2; + if((flags & (1 << MTDFLAGS_REPEAT)) && !read) + rw_mode = 3; + } + if (!s || rw_mode == 1 || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read) ret = nand_read_skip_bad(nand, off, &rwsize, @@ -571,11 +656,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) ret = nand_write_skip_bad(nand, off, &rwsize, (u_char *)addr, 0); #ifdef CONFIG_CMD_NAND_YAFFS - } else if (!strcmp(s, ".yaffs")) { - if (read) { - printf("Unknown nand command suffix '%s'.\n", s); - return 1; - } + } else if (!read && (rw_mode == 2 || !strcmp(s, ".yaffs"))) { ret = nand_write_skip_bad(nand, off, &rwsize, (u_char *)addr, 1); #endif } else if (!strcmp(s, ".oob")) { @@ -590,6 +671,38 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) ret = nand->read_oob(nand, off, &ops); else ret = nand->write_oob(nand, off, &ops); + } else if (!read && (rw_mode == 3 || !strcmp(s, ".repeat"))) { + int off_max = off + maxsize; + loff_t tmp; + if (str2off(argv[3], &tmp)) + { + printf("nand write.repeat does _NOT_ support nand offsets.\n"); + printf("(e.g. you MUST use a partition name)\n"); + return 0; + } + if ((off & (nand->writesize - 1)) != 0) { + ret = 1; + } else { + if(size > nand->erasesize) + { + printf("Cannot write larger than block size for write.repeat (%i > %i)\n", + (unsigned int)size, nand->erasesize); + return 1; + } + printf("\n"); + for(;off < off_max;off += nand->erasesize) + { + rwsize = size; + if(!nand_block_isbad(nand, off)) + { + printf("Writing %i bytes to %x ", rwsize, (unsigned int)off); + ret = nand_write(nand, off, &rwsize, (u_char *)addr); + printf("(wrote %i bytes)\n", (unsigned int)rwsize); + } else { + printf("Skipping bad block %x\n", (unsigned int)off); + } + } + } } else { printf("Unknown nand command suffix '%s'.\n", s); return 1; @@ -667,7 +780,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) } if (strcmp(cmd, "unlock") == 0) { - if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size) < 0) + if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size, NULL) < 0) return 1; if (!nand_unlock(&nand_info[dev], off, size)) { @@ -699,6 +812,10 @@ U_BOOT_CMD( " write 'size' bytes starting at offset 'off' with yaffs format\n" " from memory address 'addr', skipping bad blocks.\n" #endif + "nand write.repeat - addr off|partition size\n" + " write 'size' bytes starting at offset 'off'\n" + " from memory address 'addr', repeating the write for\n" + " every block in the partition\n" "nand erase[.spread] [clean] off size - erase 'size' bytes " "from offset 'off'\n" " With '.spread', erase enough for given file size, otherwise,\n" @@ -728,6 +845,10 @@ U_BOOT_CMD( "\n" "nand debug [level] - display or set the MTD debug level" #endif + "nand write.auto addr partition [size]\n" + " write 'size' bytes to partition. Auto-picks ECC and\n" + " write.yaffs or write.repeat variants based on mtdflags\n" + " for the given partition.\n" ); static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, diff --git a/common/lcd.c b/common/lcd.c index 85a946828e5..3b89d4e0ef9 100644 --- a/common/lcd.c +++ b/common/lcd.c @@ -1227,7 +1227,7 @@ void lcd_percent_update(int size) } } -static int do_lcd_percent (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +int do_lcd_percent (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { if (argc > 1) strncpy(percent_data.string, argv[1], sizeof(percent_data.string) - 1); |