Nicolas Pitre nico at cam.org don't ignore bad options passed to the "nand write...
[fw/openocd] / src / flash / nand.c
index 3c00db1dd53460f973e8151d7eb495f72b76e908..8708ef3c1b58a8d78f692784b6f235eaeb847af1 100644 (file)
@@ -2,10 +2,8 @@
  *   Copyright (C) 2007 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *                                                                         *
- *   partially based on                                                    *
- *      drivers/mtd/nand_ids.c                                                *
- *                                                                         *
- *   Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)               *
+ *   Partially based on drivers/mtd/nand_ids.c from Linux.                 *
+ *   Copyright (C) 2002 Thomas Gleixner <tglx@linutronix.de>               *
  *                                                                         *
  *   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  *
@@ -195,7 +193,7 @@ int handle_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, cha
                
        if (argc < 1)
        {
-               WARNING("incomplete flash device nand configuration");
+               LOG_WARNING("incomplete flash device nand configuration");
                return ERROR_FLASH_BANK_INVALID;
        }
        
@@ -206,10 +204,10 @@ int handle_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, cha
                if (strcmp(args[0], nand_flash_controllers[i]->name) == 0)
                {
                        /* register flash specific commands */
-                       if (nand_flash_controllers[i]->register_commands(cmd_ctx) != ERROR_OK)
+                       if ((retval = nand_flash_controllers[i]->register_commands(cmd_ctx)) != ERROR_OK)
                        {
-                               ERROR("couldn't register '%s' commands", args[0]);
-                               exit(-1);
+                               LOG_ERROR("couldn't register '%s' commands", args[0]);
+                               return retval;
                        }
        
                        c = malloc(sizeof(nand_device_t));
@@ -226,7 +224,7 @@ int handle_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, cha
 
                        if ((retval = nand_flash_controllers[i]->nand_device_command(cmd_ctx, cmd, args, argc, c)) != ERROR_OK)
                        {
-                               ERROR("'%s' driver rejected nand flash", c->controller->name);
+                               LOG_ERROR("'%s' driver rejected nand flash", c->controller->name);
                                free(c);
                                return ERROR_OK;
                        }
@@ -251,11 +249,11 @@ int handle_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, cha
        /* no valid NAND controller was found (i.e. the configuration option,
         * didn't match one of the compiled-in controllers)
         */
-       ERROR("No valid NAND flash controller found (%s)", args[0]);
-       ERROR("compiled-in NAND flash controllers:");
+       LOG_ERROR("No valid NAND flash controller found (%s)", args[0]);
+       LOG_ERROR("compiled-in NAND flash controllers:");
        for (i = 0; nand_flash_controllers[i]; i++)
        {
-               ERROR("%i: %s", i, nand_flash_controllers[i]->name);
+               LOG_ERROR("%i: %s", i, nand_flash_controllers[i]->name);
        }
        
        return ERROR_OK;
@@ -289,7 +287,7 @@ int nand_init(struct command_context_s *cmd_ctx)
                register_command(cmd_ctx, nand_cmd, "dump", handle_nand_dump_command, COMMAND_EXEC,
                                                 "dump from NAND flash device <num> <filename> <offset> <size> [options]");
                register_command(cmd_ctx, nand_cmd, "write", handle_nand_write_command, COMMAND_EXEC,
-                                                "write to NAND flash device <num> <filename> <offset> [options]");
+                                                "write to NAND flash device <num> <filename> <offset> [oob_raw|oob_only]");
                register_command(cmd_ctx, nand_cmd, "raw_access", handle_nand_raw_access_command, COMMAND_EXEC,
                                                 "raw access to NAND flash device <num> ['enable'|'disable']");
        }
@@ -335,7 +333,7 @@ int nand_build_bbt(struct nand_device_s *device, int first, int last)
                        || (((device->page_size == 512) && (oob[5] != 0xff)) ||
                                ((device->page_size == 2048) && (oob[0] != 0xff))))
                {
-                       WARNING("invalid block: %i", i);
+                       LOG_WARNING("invalid block: %i", i);
                        device->blocks[i].is_bad = 1;
                }
                else
@@ -357,7 +355,7 @@ int nand_read_status(struct nand_device_s *device, u8 *status)
        /* Send read status command */
        device->controller->command(device, NAND_CMD_STATUS);
        
-       usleep(1000);
+       alive_sleep(1);
        
        /* read status */
        if (device->device->options & NAND_BUSWIDTH_16)
@@ -377,6 +375,7 @@ int nand_read_status(struct nand_device_s *device, u8 *status)
 int nand_probe(struct nand_device_s *device)
 {
        u8 manufacturer_id, device_id;
+       u8 id_buff[6];
        int retval;
        int i;
 
@@ -396,13 +395,13 @@ int nand_probe(struct nand_device_s *device)
                switch (retval)
                {
                        case ERROR_NAND_OPERATION_FAILED:
-                               DEBUG("controller initialization failed");
+                               LOG_DEBUG("controller initialization failed");
                                return ERROR_NAND_OPERATION_FAILED;
                        case ERROR_NAND_OPERATION_NOT_SUPPORTED:
-                               ERROR("BUG: controller reported that it doesn't support default parameters");
+                               LOG_ERROR("BUG: controller reported that it doesn't support default parameters");
                                return ERROR_NAND_OPERATION_FAILED;
                        default:
-                               ERROR("BUG: unknown controller initialization failure");
+                               LOG_ERROR("BUG: unknown controller initialization failure");
                                return ERROR_NAND_OPERATION_FAILED;
                }
        }
@@ -453,12 +452,12 @@ int nand_probe(struct nand_device_s *device)
        
        if (!device->device)
        {
-               ERROR("unknown NAND flash device found, manufacturer id: 0x%2.2x device id: 0x%2.2x",
+               LOG_ERROR("unknown NAND flash device found, manufacturer id: 0x%2.2x device id: 0x%2.2x",
                        manufacturer_id, device_id);
                return ERROR_NAND_OPERATION_FAILED;
        }
        
-       DEBUG("found %s (%s)", device->device->name, device->manufacturer->name);
+       LOG_DEBUG("found %s (%s)", device->device->name, device->manufacturer->name);
        
        /* initialize device parameters */
        
@@ -467,16 +466,40 @@ int nand_probe(struct nand_device_s *device)
                device->bus_width = 16;
        else
                device->bus_width = 8;
+
+       /* Do we need extended device probe information? */
+       if (device->device->page_size == 0 ||
+           device->device->erase_size == 0)
+       {
+               if (device->bus_width == 8)
+               {
+                       device->controller->read_data(device, id_buff+3);
+                       device->controller->read_data(device, id_buff+4);
+                       device->controller->read_data(device, id_buff+5);
+               }
+               else
+               {
+                       u16 data_buf;
+
+                       device->controller->read_data(device, &data_buf);
+                       id_buff[3] = data_buf;
+
+                       device->controller->read_data(device, &data_buf);
+                       id_buff[4] = data_buf;
+
+                       device->controller->read_data(device, &data_buf);
+                       id_buff[5] = data_buf >> 8;
+               }
+       }
                
        /* page size */
        if (device->device->page_size == 0)
        {
-               /* TODO: support reading extended chip id to determine page size */
-               return ERROR_NAND_OPERATION_FAILED;
+               device->page_size = 1 << (10 + (id_buff[4] & 3));
        }
        else if (device->device->page_size == 256)
        {
-               ERROR("NAND flashes with 256 byte pagesize are not supported");
+               LOG_ERROR("NAND flashes with 256 byte pagesize are not supported");
                return ERROR_NAND_OPERATION_FAILED;
        }
        else
@@ -494,7 +517,7 @@ int nand_probe(struct nand_device_s *device)
                        device->address_cycles = 4;
                else
                {
-                       ERROR("BUG: small page NAND device with more than 8 GiB encountered");
+                       LOG_ERROR("BUG: small page NAND device with more than 8 GiB encountered");
                        device->address_cycles = 5;
                }
        }
@@ -507,7 +530,7 @@ int nand_probe(struct nand_device_s *device)
                        device->address_cycles = 5;
                else
                {
-                       ERROR("BUG: small page NAND device with more than 32 GiB encountered");
+                       LOG_ERROR("BUG: large page NAND device with more than 32 GiB encountered");
                        device->address_cycles = 6;
                }
        }
@@ -515,7 +538,20 @@ int nand_probe(struct nand_device_s *device)
        /* erase size */
        if (device->device->erase_size == 0)
        {
-               /* TODO: support reading extended chip id to determine erase size */
+               switch ((id_buff[4] >> 4) & 3) {
+               case 0:
+                       device->erase_size = 64 << 10;
+                       break;
+               case 1:
+                       device->erase_size = 128 << 10;
+                       break;
+               case 2:
+                       device->erase_size = 256 << 10;
+                       break;
+               case 3:
+                       device->erase_size =512 << 10;
+                       break;
+               }
        }
        else
        {
@@ -528,14 +564,14 @@ int nand_probe(struct nand_device_s *device)
                switch (retval)
                {
                        case ERROR_NAND_OPERATION_FAILED:
-                               DEBUG("controller initialization failed");
+                               LOG_DEBUG("controller initialization failed");
                                return ERROR_NAND_OPERATION_FAILED;
                        case ERROR_NAND_OPERATION_NOT_SUPPORTED:
-                               ERROR("controller doesn't support requested parameters (buswidth: %i, address cycles: %i, page size: %i)",
+                               LOG_ERROR("controller doesn't support requested parameters (buswidth: %i, address cycles: %i, page size: %i)",
                                        device->bus_width, device->address_cycles, device->page_size);
                                return ERROR_NAND_OPERATION_FAILED;
                        default:
-                               ERROR("BUG: unknown controller initialization failure");
+                               LOG_ERROR("BUG: unknown controller initialization failure");
                                return ERROR_NAND_OPERATION_FAILED;
                }
        }
@@ -615,19 +651,19 @@ int nand_erase(struct nand_device_s *device, int first_block, int last_block)
                
                if (!device->controller->nand_ready(device, 1000))
                {
-                       ERROR("timeout waiting for NAND flash block erase to complete");
+                       LOG_ERROR("timeout waiting for NAND flash block erase to complete");
                        return ERROR_NAND_OPERATION_TIMEOUT;
                }
                
                if ((retval = nand_read_status(device, &status)) != ERROR_OK)
                {
-                       ERROR("couldn't read status");
+                       LOG_ERROR("couldn't read status");
                        return ERROR_NAND_OPERATION_FAILED;
                }
                
                if (status & 0x1)
                {
-                       ERROR("erase operation didn't pass, status: 0x%2.2x", status);
+                       LOG_ERROR("erase operation didn't pass, status: 0x%2.2x", status);
                        return ERROR_NAND_OPERATION_FAILED;
                }
        }
@@ -644,7 +680,7 @@ int nand_read_plain(struct nand_device_s *device, u32 address, u8 *data, u32 dat
                
        if (address % device->page_size)
        {
-               ERROR("reads need to be page aligned");
+               LOG_ERROR("reads need to be page aligned");
                return ERROR_NAND_OPERATION_FAILED;
        }
        
@@ -681,7 +717,7 @@ int nand_write_plain(struct nand_device_s *device, u32 address, u8 *data, u32 da
                
        if (address % device->page_size)
        {
-               ERROR("writes need to be page aligned");
+               LOG_ERROR("writes need to be page aligned");
                return ERROR_NAND_OPERATION_FAILED;
        }
        
@@ -770,7 +806,10 @@ int nand_read_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 dat
                 * or 2048 for the beginning of OOB area)
                 */
                device->controller->address(device, 0x0);
-               device->controller->address(device, 0x8);
+               if (data)
+                       device->controller->address(device, 0x0);
+               else
+                       device->controller->address(device, 0x8);
                
                /* row */
                device->controller->address(device, page & 0xff);
@@ -872,7 +911,10 @@ int nand_write_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 da
                 * or 2048 for the beginning of OOB area)
                 */
                device->controller->address(device, 0x0);
-               device->controller->address(device, 0x8);
+               if (data)
+                       device->controller->address(device, 0x0);
+               else
+                       device->controller->address(device, 0x8);
                
                /* row */
                device->controller->address(device, page & 0xff);
@@ -940,13 +982,13 @@ int nand_write_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 da
        
        if ((retval = nand_read_status(device, &status)) != ERROR_OK)
        {
-               ERROR("couldn't read status");
+               LOG_ERROR("couldn't read status");
                return ERROR_NAND_OPERATION_FAILED;
        }
                
        if (status & NAND_STATUS_FAIL)
        {
-               ERROR("write operation didn't pass, status: 0x%2.2x", status);
+               LOG_ERROR("write operation didn't pass, status: 0x%2.2x", status);
                return ERROR_NAND_OPERATION_FAILED;
        }
        
@@ -986,8 +1028,8 @@ int handle_nand_info_command(struct command_context_s *cmd_ctx, char *cmd, char
                
        if ((argc < 1) || (argc > 3))
        {
-               command_print(cmd_ctx, "usage: nand info <num> [<first> <last>]");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        }
        
        if (argc == 2)
@@ -1053,8 +1095,7 @@ int handle_nand_probe_command(struct command_context_s *cmd_ctx, char *cmd, char
                
        if (argc != 1)
        {
-               command_print(cmd_ctx, "usage: nand probe <num>");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
        }
        
        p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
@@ -1088,8 +1129,8 @@ int handle_nand_erase_command(struct command_context_s *cmd_ctx, char *cmd, char
                
        if (argc != 3)
        {
-               command_print(cmd_ctx, "usage: nand erase <num> <first> <last>");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        }
        
        p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
@@ -1128,8 +1169,8 @@ int handle_nand_check_bad_blocks_command(struct command_context_s *cmd_ctx, char
                
        if ((argc < 1) || (argc > 3) || (argc == 2))
        {
-               command_print(cmd_ctx, "usage: nand check_bad_blocks <num> [<first> <last>]");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        }
        
        if (argc == 3)
@@ -1168,8 +1209,8 @@ int handle_nand_copy_command(struct command_context_s *cmd_ctx, char *cmd, char
                
        if (argc != 4)
        {
-               command_print(cmd_ctx, "usage: nand copy <num> <offset> <length> <ram-address>");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        }
        
        p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
@@ -1201,8 +1242,8 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
                
        if (argc < 3)
        {
-               command_print(cmd_ctx, "usage: nand write <num> <file> <offset> [options]");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        }
        
        p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
@@ -1213,7 +1254,6 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
                u8 *oob = NULL;
                u32 oob_size = 0;
                        
-               duration_start_measure(&duration);
                offset = strtoul(args[2], NULL, 0);
                
                if (argc > 3)
@@ -1228,13 +1268,15 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
                                else
                                {
                                        command_print(cmd_ctx, "unknown option: %s", args[i]);
+                                       return ERROR_COMMAND_SYNTAX_ERROR;
                                }
                        }
                }
                
+               duration_start_measure(&duration);
+
                if (fileio_open(&fileio, args[1], FILEIO_READ, FILEIO_BINARY) != ERROR_OK)
                {
-                       command_print(cmd_ctx, "file open error: %s", fileio.error_str);
                        return ERROR_OK;
                }
        
@@ -1258,6 +1300,9 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
                if (offset % p->page_size)
                {
                        command_print(cmd_ctx, "only page size aligned offsets and sizes are supported");
+                       fileio_close(&fileio);
+                       free(oob);
+                       free(page);
                        return ERROR_OK;
                }
                
@@ -1265,7 +1310,7 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
                {
                        u32 size_read;
                        
-                       if (page)
+                       if (NULL != page)
                        {
                                fileio_read(&fileio, page_size, page, &size_read);
                                buf_cnt -= size_read;
@@ -1275,7 +1320,7 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
                                }
                        }
                                
-                       if (oob)
+                       if (NULL != oob)
                        {
                                fileio_read(&fileio, oob_size, oob, &size_read);
                                buf_cnt -= size_read;
@@ -1289,17 +1334,26 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
                        {
                                command_print(cmd_ctx, "failed writing file %s to NAND flash %s at offset 0x%8.8x",
                                        args[1], args[0], offset);
+
+                               fileio_close(&fileio);
+                               free(oob);
+                               free(page);
+
                                return ERROR_OK;
                        }
                        offset += page_size;
                }
 
                fileio_close(&fileio);
-               
+               free(oob);
+               free(page);
+               oob = NULL;
+               page = NULL;
                duration_stop_measure(&duration, &duration_text);
-               command_print(cmd_ctx, "wrote file %s to NAND flash %s at offset 0x%8.8x in %s",
+               command_print(cmd_ctx, "wrote file %s to NAND flash %s up to offset 0x%8.8x in %s",
                        args[1], args[0], offset, duration_text);
                free(duration_text);
+               duration_text = NULL;
        }
        else
        {
@@ -1315,8 +1369,7 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
                        
        if (argc < 4)
        {
-               command_print(cmd_ctx, "usage: nand dump <num> <filename> <address> <size> [options]");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
        }
        
        p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
@@ -1375,7 +1428,6 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
                        
                        if (fileio_open(&fileio, args[1], FILEIO_WRITE, FILEIO_BINARY) != ERROR_OK)
                        {
-                               command_print(cmd_ctx, "dump_image error: %s", fileio.error_str);
                                return ERROR_OK;
                        }
        
@@ -1387,16 +1439,19 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
                                if ((retval = nand_read_page(p, address / p->page_size, page, page_size, oob, oob_size)) != ERROR_OK)
                                {
                                        command_print(cmd_ctx, "reading NAND flash page failed");
+                                       free(page);
+                                       free(oob);                                                              
+                                       fileio_close(&fileio);
                                        return ERROR_OK;
                                }
                                
-                               if (page)
+                               if (NULL != page)
                                {
                                        fileio_write(&fileio, page_size, page, &size_written);
                                        bytes_done += page_size;
                                }
                                        
-                               if (oob)
+                               if (NULL != oob)
                                {
                                        fileio_write(&fileio, oob_size, oob, &size_written);
                                        bytes_done += oob_size;
@@ -1406,17 +1461,16 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
                                address += p->page_size;
                        }
                        
-                       if (page)
-                               free(page);
-                               
-                       if (oob)
-                               free(oob);
-                       
+                       free(page);
+                       page = NULL;
+                       free(oob);
+                       oob = NULL;
                        fileio_close(&fileio);
 
                        duration_stop_measure(&duration, &duration_text);
                        command_print(cmd_ctx, "dumped %"PRIi64" byte in %s", fileio.size, duration_text);
                        free(duration_text);
+                       duration_text = NULL;
                }
                else
                {
@@ -1437,8 +1491,7 @@ int handle_nand_raw_access_command(struct command_context_s *cmd_ctx, char *cmd,
                
        if ((argc < 1) || (argc > 2))
        {
-               command_print(cmd_ctx, "usage: nand raw_access <num> ['enable'|'disable']");
-               return ERROR_OK;
+               return ERROR_COMMAND_SYNTAX_ERROR;
        }
        
        p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
@@ -1458,7 +1511,7 @@ int handle_nand_raw_access_command(struct command_context_s *cmd_ctx, char *cmd,
                                }
                                else
                                {
-                                       command_print(cmd_ctx, "usage: nand raw_access ['enable'|disable']");
+                                       return ERROR_COMMAND_SYNTAX_ERROR;
                                }
                        }
        
@@ -1476,4 +1529,3 @@ int handle_nand_raw_access_command(struct command_context_s *cmd_ctx, char *cmd,
        
        return ERROR_OK;
 }
-