Commit d9831893ab2efd3dd36f948da898c83f33c4992c
Exists in
master
and in
54 other branches
Merge with /home/sr/git/u-boot/nand-ladis
Showing 8 changed files Inline Diff
board/netstar/netstar.c
1 | /* | 1 | /* |
2 | * (C) Copyright 2005 2N TELEKOMUNIKACE, Ladislav Michl | 2 | * (C) Copyright 2005 2N TELEKOMUNIKACE, Ladislav Michl |
3 | * | 3 | * |
4 | * See file CREDITS for list of people who contributed to this | 4 | * See file CREDITS for list of people who contributed to this |
5 | * project. | 5 | * project. |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License as | 8 | * modify it under the terms of the GNU General Public License as |
9 | * published by the Free Software Foundation; either version 2 of | 9 | * published by the Free Software Foundation; either version 2 of |
10 | * the License, or (at your option) any later version. | 10 | * the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This program is distributed in the hope that it will be useful, | 12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. | 15 | * GNU General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU General Public License | 17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program; if not, write to the Free Software | 18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
20 | * MA 02111-1307 USA | 20 | * MA 02111-1307 USA |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <common.h> | 23 | #include <common.h> |
24 | 24 | ||
25 | DECLARE_GLOBAL_DATA_PTR; | 25 | DECLARE_GLOBAL_DATA_PTR; |
26 | 26 | ||
27 | int board_init(void) | 27 | int board_init(void) |
28 | { | 28 | { |
29 | /* arch number of NetStar board */ | 29 | /* arch number of NetStar board */ |
30 | gd->bd->bi_arch_number = 692; | 30 | gd->bd->bi_arch_number = MACH_TYPE_NETSTAR; |
31 | 31 | ||
32 | /* adress of boot parameters */ | 32 | /* adress of boot parameters */ |
33 | gd->bd->bi_boot_params = 0x10000100; | 33 | gd->bd->bi_boot_params = 0x10000100; |
34 | 34 | ||
35 | return 0; | 35 | return 0; |
36 | } | 36 | } |
37 | 37 | ||
38 | int dram_init(void) | 38 | int dram_init(void) |
39 | { | 39 | { |
40 | gd->bd->bi_dram[0].start = PHYS_SDRAM_1; | 40 | gd->bd->bi_dram[0].start = PHYS_SDRAM_1; |
41 | gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; | 41 | gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; |
42 | 42 | ||
43 | /* Take the Ethernet controller out of reset and wait | 43 | /* Take the Ethernet controller out of reset and wait |
44 | * for the EEPROM load to complete. */ | 44 | * for the EEPROM load to complete. */ |
45 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) |= 0x80; | 45 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) |= 0x80; |
46 | udelay(10); /* doesn't work before interrupt_init call */ | 46 | udelay(10); /* doesn't work before interrupt_init call */ |
47 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) &= ~0x80; | 47 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) &= ~0x80; |
48 | udelay(500); | 48 | udelay(500); |
49 | 49 | ||
50 | return 0; | 50 | return 0; |
51 | } | 51 | } |
52 | 52 | ||
53 | int misc_init_r(void) | 53 | int misc_init_r(void) |
54 | { | 54 | { |
55 | return 0; | 55 | return 0; |
56 | } | 56 | } |
57 | 57 | ||
58 | int board_late_init(void) | 58 | int board_late_init(void) |
59 | { | 59 | { |
60 | return 0; | 60 | return 0; |
61 | } | 61 | } |
62 | 62 |
board/voiceblue/voiceblue.c
1 | /* | 1 | /* |
2 | * (C) Copyright 2005 2N TELEKOMUNIKACE, Ladislav Michl | 2 | * (C) Copyright 2005 2N TELEKOMUNIKACE, Ladislav Michl |
3 | * | 3 | * |
4 | * See file CREDITS for list of people who contributed to this | 4 | * See file CREDITS for list of people who contributed to this |
5 | * project. | 5 | * project. |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License | 8 | * modify it under the terms of the GNU General Public License |
9 | * version 2 as published by the Free Software Foundation. | 9 | * version 2 as published by the Free Software Foundation. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
19 | * MA 02111-1307 USA | 19 | * MA 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <common.h> | 22 | #include <common.h> |
23 | 23 | ||
24 | DECLARE_GLOBAL_DATA_PTR; | 24 | DECLARE_GLOBAL_DATA_PTR; |
25 | 25 | ||
26 | int board_init(void) | 26 | int board_init(void) |
27 | { | 27 | { |
28 | *((volatile unsigned char *) VOICEBLUE_LED_REG) = 0xaa; | 28 | *((volatile unsigned char *) VOICEBLUE_LED_REG) = 0xaa; |
29 | 29 | ||
30 | /* arch number of VoiceBlue board */ | 30 | /* arch number of VoiceBlue board */ |
31 | /* TODO: use define from asm/mach-types.h */ | 31 | gd->bd->bi_arch_number = MACH_TYPE_VOICEBLUE; |
32 | gd->bd->bi_arch_number = 218; | ||
33 | 32 | ||
34 | /* adress of boot parameters */ | 33 | /* adress of boot parameters */ |
35 | gd->bd->bi_boot_params = 0x10000100; | 34 | gd->bd->bi_boot_params = 0x10000100; |
36 | 35 | ||
37 | return 0; | 36 | return 0; |
38 | } | 37 | } |
39 | 38 | ||
40 | int dram_init(void) | 39 | int dram_init(void) |
41 | { | 40 | { |
42 | *((volatile unsigned short *) VOICEBLUE_LED_REG) = 0xff; | 41 | *((volatile unsigned short *) VOICEBLUE_LED_REG) = 0xff; |
43 | 42 | ||
44 | /* Take the Ethernet controller out of reset and wait | 43 | /* Take the Ethernet controller out of reset and wait |
45 | * for the EEPROM load to complete. */ | 44 | * for the EEPROM load to complete. */ |
46 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) |= 0x80; | 45 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) |= 0x80; |
47 | udelay(10); /* doesn't work before interrupt_init call */ | 46 | udelay(10); /* doesn't work before interrupt_init call */ |
48 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) &= ~0x80; | 47 | *((volatile unsigned short *) GPIO_DATA_OUTPUT_REG) &= ~0x80; |
49 | udelay(500); | 48 | udelay(500); |
50 | 49 | ||
51 | gd->bd->bi_dram[0].start = PHYS_SDRAM_1; | 50 | gd->bd->bi_dram[0].start = PHYS_SDRAM_1; |
52 | gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; | 51 | gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; |
53 | 52 | ||
54 | return 0; | 53 | return 0; |
55 | } | 54 | } |
56 | 55 | ||
57 | int misc_init_r(void) | 56 | int misc_init_r(void) |
58 | { | 57 | { |
59 | *((volatile unsigned short *) VOICEBLUE_LED_REG) = 0x55; | 58 | *((volatile unsigned short *) VOICEBLUE_LED_REG) = 0x55; |
60 | 59 | ||
61 | return 0; | 60 | return 0; |
62 | } | 61 | } |
63 | 62 | ||
64 | int board_late_init(void) | 63 | int board_late_init(void) |
65 | { | 64 | { |
66 | *((volatile unsigned char *) VOICEBLUE_LED_REG) = 0x00; | 65 | *((volatile unsigned char *) VOICEBLUE_LED_REG) = 0x00; |
67 | 66 | ||
68 | return 0; | 67 | return 0; |
69 | } | 68 | } |
70 | 69 |
common/cmd_jffs2.c
1 | /* | 1 | /* |
2 | * (C) Copyright 2002 | 2 | * (C) Copyright 2002 |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | 3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
4 | * | 4 | * |
5 | * (C) Copyright 2002 | 5 | * (C) Copyright 2002 |
6 | * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de> | 6 | * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de> |
7 | * | 7 | * |
8 | * (C) Copyright 2003 | 8 | * (C) Copyright 2003 |
9 | * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> | 9 | * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> |
10 | * | 10 | * |
11 | * (C) Copyright 2005 | 11 | * (C) Copyright 2005 |
12 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | 12 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
13 | * | 13 | * |
14 | * Added support for reading flash partition table from environment. | 14 | * Added support for reading flash partition table from environment. |
15 | * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 | 15 | * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 |
16 | * kernel tree. | 16 | * kernel tree. |
17 | * | 17 | * |
18 | * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ | 18 | * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ |
19 | * Copyright 2002 SYSGO Real-Time Solutions GmbH | 19 | * Copyright 2002 SYSGO Real-Time Solutions GmbH |
20 | * | 20 | * |
21 | * See file CREDITS for list of people who contributed to this | 21 | * See file CREDITS for list of people who contributed to this |
22 | * project. | 22 | * project. |
23 | * | 23 | * |
24 | * This program is free software; you can redistribute it and/or | 24 | * This program is free software; you can redistribute it and/or |
25 | * modify it under the terms of the GNU General Public License as | 25 | * modify it under the terms of the GNU General Public License as |
26 | * published by the Free Software Foundation; either version 2 of | 26 | * published by the Free Software Foundation; either version 2 of |
27 | * the License, or (at your option) any later version. | 27 | * the License, or (at your option) any later version. |
28 | * | 28 | * |
29 | * This program is distributed in the hope that it will be useful, | 29 | * This program is distributed in the hope that it will be useful, |
30 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 30 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
32 | * GNU General Public License for more details. | 32 | * GNU General Public License for more details. |
33 | * | 33 | * |
34 | * You should have received a copy of the GNU General Public License | 34 | * You should have received a copy of the GNU General Public License |
35 | * along with this program; if not, write to the Free Software | 35 | * along with this program; if not, write to the Free Software |
36 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 36 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
37 | * MA 02111-1307 USA | 37 | * MA 02111-1307 USA |
38 | */ | 38 | */ |
39 | 39 | ||
40 | /* | 40 | /* |
41 | * Three environment variables are used by the parsing routines: | 41 | * Three environment variables are used by the parsing routines: |
42 | * | 42 | * |
43 | * 'partition' - keeps current partition identifier | 43 | * 'partition' - keeps current partition identifier |
44 | * | 44 | * |
45 | * partition := <part-id> | 45 | * partition := <part-id> |
46 | * <part-id> := <dev-id>,part_num | 46 | * <part-id> := <dev-id>,part_num |
47 | * | 47 | * |
48 | * | 48 | * |
49 | * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping | 49 | * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping |
50 | * | 50 | * |
51 | * mtdids=<idmap>[,<idmap>,...] | 51 | * mtdids=<idmap>[,<idmap>,...] |
52 | * | 52 | * |
53 | * <idmap> := <dev-id>=<mtd-id> | 53 | * <idmap> := <dev-id>=<mtd-id> |
54 | * <dev-id> := 'nand'|'nor'<dev-num> | 54 | * <dev-id> := 'nand'|'nor'<dev-num> |
55 | * <dev-num> := mtd device number, 0... | 55 | * <dev-num> := mtd device number, 0... |
56 | * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) | 56 | * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) |
57 | * | 57 | * |
58 | * | 58 | * |
59 | * 'mtdparts' - partition list | 59 | * 'mtdparts' - partition list |
60 | * | 60 | * |
61 | * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...] | 61 | * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...] |
62 | * | 62 | * |
63 | * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...] | 63 | * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...] |
64 | * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) | 64 | * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) |
65 | * <part-def> := <size>[@<offset>][<name>][<ro-flag>] | 65 | * <part-def> := <size>[@<offset>][<name>][<ro-flag>] |
66 | * <size> := standard linux memsize OR '-' to denote all remaining space | 66 | * <size> := standard linux memsize OR '-' to denote all remaining space |
67 | * <offset> := partition start offset within the device | 67 | * <offset> := partition start offset within the device |
68 | * <name> := '(' NAME ')' | 68 | * <name> := '(' NAME ')' |
69 | * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel) | 69 | * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel) |
70 | * | 70 | * |
71 | * Notes: | 71 | * Notes: |
72 | * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping | 72 | * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping |
73 | * - if the above variables are not set defaults for a given target are used | 73 | * - if the above variables are not set defaults for a given target are used |
74 | * | 74 | * |
75 | * Examples: | 75 | * Examples: |
76 | * | 76 | * |
77 | * 1 NOR Flash, with 1 single writable partition: | 77 | * 1 NOR Flash, with 1 single writable partition: |
78 | * mtdids=nor0=edb7312-nor | 78 | * mtdids=nor0=edb7312-nor |
79 | * mtdparts=mtdparts=edb7312-nor:- | 79 | * mtdparts=mtdparts=edb7312-nor:- |
80 | * | 80 | * |
81 | * 1 NOR Flash with 2 partitions, 1 NAND with one | 81 | * 1 NOR Flash with 2 partitions, 1 NAND with one |
82 | * mtdids=nor0=edb7312-nor,nand0=edb7312-nand | 82 | * mtdids=nor0=edb7312-nor,nand0=edb7312-nand |
83 | * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) | 83 | * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) |
84 | * | 84 | * |
85 | */ | 85 | */ |
86 | 86 | ||
87 | /* | 87 | /* |
88 | * JFFS2/CRAMFS support | 88 | * JFFS2/CRAMFS support |
89 | */ | 89 | */ |
90 | #include <common.h> | 90 | #include <common.h> |
91 | #include <command.h> | 91 | #include <command.h> |
92 | #include <malloc.h> | 92 | #include <malloc.h> |
93 | #include <jffs2/jffs2.h> | 93 | #include <jffs2/jffs2.h> |
94 | #include <linux/list.h> | 94 | #include <linux/list.h> |
95 | #include <linux/ctype.h> | 95 | #include <linux/ctype.h> |
96 | 96 | ||
97 | #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) | 97 | #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) |
98 | 98 | ||
99 | #include <cramfs/cramfs_fs.h> | 99 | #include <cramfs/cramfs_fs.h> |
100 | 100 | ||
101 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) | 101 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) |
102 | #ifdef CFG_NAND_LEGACY | 102 | #ifdef CFG_NAND_LEGACY |
103 | #include <linux/mtd/nand_legacy.h> | 103 | #include <linux/mtd/nand_legacy.h> |
104 | #else /* !CFG_NAND_LEGACY */ | 104 | #else /* !CFG_NAND_LEGACY */ |
105 | #include <linux/mtd/nand.h> | 105 | #include <linux/mtd/nand.h> |
106 | #include <nand.h> | 106 | #include <nand.h> |
107 | #endif /* !CFG_NAND_LEGACY */ | 107 | #endif /* !CFG_NAND_LEGACY */ |
108 | #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ | 108 | #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ |
109 | /* enable/disable debugging messages */ | 109 | /* enable/disable debugging messages */ |
110 | #define DEBUG_JFFS | 110 | #define DEBUG_JFFS |
111 | #undef DEBUG_JFFS | 111 | #undef DEBUG_JFFS |
112 | 112 | ||
113 | #ifdef DEBUG_JFFS | 113 | #ifdef DEBUG_JFFS |
114 | # define DEBUGF(fmt, args...) printf(fmt ,##args) | 114 | # define DEBUGF(fmt, args...) printf(fmt ,##args) |
115 | #else | 115 | #else |
116 | # define DEBUGF(fmt, args...) | 116 | # define DEBUGF(fmt, args...) |
117 | #endif | 117 | #endif |
118 | 118 | ||
119 | /* special size referring to all the remaining space in a partition */ | 119 | /* special size referring to all the remaining space in a partition */ |
120 | #define SIZE_REMAINING 0xFFFFFFFF | 120 | #define SIZE_REMAINING 0xFFFFFFFF |
121 | 121 | ||
122 | /* special offset value, it is used when not provided by user | 122 | /* special offset value, it is used when not provided by user |
123 | * | 123 | * |
124 | * this value is used temporarily during parsing, later such offests | 124 | * this value is used temporarily during parsing, later such offests |
125 | * are recalculated */ | 125 | * are recalculated */ |
126 | #define OFFSET_NOT_SPECIFIED 0xFFFFFFFF | 126 | #define OFFSET_NOT_SPECIFIED 0xFFFFFFFF |
127 | 127 | ||
128 | /* minimum partition size */ | 128 | /* minimum partition size */ |
129 | #define MIN_PART_SIZE 4096 | 129 | #define MIN_PART_SIZE 4096 |
130 | 130 | ||
131 | /* this flag needs to be set in part_info struct mask_flags | 131 | /* this flag needs to be set in part_info struct mask_flags |
132 | * field for read-only partitions */ | 132 | * field for read-only partitions */ |
133 | #define MTD_WRITEABLE_CMD 1 | 133 | #define MTD_WRITEABLE_CMD 1 |
134 | 134 | ||
135 | #ifdef CONFIG_JFFS2_CMDLINE | 135 | #ifdef CONFIG_JFFS2_CMDLINE |
136 | /* default values for mtdids and mtdparts variables */ | 136 | /* default values for mtdids and mtdparts variables */ |
137 | #if defined(MTDIDS_DEFAULT) | 137 | #if defined(MTDIDS_DEFAULT) |
138 | static const char *const mtdids_default = MTDIDS_DEFAULT; | 138 | static const char *const mtdids_default = MTDIDS_DEFAULT; |
139 | #else | 139 | #else |
140 | #warning "MTDIDS_DEFAULT not defined!" | 140 | #warning "MTDIDS_DEFAULT not defined!" |
141 | static const char *const mtdids_default = NULL; | 141 | static const char *const mtdids_default = NULL; |
142 | #endif | 142 | #endif |
143 | 143 | ||
144 | #if defined(MTDPARTS_DEFAULT) | 144 | #if defined(MTDPARTS_DEFAULT) |
145 | static const char *const mtdparts_default = MTDPARTS_DEFAULT; | 145 | static const char *const mtdparts_default = MTDPARTS_DEFAULT; |
146 | #else | 146 | #else |
147 | #warning "MTDPARTS_DEFAULT not defined!" | 147 | #warning "MTDPARTS_DEFAULT not defined!" |
148 | static const char *const mtdparts_default = NULL; | 148 | static const char *const mtdparts_default = NULL; |
149 | #endif | 149 | #endif |
150 | 150 | ||
151 | /* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */ | 151 | /* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */ |
152 | #define MTDIDS_MAXLEN 128 | 152 | #define MTDIDS_MAXLEN 128 |
153 | #define MTDPARTS_MAXLEN 512 | 153 | #define MTDPARTS_MAXLEN 512 |
154 | #define PARTITION_MAXLEN 16 | 154 | #define PARTITION_MAXLEN 16 |
155 | static char last_ids[MTDIDS_MAXLEN]; | 155 | static char last_ids[MTDIDS_MAXLEN]; |
156 | static char last_parts[MTDPARTS_MAXLEN]; | 156 | static char last_parts[MTDPARTS_MAXLEN]; |
157 | static char last_partition[PARTITION_MAXLEN]; | 157 | static char last_partition[PARTITION_MAXLEN]; |
158 | 158 | ||
159 | /* low level jffs2 cache cleaning routine */ | 159 | /* low level jffs2 cache cleaning routine */ |
160 | extern void jffs2_free_cache(struct part_info *part); | 160 | extern void jffs2_free_cache(struct part_info *part); |
161 | 161 | ||
162 | /* mtdids mapping list, filled by parse_ids() */ | 162 | /* mtdids mapping list, filled by parse_ids() */ |
163 | struct list_head mtdids; | 163 | struct list_head mtdids; |
164 | 164 | ||
165 | /* device/partition list, parse_cmdline() parses into here */ | 165 | /* device/partition list, parse_cmdline() parses into here */ |
166 | struct list_head devices; | 166 | struct list_head devices; |
167 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ | 167 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ |
168 | 168 | ||
169 | /* current active device and partition number */ | 169 | /* current active device and partition number */ |
170 | static struct mtd_device *current_dev = NULL; | 170 | static struct mtd_device *current_dev = NULL; |
171 | static u8 current_partnum = 0; | 171 | static u8 current_partnum = 0; |
172 | 172 | ||
173 | extern int cramfs_check (struct part_info *info); | 173 | extern int cramfs_check (struct part_info *info); |
174 | extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename); | 174 | extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename); |
175 | extern int cramfs_ls (struct part_info *info, char *filename); | 175 | extern int cramfs_ls (struct part_info *info, char *filename); |
176 | extern int cramfs_info (struct part_info *info); | 176 | extern int cramfs_info (struct part_info *info); |
177 | 177 | ||
178 | static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num); | 178 | static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num); |
179 | 179 | ||
180 | /* command line only routines */ | 180 | /* command line only routines */ |
181 | #ifdef CONFIG_JFFS2_CMDLINE | 181 | #ifdef CONFIG_JFFS2_CMDLINE |
182 | 182 | ||
183 | static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len); | 183 | static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len); |
184 | static int device_del(struct mtd_device *dev); | 184 | static int device_del(struct mtd_device *dev); |
185 | 185 | ||
186 | /** | 186 | /** |
187 | * Parses a string into a number. The number stored at ptr is | 187 | * Parses a string into a number. The number stored at ptr is |
188 | * potentially suffixed with K (for kilobytes, or 1024 bytes), | 188 | * potentially suffixed with K (for kilobytes, or 1024 bytes), |
189 | * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or | 189 | * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or |
190 | * 1073741824). If the number is suffixed with K, M, or G, then | 190 | * 1073741824). If the number is suffixed with K, M, or G, then |
191 | * the return value is the number multiplied by one kilobyte, one | 191 | * the return value is the number multiplied by one kilobyte, one |
192 | * megabyte, or one gigabyte, respectively. | 192 | * megabyte, or one gigabyte, respectively. |
193 | * | 193 | * |
194 | * @param ptr where parse begins | 194 | * @param ptr where parse begins |
195 | * @param retptr output pointer to next char after parse completes (output) | 195 | * @param retptr output pointer to next char after parse completes (output) |
196 | * @return resulting unsigned int | 196 | * @return resulting unsigned int |
197 | */ | 197 | */ |
198 | static unsigned long memsize_parse (const char *const ptr, const char **retptr) | 198 | static unsigned long memsize_parse (const char *const ptr, const char **retptr) |
199 | { | 199 | { |
200 | unsigned long ret = simple_strtoul(ptr, (char **)retptr, 0); | 200 | unsigned long ret = simple_strtoul(ptr, (char **)retptr, 0); |
201 | 201 | ||
202 | switch (**retptr) { | 202 | switch (**retptr) { |
203 | case 'G': | 203 | case 'G': |
204 | case 'g': | 204 | case 'g': |
205 | ret <<= 10; | 205 | ret <<= 10; |
206 | case 'M': | 206 | case 'M': |
207 | case 'm': | 207 | case 'm': |
208 | ret <<= 10; | 208 | ret <<= 10; |
209 | case 'K': | 209 | case 'K': |
210 | case 'k': | 210 | case 'k': |
211 | ret <<= 10; | 211 | ret <<= 10; |
212 | (*retptr)++; | 212 | (*retptr)++; |
213 | default: | 213 | default: |
214 | break; | 214 | break; |
215 | } | 215 | } |
216 | 216 | ||
217 | return ret; | 217 | return ret; |
218 | } | 218 | } |
219 | 219 | ||
220 | /** | 220 | /** |
221 | * Format string describing supplied size. This routine does the opposite job | 221 | * Format string describing supplied size. This routine does the opposite job |
222 | * to memsize_parse(). Size in bytes is converted to string and if possible | 222 | * to memsize_parse(). Size in bytes is converted to string and if possible |
223 | * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix. | 223 | * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix. |
224 | * | 224 | * |
225 | * Note, that this routine does not check for buffer overflow, it's the caller | 225 | * Note, that this routine does not check for buffer overflow, it's the caller |
226 | * who must assure enough space. | 226 | * who must assure enough space. |
227 | * | 227 | * |
228 | * @param buf output buffer | 228 | * @param buf output buffer |
229 | * @param size size to be converted to string | 229 | * @param size size to be converted to string |
230 | */ | 230 | */ |
231 | static void memsize_format(char *buf, u32 size) | 231 | static void memsize_format(char *buf, u32 size) |
232 | { | 232 | { |
233 | #define SIZE_GB ((u32)1024*1024*1024) | 233 | #define SIZE_GB ((u32)1024*1024*1024) |
234 | #define SIZE_MB ((u32)1024*1024) | 234 | #define SIZE_MB ((u32)1024*1024) |
235 | #define SIZE_KB ((u32)1024) | 235 | #define SIZE_KB ((u32)1024) |
236 | 236 | ||
237 | if ((size % SIZE_GB) == 0) | 237 | if ((size % SIZE_GB) == 0) |
238 | sprintf(buf, "%lug", size/SIZE_GB); | 238 | sprintf(buf, "%lug", size/SIZE_GB); |
239 | else if ((size % SIZE_MB) == 0) | 239 | else if ((size % SIZE_MB) == 0) |
240 | sprintf(buf, "%lum", size/SIZE_MB); | 240 | sprintf(buf, "%lum", size/SIZE_MB); |
241 | else if (size % SIZE_KB == 0) | 241 | else if (size % SIZE_KB == 0) |
242 | sprintf(buf, "%luk", size/SIZE_KB); | 242 | sprintf(buf, "%luk", size/SIZE_KB); |
243 | else | 243 | else |
244 | sprintf(buf, "%lu", size); | 244 | sprintf(buf, "%lu", size); |
245 | } | 245 | } |
246 | 246 | ||
247 | /** | 247 | /** |
248 | * This routine does global indexing of all partitions. Resulting index for | 248 | * This routine does global indexing of all partitions. Resulting index for |
249 | * current partition is saved in 'mtddevnum'. Current partition name in | 249 | * current partition is saved in 'mtddevnum'. Current partition name in |
250 | * 'mtddevname'. | 250 | * 'mtddevname'. |
251 | */ | 251 | */ |
252 | static void index_partitions(void) | 252 | static void index_partitions(void) |
253 | { | 253 | { |
254 | char buf[16]; | 254 | char buf[16]; |
255 | u16 mtddevnum; | 255 | u16 mtddevnum; |
256 | struct part_info *part; | 256 | struct part_info *part; |
257 | struct list_head *dentry; | 257 | struct list_head *dentry; |
258 | struct mtd_device *dev; | 258 | struct mtd_device *dev; |
259 | 259 | ||
260 | DEBUGF("--- index partitions ---\n"); | 260 | DEBUGF("--- index partitions ---\n"); |
261 | 261 | ||
262 | if (current_dev) { | 262 | if (current_dev) { |
263 | mtddevnum = 0; | 263 | mtddevnum = 0; |
264 | list_for_each(dentry, &devices) { | 264 | list_for_each(dentry, &devices) { |
265 | dev = list_entry(dentry, struct mtd_device, link); | 265 | dev = list_entry(dentry, struct mtd_device, link); |
266 | if (dev == current_dev) { | 266 | if (dev == current_dev) { |
267 | mtddevnum += current_partnum; | 267 | mtddevnum += current_partnum; |
268 | sprintf(buf, "%d", mtddevnum); | 268 | sprintf(buf, "%d", mtddevnum); |
269 | setenv("mtddevnum", buf); | 269 | setenv("mtddevnum", buf); |
270 | break; | 270 | break; |
271 | } | 271 | } |
272 | mtddevnum += dev->num_parts; | 272 | mtddevnum += dev->num_parts; |
273 | } | 273 | } |
274 | 274 | ||
275 | part = jffs2_part_info(current_dev, current_partnum); | 275 | part = jffs2_part_info(current_dev, current_partnum); |
276 | setenv("mtddevname", part->name); | 276 | setenv("mtddevname", part->name); |
277 | 277 | ||
278 | DEBUGF("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name); | 278 | DEBUGF("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name); |
279 | } else { | 279 | } else { |
280 | setenv("mtddevnum", NULL); | 280 | setenv("mtddevnum", NULL); |
281 | setenv("mtddevname", NULL); | 281 | setenv("mtddevname", NULL); |
282 | 282 | ||
283 | DEBUGF("=> mtddevnum NULL\n=> mtddevname NULL\n"); | 283 | DEBUGF("=> mtddevnum NULL\n=> mtddevname NULL\n"); |
284 | } | 284 | } |
285 | } | 285 | } |
286 | 286 | ||
287 | /** | 287 | /** |
288 | * Save current device and partition in environment variable 'partition'. | 288 | * Save current device and partition in environment variable 'partition'. |
289 | */ | 289 | */ |
290 | static void current_save(void) | 290 | static void current_save(void) |
291 | { | 291 | { |
292 | char buf[16]; | 292 | char buf[16]; |
293 | 293 | ||
294 | DEBUGF("--- current_save ---\n"); | 294 | DEBUGF("--- current_save ---\n"); |
295 | 295 | ||
296 | if (current_dev) { | 296 | if (current_dev) { |
297 | sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_dev->id->type), | 297 | sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_dev->id->type), |
298 | current_dev->id->num, current_partnum); | 298 | current_dev->id->num, current_partnum); |
299 | 299 | ||
300 | setenv("partition", buf); | 300 | setenv("partition", buf); |
301 | strncpy(last_partition, buf, 16); | 301 | strncpy(last_partition, buf, 16); |
302 | 302 | ||
303 | DEBUGF("=> partition %s\n", buf); | 303 | DEBUGF("=> partition %s\n", buf); |
304 | } else { | 304 | } else { |
305 | setenv("partition", NULL); | 305 | setenv("partition", NULL); |
306 | last_partition[0] = '\0'; | 306 | last_partition[0] = '\0'; |
307 | 307 | ||
308 | DEBUGF("=> partition NULL\n"); | 308 | DEBUGF("=> partition NULL\n"); |
309 | } | 309 | } |
310 | index_partitions(); | 310 | index_partitions(); |
311 | } | 311 | } |
312 | 312 | ||
313 | /** | 313 | /** |
314 | * Performs sanity check for supplied NOR flash partition. Table of existing | 314 | * Performs sanity check for supplied NOR flash partition. Table of existing |
315 | * NOR flash devices is searched and partition device is located. Alignment | 315 | * NOR flash devices is searched and partition device is located. Alignment |
316 | * with the granularity of NOR flash sectors is verified. | 316 | * with the granularity of NOR flash sectors is verified. |
317 | * | 317 | * |
318 | * @param id of the parent device | 318 | * @param id of the parent device |
319 | * @param part partition to validate | 319 | * @param part partition to validate |
320 | * @return 0 if partition is valid, 1 otherwise | 320 | * @return 0 if partition is valid, 1 otherwise |
321 | */ | 321 | */ |
322 | static int part_validate_nor(struct mtdids *id, struct part_info *part) | 322 | static int part_validate_nor(struct mtdids *id, struct part_info *part) |
323 | { | 323 | { |
324 | #if (CONFIG_COMMANDS & CFG_CMD_FLASH) | 324 | #if (CONFIG_COMMANDS & CFG_CMD_FLASH) |
325 | /* info for FLASH chips */ | 325 | /* info for FLASH chips */ |
326 | extern flash_info_t flash_info[]; | 326 | extern flash_info_t flash_info[]; |
327 | flash_info_t *flash; | 327 | flash_info_t *flash; |
328 | int offset_aligned; | 328 | int offset_aligned; |
329 | u32 end_offset; | 329 | u32 end_offset; |
330 | int i; | 330 | int i; |
331 | 331 | ||
332 | flash = &flash_info[id->num]; | 332 | flash = &flash_info[id->num]; |
333 | 333 | ||
334 | offset_aligned = 0; | 334 | offset_aligned = 0; |
335 | for (i = 0; i < flash->sector_count; i++) { | 335 | for (i = 0; i < flash->sector_count; i++) { |
336 | if ((flash->start[i] - flash->start[0]) == part->offset) { | 336 | if ((flash->start[i] - flash->start[0]) == part->offset) { |
337 | offset_aligned = 1; | 337 | offset_aligned = 1; |
338 | break; | 338 | break; |
339 | } | 339 | } |
340 | } | 340 | } |
341 | if (offset_aligned == 0) { | 341 | if (offset_aligned == 0) { |
342 | printf("%s%d: partition (%s) start offset alignment incorrect\n", | 342 | printf("%s%d: partition (%s) start offset alignment incorrect\n", |
343 | MTD_DEV_TYPE(id->type), id->num, part->name); | 343 | MTD_DEV_TYPE(id->type), id->num, part->name); |
344 | return 1; | 344 | return 1; |
345 | } | 345 | } |
346 | 346 | ||
347 | end_offset = part->offset + part->size; | 347 | end_offset = part->offset + part->size; |
348 | for (i = 0; i < flash->sector_count; i++) { | 348 | for (i = 0; i < flash->sector_count; i++) { |
349 | if ((flash->start[i] - flash->start[0]) == end_offset) | 349 | if ((flash->start[i] - flash->start[0]) == end_offset) |
350 | return 0; | 350 | return 0; |
351 | } | 351 | } |
352 | 352 | ||
353 | if (flash->size == end_offset) | 353 | if (flash->size == end_offset) |
354 | return 0; | 354 | return 0; |
355 | 355 | ||
356 | printf("%s%d: partition (%s) size alignment incorrect\n", | 356 | printf("%s%d: partition (%s) size alignment incorrect\n", |
357 | MTD_DEV_TYPE(id->type), id->num, part->name); | 357 | MTD_DEV_TYPE(id->type), id->num, part->name); |
358 | #endif | 358 | #endif |
359 | return 1; | 359 | return 1; |
360 | } | 360 | } |
361 | 361 | ||
362 | /** | 362 | /** |
363 | * Performs sanity check for supplied NAND flash partition. Table of existing | 363 | * Performs sanity check for supplied NAND flash partition. Table of existing |
364 | * NAND flash devices is searched and partition device is located. Alignment | 364 | * NAND flash devices is searched and partition device is located. Alignment |
365 | * with the granularity of nand erasesize is verified. | 365 | * with the granularity of nand erasesize is verified. |
366 | * | 366 | * |
367 | * @param id of the parent device | 367 | * @param id of the parent device |
368 | * @param part partition to validate | 368 | * @param part partition to validate |
369 | * @return 0 if partition is valid, 1 otherwise | 369 | * @return 0 if partition is valid, 1 otherwise |
370 | */ | 370 | */ |
371 | static int part_validate_nand(struct mtdids *id, struct part_info *part) | 371 | static int part_validate_nand(struct mtdids *id, struct part_info *part) |
372 | { | 372 | { |
373 | #if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) | 373 | #if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) |
374 | /* info for NAND chips */ | 374 | /* info for NAND chips */ |
375 | nand_info_t *nand; | 375 | nand_info_t *nand; |
376 | 376 | ||
377 | nand = &nand_info[id->num]; | 377 | nand = &nand_info[id->num]; |
378 | 378 | ||
379 | if ((unsigned long)(part->offset) % nand->erasesize) { | 379 | if ((unsigned long)(part->offset) % nand->erasesize) { |
380 | printf("%s%d: partition (%s) start offset alignment incorrect\n", | 380 | printf("%s%d: partition (%s) start offset alignment incorrect\n", |
381 | MTD_DEV_TYPE(id->type), id->num, part->name); | 381 | MTD_DEV_TYPE(id->type), id->num, part->name); |
382 | return 1; | 382 | return 1; |
383 | } | 383 | } |
384 | 384 | ||
385 | if (part->size % nand->erasesize) { | 385 | if (part->size % nand->erasesize) { |
386 | printf("%s%d: partition (%s) size alignment incorrect\n", | 386 | printf("%s%d: partition (%s) size alignment incorrect\n", |
387 | MTD_DEV_TYPE(id->type), id->num, part->name); | 387 | MTD_DEV_TYPE(id->type), id->num, part->name); |
388 | return 1; | 388 | return 1; |
389 | } | 389 | } |
390 | 390 | ||
391 | return 0; | 391 | return 0; |
392 | #else | 392 | #else |
393 | return 1; | 393 | return 1; |
394 | #endif | 394 | #endif |
395 | } | 395 | } |
396 | 396 | ||
397 | /** | 397 | /** |
398 | * Performs sanity check for supplied partition. Offset and size are verified | 398 | * Performs sanity check for supplied partition. Offset and size are verified |
399 | * to be within valid range. Partition type is checked and either | 399 | * to be within valid range. Partition type is checked and either |
400 | * parts_validate_nor() or parts_validate_nand() is called with the argument | 400 | * parts_validate_nor() or parts_validate_nand() is called with the argument |
401 | * of part. | 401 | * of part. |
402 | * | 402 | * |
403 | * @param id of the parent device | 403 | * @param id of the parent device |
404 | * @param part partition to validate | 404 | * @param part partition to validate |
405 | * @return 0 if partition is valid, 1 otherwise | 405 | * @return 0 if partition is valid, 1 otherwise |
406 | */ | 406 | */ |
407 | static int part_validate(struct mtdids *id, struct part_info *part) | 407 | static int part_validate(struct mtdids *id, struct part_info *part) |
408 | { | 408 | { |
409 | if (part->size == SIZE_REMAINING) | 409 | if (part->size == SIZE_REMAINING) |
410 | part->size = id->size - part->offset; | 410 | part->size = id->size - part->offset; |
411 | 411 | ||
412 | if (part->offset > id->size) { | 412 | if (part->offset > id->size) { |
413 | printf("%s: offset %08lx beyond flash size %08lx\n", | 413 | printf("%s: offset %08lx beyond flash size %08lx\n", |
414 | id->mtd_id, part->offset, id->size); | 414 | id->mtd_id, part->offset, id->size); |
415 | return 1; | 415 | return 1; |
416 | } | 416 | } |
417 | 417 | ||
418 | if ((part->offset + part->size) <= part->offset) { | 418 | if ((part->offset + part->size) <= part->offset) { |
419 | printf("%s%d: partition (%s) size too big\n", | 419 | printf("%s%d: partition (%s) size too big\n", |
420 | MTD_DEV_TYPE(id->type), id->num, part->name); | 420 | MTD_DEV_TYPE(id->type), id->num, part->name); |
421 | return 1; | 421 | return 1; |
422 | } | 422 | } |
423 | 423 | ||
424 | if (part->offset + part->size > id->size) { | 424 | if (part->offset + part->size > id->size) { |
425 | printf("%s: partitioning exceeds flash size\n", id->mtd_id); | 425 | printf("%s: partitioning exceeds flash size\n", id->mtd_id); |
426 | return 1; | 426 | return 1; |
427 | } | 427 | } |
428 | 428 | ||
429 | if (id->type == MTD_DEV_TYPE_NAND) | 429 | if (id->type == MTD_DEV_TYPE_NAND) |
430 | return part_validate_nand(id, part); | 430 | return part_validate_nand(id, part); |
431 | else if (id->type == MTD_DEV_TYPE_NOR) | 431 | else if (id->type == MTD_DEV_TYPE_NOR) |
432 | return part_validate_nor(id, part); | 432 | return part_validate_nor(id, part); |
433 | else | 433 | else |
434 | DEBUGF("part_validate: invalid dev type\n"); | 434 | DEBUGF("part_validate: invalid dev type\n"); |
435 | 435 | ||
436 | return 1; | 436 | return 1; |
437 | } | 437 | } |
438 | 438 | ||
439 | /** | 439 | /** |
440 | * Delete selected partition from the partion list of the specified device. | 440 | * Delete selected partition from the partion list of the specified device. |
441 | * | 441 | * |
442 | * @param dev device to delete partition from | 442 | * @param dev device to delete partition from |
443 | * @param part partition to delete | 443 | * @param part partition to delete |
444 | * @return 0 on success, 1 otherwise | 444 | * @return 0 on success, 1 otherwise |
445 | */ | 445 | */ |
446 | static int part_del(struct mtd_device *dev, struct part_info *part) | 446 | static int part_del(struct mtd_device *dev, struct part_info *part) |
447 | { | 447 | { |
448 | u8 current_save_needed = 0; | 448 | u8 current_save_needed = 0; |
449 | 449 | ||
450 | /* if there is only one partition, remove whole device */ | 450 | /* if there is only one partition, remove whole device */ |
451 | if (dev->num_parts == 1) | 451 | if (dev->num_parts == 1) |
452 | return device_del(dev); | 452 | return device_del(dev); |
453 | 453 | ||
454 | /* otherwise just delete this partition */ | 454 | /* otherwise just delete this partition */ |
455 | 455 | ||
456 | if (dev == current_dev) { | 456 | if (dev == current_dev) { |
457 | /* we are modyfing partitions for the current device, | 457 | /* we are modyfing partitions for the current device, |
458 | * update current */ | 458 | * update current */ |
459 | struct part_info *curr_pi; | 459 | struct part_info *curr_pi; |
460 | curr_pi = jffs2_part_info(current_dev, current_partnum); | 460 | curr_pi = jffs2_part_info(current_dev, current_partnum); |
461 | 461 | ||
462 | if (curr_pi) { | 462 | if (curr_pi) { |
463 | if (curr_pi == part) { | 463 | if (curr_pi == part) { |
464 | printf("current partition deleted, resetting current to 0\n"); | 464 | printf("current partition deleted, resetting current to 0\n"); |
465 | current_partnum = 0; | 465 | current_partnum = 0; |
466 | } else if (part->offset <= curr_pi->offset) { | 466 | } else if (part->offset <= curr_pi->offset) { |
467 | current_partnum--; | 467 | current_partnum--; |
468 | } | 468 | } |
469 | current_save_needed = 1; | 469 | current_save_needed = 1; |
470 | } | 470 | } |
471 | } | 471 | } |
472 | 472 | ||
473 | #ifdef CFG_NAND_LEGACY | 473 | #ifdef CFG_NAND_LEGACY |
474 | jffs2_free_cache(part); | 474 | jffs2_free_cache(part); |
475 | #endif | 475 | #endif |
476 | list_del(&part->link); | 476 | list_del(&part->link); |
477 | free(part); | 477 | free(part); |
478 | dev->num_parts--; | 478 | dev->num_parts--; |
479 | 479 | ||
480 | if (current_save_needed > 0) | 480 | if (current_save_needed > 0) |
481 | current_save(); | 481 | current_save(); |
482 | else | 482 | else |
483 | index_partitions(); | 483 | index_partitions(); |
484 | 484 | ||
485 | return 0; | 485 | return 0; |
486 | } | 486 | } |
487 | 487 | ||
488 | /** | 488 | /** |
489 | * Delete all partitions from parts head list, free memory. | 489 | * Delete all partitions from parts head list, free memory. |
490 | * | 490 | * |
491 | * @param head list of partitions to delete | 491 | * @param head list of partitions to delete |
492 | */ | 492 | */ |
493 | static void part_delall(struct list_head *head) | 493 | static void part_delall(struct list_head *head) |
494 | { | 494 | { |
495 | struct list_head *entry, *n; | 495 | struct list_head *entry, *n; |
496 | struct part_info *part_tmp; | 496 | struct part_info *part_tmp; |
497 | 497 | ||
498 | /* clean tmp_list and free allocated memory */ | 498 | /* clean tmp_list and free allocated memory */ |
499 | list_for_each_safe(entry, n, head) { | 499 | list_for_each_safe(entry, n, head) { |
500 | part_tmp = list_entry(entry, struct part_info, link); | 500 | part_tmp = list_entry(entry, struct part_info, link); |
501 | 501 | ||
502 | #ifdef CFG_NAND_LEGACY | 502 | #ifdef CFG_NAND_LEGACY |
503 | jffs2_free_cache(part_tmp); | 503 | jffs2_free_cache(part_tmp); |
504 | #endif | 504 | #endif |
505 | list_del(entry); | 505 | list_del(entry); |
506 | free(part_tmp); | 506 | free(part_tmp); |
507 | } | 507 | } |
508 | } | 508 | } |
509 | 509 | ||
510 | /** | 510 | /** |
511 | * Add new partition to the supplied partition list. Make sure partitions are | 511 | * Add new partition to the supplied partition list. Make sure partitions are |
512 | * sorted by offset in ascending order. | 512 | * sorted by offset in ascending order. |
513 | * | 513 | * |
514 | * @param head list this partition is to be added to | 514 | * @param head list this partition is to be added to |
515 | * @param new partition to be added | 515 | * @param new partition to be added |
516 | */ | 516 | */ |
517 | static int part_sort_add(struct mtd_device *dev, struct part_info *part) | 517 | static int part_sort_add(struct mtd_device *dev, struct part_info *part) |
518 | { | 518 | { |
519 | struct list_head *entry; | 519 | struct list_head *entry; |
520 | struct part_info *new_pi, *curr_pi; | 520 | struct part_info *new_pi, *curr_pi; |
521 | 521 | ||
522 | /* link partition to parrent dev */ | 522 | /* link partition to parrent dev */ |
523 | part->dev = dev; | 523 | part->dev = dev; |
524 | 524 | ||
525 | if (list_empty(&dev->parts)) { | 525 | if (list_empty(&dev->parts)) { |
526 | DEBUGF("part_sort_add: list empty\n"); | 526 | DEBUGF("part_sort_add: list empty\n"); |
527 | list_add(&part->link, &dev->parts); | 527 | list_add(&part->link, &dev->parts); |
528 | dev->num_parts++; | 528 | dev->num_parts++; |
529 | index_partitions(); | 529 | index_partitions(); |
530 | return 0; | 530 | return 0; |
531 | } | 531 | } |
532 | 532 | ||
533 | new_pi = list_entry(&part->link, struct part_info, link); | 533 | new_pi = list_entry(&part->link, struct part_info, link); |
534 | 534 | ||
535 | /* get current partition info if we are updating current device */ | 535 | /* get current partition info if we are updating current device */ |
536 | curr_pi = NULL; | 536 | curr_pi = NULL; |
537 | if (dev == current_dev) | 537 | if (dev == current_dev) |
538 | curr_pi = jffs2_part_info(current_dev, current_partnum); | 538 | curr_pi = jffs2_part_info(current_dev, current_partnum); |
539 | 539 | ||
540 | list_for_each(entry, &dev->parts) { | 540 | list_for_each(entry, &dev->parts) { |
541 | struct part_info *pi; | 541 | struct part_info *pi; |
542 | 542 | ||
543 | pi = list_entry(entry, struct part_info, link); | 543 | pi = list_entry(entry, struct part_info, link); |
544 | 544 | ||
545 | /* be compliant with kernel cmdline, allow only one partition at offset zero */ | 545 | /* be compliant with kernel cmdline, allow only one partition at offset zero */ |
546 | if ((new_pi->offset == pi->offset) && (pi->offset == 0)) { | 546 | if ((new_pi->offset == pi->offset) && (pi->offset == 0)) { |
547 | printf("cannot add second partition at offset 0\n"); | 547 | printf("cannot add second partition at offset 0\n"); |
548 | return 1; | 548 | return 1; |
549 | } | 549 | } |
550 | 550 | ||
551 | if (new_pi->offset <= pi->offset) { | 551 | if (new_pi->offset <= pi->offset) { |
552 | list_add_tail(&part->link, entry); | 552 | list_add_tail(&part->link, entry); |
553 | dev->num_parts++; | 553 | dev->num_parts++; |
554 | 554 | ||
555 | if (curr_pi && (pi->offset <= curr_pi->offset)) { | 555 | if (curr_pi && (pi->offset <= curr_pi->offset)) { |
556 | /* we are modyfing partitions for the current | 556 | /* we are modyfing partitions for the current |
557 | * device, update current */ | 557 | * device, update current */ |
558 | current_partnum++; | 558 | current_partnum++; |
559 | current_save(); | 559 | current_save(); |
560 | } else { | 560 | } else { |
561 | index_partitions(); | 561 | index_partitions(); |
562 | } | 562 | } |
563 | return 0; | 563 | return 0; |
564 | } | 564 | } |
565 | } | 565 | } |
566 | 566 | ||
567 | list_add_tail(&part->link, &dev->parts); | 567 | list_add_tail(&part->link, &dev->parts); |
568 | dev->num_parts++; | 568 | dev->num_parts++; |
569 | index_partitions(); | 569 | index_partitions(); |
570 | return 0; | 570 | return 0; |
571 | } | 571 | } |
572 | 572 | ||
573 | /** | 573 | /** |
574 | * Add provided partition to the partition list of a given device. | 574 | * Add provided partition to the partition list of a given device. |
575 | * | 575 | * |
576 | * @param dev device to which partition is added | 576 | * @param dev device to which partition is added |
577 | * @param part partition to be added | 577 | * @param part partition to be added |
578 | * @return 0 on success, 1 otherwise | 578 | * @return 0 on success, 1 otherwise |
579 | */ | 579 | */ |
580 | static int part_add(struct mtd_device *dev, struct part_info *part) | 580 | static int part_add(struct mtd_device *dev, struct part_info *part) |
581 | { | 581 | { |
582 | /* verify alignment and size */ | 582 | /* verify alignment and size */ |
583 | if (part_validate(dev->id, part) != 0) | 583 | if (part_validate(dev->id, part) != 0) |
584 | return 1; | 584 | return 1; |
585 | 585 | ||
586 | /* partition is ok, add it to the list */ | 586 | /* partition is ok, add it to the list */ |
587 | if (part_sort_add(dev, part) != 0) | 587 | if (part_sort_add(dev, part) != 0) |
588 | return 1; | 588 | return 1; |
589 | 589 | ||
590 | return 0; | 590 | return 0; |
591 | } | 591 | } |
592 | 592 | ||
593 | /** | 593 | /** |
594 | * Parse one partition definition, allocate memory and return pointer to this | 594 | * Parse one partition definition, allocate memory and return pointer to this |
595 | * location in retpart. | 595 | * location in retpart. |
596 | * | 596 | * |
597 | * @param partdef pointer to the partition definition string i.e. <part-def> | 597 | * @param partdef pointer to the partition definition string i.e. <part-def> |
598 | * @param ret output pointer to next char after parse completes (output) | 598 | * @param ret output pointer to next char after parse completes (output) |
599 | * @param retpart pointer to the allocated partition (output) | 599 | * @param retpart pointer to the allocated partition (output) |
600 | * @return 0 on success, 1 otherwise | 600 | * @return 0 on success, 1 otherwise |
601 | */ | 601 | */ |
602 | static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart) | 602 | static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart) |
603 | { | 603 | { |
604 | struct part_info *part; | 604 | struct part_info *part; |
605 | unsigned long size; | 605 | unsigned long size; |
606 | unsigned long offset; | 606 | unsigned long offset; |
607 | const char *name; | 607 | const char *name; |
608 | int name_len; | 608 | int name_len; |
609 | unsigned int mask_flags; | 609 | unsigned int mask_flags; |
610 | const char *p; | 610 | const char *p; |
611 | 611 | ||
612 | p = partdef; | 612 | p = partdef; |
613 | *retpart = NULL; | 613 | *retpart = NULL; |
614 | *ret = NULL; | 614 | *ret = NULL; |
615 | 615 | ||
616 | /* fetch the partition size */ | 616 | /* fetch the partition size */ |
617 | if (*p == '-') { | 617 | if (*p == '-') { |
618 | /* assign all remaining space to this partition */ | 618 | /* assign all remaining space to this partition */ |
619 | DEBUGF("'-': remaining size assigned\n"); | 619 | DEBUGF("'-': remaining size assigned\n"); |
620 | size = SIZE_REMAINING; | 620 | size = SIZE_REMAINING; |
621 | p++; | 621 | p++; |
622 | } else { | 622 | } else { |
623 | size = memsize_parse(p, &p); | 623 | size = memsize_parse(p, &p); |
624 | if (size < MIN_PART_SIZE) { | 624 | if (size < MIN_PART_SIZE) { |
625 | printf("partition size too small (%lx)\n", size); | 625 | printf("partition size too small (%lx)\n", size); |
626 | return 1; | 626 | return 1; |
627 | } | 627 | } |
628 | } | 628 | } |
629 | 629 | ||
630 | /* check for offset */ | 630 | /* check for offset */ |
631 | offset = OFFSET_NOT_SPECIFIED; | 631 | offset = OFFSET_NOT_SPECIFIED; |
632 | if (*p == '@') { | 632 | if (*p == '@') { |
633 | p++; | 633 | p++; |
634 | offset = memsize_parse(p, &p); | 634 | offset = memsize_parse(p, &p); |
635 | } | 635 | } |
636 | 636 | ||
637 | /* now look for the name */ | 637 | /* now look for the name */ |
638 | if (*p == '(') { | 638 | if (*p == '(') { |
639 | name = ++p; | 639 | name = ++p; |
640 | if ((p = strchr(name, ')')) == NULL) { | 640 | if ((p = strchr(name, ')')) == NULL) { |
641 | printf("no closing ) found in partition name\n"); | 641 | printf("no closing ) found in partition name\n"); |
642 | return 1; | 642 | return 1; |
643 | } | 643 | } |
644 | name_len = p - name + 1; | 644 | name_len = p - name + 1; |
645 | if ((name_len - 1) == 0) { | 645 | if ((name_len - 1) == 0) { |
646 | printf("empty partition name\n"); | 646 | printf("empty partition name\n"); |
647 | return 1; | 647 | return 1; |
648 | } | 648 | } |
649 | p++; | 649 | p++; |
650 | } else { | 650 | } else { |
651 | /* 0x00000000@0x00000000 */ | 651 | /* 0x00000000@0x00000000 */ |
652 | name_len = 22; | 652 | name_len = 22; |
653 | name = NULL; | 653 | name = NULL; |
654 | } | 654 | } |
655 | 655 | ||
656 | /* test for options */ | 656 | /* test for options */ |
657 | mask_flags = 0; | 657 | mask_flags = 0; |
658 | if (strncmp(p, "ro", 2) == 0) { | 658 | if (strncmp(p, "ro", 2) == 0) { |
659 | mask_flags |= MTD_WRITEABLE_CMD; | 659 | mask_flags |= MTD_WRITEABLE_CMD; |
660 | p += 2; | 660 | p += 2; |
661 | } | 661 | } |
662 | 662 | ||
663 | /* check for next partition definition */ | 663 | /* check for next partition definition */ |
664 | if (*p == ',') { | 664 | if (*p == ',') { |
665 | if (size == SIZE_REMAINING) { | 665 | if (size == SIZE_REMAINING) { |
666 | *ret = NULL; | 666 | *ret = NULL; |
667 | printf("no partitions allowed after a fill-up partition\n"); | 667 | printf("no partitions allowed after a fill-up partition\n"); |
668 | return 1; | 668 | return 1; |
669 | } | 669 | } |
670 | *ret = ++p; | 670 | *ret = ++p; |
671 | } else if ((*p == ';') || (*p == '\0')) { | 671 | } else if ((*p == ';') || (*p == '\0')) { |
672 | *ret = p; | 672 | *ret = p; |
673 | } else { | 673 | } else { |
674 | printf("unexpected character '%c' at the end of partition\n", *p); | 674 | printf("unexpected character '%c' at the end of partition\n", *p); |
675 | *ret = NULL; | 675 | *ret = NULL; |
676 | return 1; | 676 | return 1; |
677 | } | 677 | } |
678 | 678 | ||
679 | /* allocate memory */ | 679 | /* allocate memory */ |
680 | part = (struct part_info *)malloc(sizeof(struct part_info) + name_len); | 680 | part = (struct part_info *)malloc(sizeof(struct part_info) + name_len); |
681 | if (!part) { | 681 | if (!part) { |
682 | printf("out of memory\n"); | 682 | printf("out of memory\n"); |
683 | return 1; | 683 | return 1; |
684 | } | 684 | } |
685 | memset(part, 0, sizeof(struct part_info) + name_len); | 685 | memset(part, 0, sizeof(struct part_info) + name_len); |
686 | part->size = size; | 686 | part->size = size; |
687 | part->offset = offset; | 687 | part->offset = offset; |
688 | part->mask_flags = mask_flags; | 688 | part->mask_flags = mask_flags; |
689 | part->name = (char *)(part + 1); | 689 | part->name = (char *)(part + 1); |
690 | 690 | ||
691 | if (name) { | 691 | if (name) { |
692 | /* copy user provided name */ | 692 | /* copy user provided name */ |
693 | strncpy(part->name, name, name_len - 1); | 693 | strncpy(part->name, name, name_len - 1); |
694 | part->auto_name = 0; | 694 | part->auto_name = 0; |
695 | } else { | 695 | } else { |
696 | /* auto generated name in form of size@offset */ | 696 | /* auto generated name in form of size@offset */ |
697 | sprintf(part->name, "0x%08lx@0x%08lx", size, offset); | 697 | sprintf(part->name, "0x%08lx@0x%08lx", size, offset); |
698 | part->auto_name = 1; | 698 | part->auto_name = 1; |
699 | } | 699 | } |
700 | 700 | ||
701 | part->name[name_len - 1] = '\0'; | 701 | part->name[name_len - 1] = '\0'; |
702 | INIT_LIST_HEAD(&part->link); | 702 | INIT_LIST_HEAD(&part->link); |
703 | 703 | ||
704 | DEBUGF("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n", | 704 | DEBUGF("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n", |
705 | part->name, part->size, | 705 | part->name, part->size, |
706 | part->offset, part->mask_flags); | 706 | part->offset, part->mask_flags); |
707 | 707 | ||
708 | *retpart = part; | 708 | *retpart = part; |
709 | return 0; | 709 | return 0; |
710 | } | 710 | } |
711 | #endif/* #ifdef CONFIG_JFFS2_CMDLINE */ | 711 | #endif/* #ifdef CONFIG_JFFS2_CMDLINE */ |
712 | 712 | ||
713 | /** | 713 | /** |
714 | * Check device number to be within valid range for given device type. | 714 | * Check device number to be within valid range for given device type. |
715 | * | 715 | * |
716 | * @param dev device to validate | 716 | * @param dev device to validate |
717 | * @return 0 if device is valid, 1 otherwise | 717 | * @return 0 if device is valid, 1 otherwise |
718 | */ | 718 | */ |
719 | static int device_validate(u8 type, u8 num, u32 *size) | 719 | static int device_validate(u8 type, u8 num, u32 *size) |
720 | { | 720 | { |
721 | if (type == MTD_DEV_TYPE_NOR) { | 721 | if (type == MTD_DEV_TYPE_NOR) { |
722 | #if (CONFIG_COMMANDS & CFG_CMD_FLASH) | 722 | #if (CONFIG_COMMANDS & CFG_CMD_FLASH) |
723 | if (num < CFG_MAX_FLASH_BANKS) { | 723 | if (num < CFG_MAX_FLASH_BANKS) { |
724 | extern flash_info_t flash_info[]; | 724 | extern flash_info_t flash_info[]; |
725 | *size = flash_info[num].size; | 725 | *size = flash_info[num].size; |
726 | 726 | ||
727 | return 0; | 727 | return 0; |
728 | } | 728 | } |
729 | 729 | ||
730 | printf("no such FLASH device: %s%d (valid range 0 ... %d\n", | 730 | printf("no such FLASH device: %s%d (valid range 0 ... %d\n", |
731 | MTD_DEV_TYPE(type), num, CFG_MAX_FLASH_BANKS - 1); | 731 | MTD_DEV_TYPE(type), num, CFG_MAX_FLASH_BANKS - 1); |
732 | #else | 732 | #else |
733 | printf("support for FLASH devices not present\n"); | 733 | printf("support for FLASH devices not present\n"); |
734 | #endif | 734 | #endif |
735 | } else if (type == MTD_DEV_TYPE_NAND) { | 735 | } else if (type == MTD_DEV_TYPE_NAND) { |
736 | #if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) | 736 | #if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) |
737 | if (num < CFG_MAX_NAND_DEVICE) { | 737 | if (num < CFG_MAX_NAND_DEVICE) { |
738 | #ifndef CFG_NAND_LEGACY | 738 | #ifndef CFG_NAND_LEGACY |
739 | *size = nand_info[num].size; | 739 | *size = nand_info[num].size; |
740 | #else | 740 | #else |
741 | extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; | 741 | extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; |
742 | *size = nand_dev_desc[num].totlen; | 742 | *size = nand_dev_desc[num].totlen; |
743 | #endif | 743 | #endif |
744 | return 0; | 744 | return 0; |
745 | } | 745 | } |
746 | 746 | ||
747 | printf("no such NAND device: %s%d (valid range 0 ... %d)\n", | 747 | printf("no such NAND device: %s%d (valid range 0 ... %d)\n", |
748 | MTD_DEV_TYPE(type), num, CFG_MAX_NAND_DEVICE - 1); | 748 | MTD_DEV_TYPE(type), num, CFG_MAX_NAND_DEVICE - 1); |
749 | #else | 749 | #else |
750 | printf("support for NAND devices not present\n"); | 750 | printf("support for NAND devices not present\n"); |
751 | #endif | 751 | #endif |
752 | } | 752 | } |
753 | 753 | ||
754 | return 1; | 754 | return 1; |
755 | } | 755 | } |
756 | 756 | ||
757 | #ifdef CONFIG_JFFS2_CMDLINE | 757 | #ifdef CONFIG_JFFS2_CMDLINE |
758 | /** | 758 | /** |
759 | * Delete all mtd devices from a supplied devices list, free memory allocated for | 759 | * Delete all mtd devices from a supplied devices list, free memory allocated for |
760 | * each device and delete all device partitions. | 760 | * each device and delete all device partitions. |
761 | * | 761 | * |
762 | * @return 0 on success, 1 otherwise | 762 | * @return 0 on success, 1 otherwise |
763 | */ | 763 | */ |
764 | static int device_delall(struct list_head *head) | 764 | static int device_delall(struct list_head *head) |
765 | { | 765 | { |
766 | struct list_head *entry, *n; | 766 | struct list_head *entry, *n; |
767 | struct mtd_device *dev_tmp; | 767 | struct mtd_device *dev_tmp; |
768 | 768 | ||
769 | /* clean devices list */ | 769 | /* clean devices list */ |
770 | list_for_each_safe(entry, n, head) { | 770 | list_for_each_safe(entry, n, head) { |
771 | dev_tmp = list_entry(entry, struct mtd_device, link); | 771 | dev_tmp = list_entry(entry, struct mtd_device, link); |
772 | list_del(entry); | 772 | list_del(entry); |
773 | part_delall(&dev_tmp->parts); | 773 | part_delall(&dev_tmp->parts); |
774 | free(dev_tmp); | 774 | free(dev_tmp); |
775 | } | 775 | } |
776 | INIT_LIST_HEAD(&devices); | 776 | INIT_LIST_HEAD(&devices); |
777 | 777 | ||
778 | return 0; | 778 | return 0; |
779 | } | 779 | } |
780 | 780 | ||
781 | /** | 781 | /** |
782 | * If provided device exists it's partitions are deleted, device is removed | 782 | * If provided device exists it's partitions are deleted, device is removed |
783 | * from device list and device memory is freed. | 783 | * from device list and device memory is freed. |
784 | * | 784 | * |
785 | * @param dev device to be deleted | 785 | * @param dev device to be deleted |
786 | * @return 0 on success, 1 otherwise | 786 | * @return 0 on success, 1 otherwise |
787 | */ | 787 | */ |
788 | static int device_del(struct mtd_device *dev) | 788 | static int device_del(struct mtd_device *dev) |
789 | { | 789 | { |
790 | part_delall(&dev->parts); | 790 | part_delall(&dev->parts); |
791 | list_del(&dev->link); | 791 | list_del(&dev->link); |
792 | free(dev); | 792 | free(dev); |
793 | 793 | ||
794 | if (dev == current_dev) { | 794 | if (dev == current_dev) { |
795 | /* we just deleted current device */ | 795 | /* we just deleted current device */ |
796 | if (list_empty(&devices)) { | 796 | if (list_empty(&devices)) { |
797 | current_dev = NULL; | 797 | current_dev = NULL; |
798 | } else { | 798 | } else { |
799 | /* reset first partition from first dev from the | 799 | /* reset first partition from first dev from the |
800 | * devices list as current */ | 800 | * devices list as current */ |
801 | current_dev = list_entry(devices.next, struct mtd_device, link); | 801 | current_dev = list_entry(devices.next, struct mtd_device, link); |
802 | current_partnum = 0; | 802 | current_partnum = 0; |
803 | } | 803 | } |
804 | current_save(); | 804 | current_save(); |
805 | return 0; | 805 | return 0; |
806 | } | 806 | } |
807 | 807 | ||
808 | index_partitions(); | 808 | index_partitions(); |
809 | return 0; | 809 | return 0; |
810 | } | 810 | } |
811 | 811 | ||
812 | /** | 812 | /** |
813 | * Search global device list and return pointer to the device of type and num | 813 | * Search global device list and return pointer to the device of type and num |
814 | * specified. | 814 | * specified. |
815 | * | 815 | * |
816 | * @param type device type | 816 | * @param type device type |
817 | * @param num device number | 817 | * @param num device number |
818 | * @return NULL if requested device does not exist | 818 | * @return NULL if requested device does not exist |
819 | */ | 819 | */ |
820 | static struct mtd_device* device_find(u8 type, u8 num) | 820 | static struct mtd_device* device_find(u8 type, u8 num) |
821 | { | 821 | { |
822 | struct list_head *entry; | 822 | struct list_head *entry; |
823 | struct mtd_device *dev_tmp; | 823 | struct mtd_device *dev_tmp; |
824 | 824 | ||
825 | list_for_each(entry, &devices) { | 825 | list_for_each(entry, &devices) { |
826 | dev_tmp = list_entry(entry, struct mtd_device, link); | 826 | dev_tmp = list_entry(entry, struct mtd_device, link); |
827 | 827 | ||
828 | if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num)) | 828 | if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num)) |
829 | return dev_tmp; | 829 | return dev_tmp; |
830 | } | 830 | } |
831 | 831 | ||
832 | return NULL; | 832 | return NULL; |
833 | } | 833 | } |
834 | 834 | ||
835 | /** | 835 | /** |
836 | * Add specified device to the global device list. | 836 | * Add specified device to the global device list. |
837 | * | 837 | * |
838 | * @param dev device to be added | 838 | * @param dev device to be added |
839 | */ | 839 | */ |
840 | static void device_add(struct mtd_device *dev) | 840 | static void device_add(struct mtd_device *dev) |
841 | { | 841 | { |
842 | u8 current_save_needed = 0; | 842 | u8 current_save_needed = 0; |
843 | 843 | ||
844 | if (list_empty(&devices)) { | 844 | if (list_empty(&devices)) { |
845 | current_dev = dev; | 845 | current_dev = dev; |
846 | current_partnum = 0; | 846 | current_partnum = 0; |
847 | current_save_needed = 1; | 847 | current_save_needed = 1; |
848 | } | 848 | } |
849 | 849 | ||
850 | list_add_tail(&dev->link, &devices); | 850 | list_add_tail(&dev->link, &devices); |
851 | 851 | ||
852 | if (current_save_needed > 0) | 852 | if (current_save_needed > 0) |
853 | current_save(); | 853 | current_save(); |
854 | else | 854 | else |
855 | index_partitions(); | 855 | index_partitions(); |
856 | } | 856 | } |
857 | 857 | ||
858 | /** | 858 | /** |
859 | * Parse device type, name and mtd-id. If syntax is ok allocate memory and | 859 | * Parse device type, name and mtd-id. If syntax is ok allocate memory and |
860 | * return pointer to the device structure. | 860 | * return pointer to the device structure. |
861 | * | 861 | * |
862 | * @param mtd_dev pointer to the device definition string i.e. <mtd-dev> | 862 | * @param mtd_dev pointer to the device definition string i.e. <mtd-dev> |
863 | * @param ret output pointer to next char after parse completes (output) | 863 | * @param ret output pointer to next char after parse completes (output) |
864 | * @param retdev pointer to the allocated device (output) | 864 | * @param retdev pointer to the allocated device (output) |
865 | * @return 0 on success, 1 otherwise | 865 | * @return 0 on success, 1 otherwise |
866 | */ | 866 | */ |
867 | static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev) | 867 | static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev) |
868 | { | 868 | { |
869 | struct mtd_device *dev; | 869 | struct mtd_device *dev; |
870 | struct part_info *part; | 870 | struct part_info *part; |
871 | struct mtdids *id; | 871 | struct mtdids *id; |
872 | const char *mtd_id; | 872 | const char *mtd_id; |
873 | unsigned int mtd_id_len; | 873 | unsigned int mtd_id_len; |
874 | const char *p, *pend; | 874 | const char *p, *pend; |
875 | LIST_HEAD(tmp_list); | 875 | LIST_HEAD(tmp_list); |
876 | struct list_head *entry, *n; | 876 | struct list_head *entry, *n; |
877 | u16 num_parts; | 877 | u16 num_parts; |
878 | u32 offset; | 878 | u32 offset; |
879 | int err = 1; | 879 | int err = 1; |
880 | 880 | ||
881 | p = mtd_dev; | 881 | p = mtd_dev; |
882 | *retdev = NULL; | 882 | *retdev = NULL; |
883 | *ret = NULL; | 883 | *ret = NULL; |
884 | 884 | ||
885 | DEBUGF("===device_parse===\n"); | 885 | DEBUGF("===device_parse===\n"); |
886 | 886 | ||
887 | /* fetch <mtd-id> */ | 887 | /* fetch <mtd-id> */ |
888 | mtd_id = p; | 888 | mtd_id = p; |
889 | if (!(p = strchr(mtd_id, ':'))) { | 889 | if (!(p = strchr(mtd_id, ':'))) { |
890 | printf("no <mtd-id> identifier\n"); | 890 | printf("no <mtd-id> identifier\n"); |
891 | return 1; | 891 | return 1; |
892 | } | 892 | } |
893 | mtd_id_len = p - mtd_id + 1; | 893 | mtd_id_len = p - mtd_id + 1; |
894 | p++; | 894 | p++; |
895 | 895 | ||
896 | /* verify if we have a valid device specified */ | 896 | /* verify if we have a valid device specified */ |
897 | if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) { | 897 | if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) { |
898 | printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id); | 898 | printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id); |
899 | return 1; | 899 | return 1; |
900 | } | 900 | } |
901 | 901 | ||
902 | DEBUGF("dev type = %d (%s), dev num = %d, mtd-id = %s\n", | 902 | DEBUGF("dev type = %d (%s), dev num = %d, mtd-id = %s\n", |
903 | id->type, MTD_DEV_TYPE(id->type), | 903 | id->type, MTD_DEV_TYPE(id->type), |
904 | id->num, id->mtd_id); | 904 | id->num, id->mtd_id); |
905 | pend = strchr(p, ';'); | 905 | pend = strchr(p, ';'); |
906 | DEBUGF("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p); | 906 | DEBUGF("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p); |
907 | 907 | ||
908 | 908 | ||
909 | /* parse partitions */ | 909 | /* parse partitions */ |
910 | num_parts = 0; | 910 | num_parts = 0; |
911 | 911 | ||
912 | offset = 0; | 912 | offset = 0; |
913 | if ((dev = device_find(id->type, id->num)) != NULL) { | 913 | if ((dev = device_find(id->type, id->num)) != NULL) { |
914 | /* if device already exists start at the end of the last partition */ | 914 | /* if device already exists start at the end of the last partition */ |
915 | part = list_entry(dev->parts.prev, struct part_info, link); | 915 | part = list_entry(dev->parts.prev, struct part_info, link); |
916 | offset = part->offset + part->size; | 916 | offset = part->offset + part->size; |
917 | } | 917 | } |
918 | 918 | ||
919 | while (p && (*p != '\0') && (*p != ';')) { | 919 | while (p && (*p != '\0') && (*p != ';')) { |
920 | err = 1; | 920 | err = 1; |
921 | if ((part_parse(p, &p, &part) != 0) || (!part)) | 921 | if ((part_parse(p, &p, &part) != 0) || (!part)) |
922 | break; | 922 | break; |
923 | 923 | ||
924 | /* calculate offset when not specified */ | 924 | /* calculate offset when not specified */ |
925 | if (part->offset == OFFSET_NOT_SPECIFIED) | 925 | if (part->offset == OFFSET_NOT_SPECIFIED) |
926 | part->offset = offset; | 926 | part->offset = offset; |
927 | else | 927 | else |
928 | offset = part->offset; | 928 | offset = part->offset; |
929 | 929 | ||
930 | /* verify alignment and size */ | 930 | /* verify alignment and size */ |
931 | if (part_validate(id, part) != 0) | 931 | if (part_validate(id, part) != 0) |
932 | break; | 932 | break; |
933 | 933 | ||
934 | offset += part->size; | 934 | offset += part->size; |
935 | 935 | ||
936 | /* partition is ok, add it to the list */ | 936 | /* partition is ok, add it to the list */ |
937 | list_add_tail(&part->link, &tmp_list); | 937 | list_add_tail(&part->link, &tmp_list); |
938 | num_parts++; | 938 | num_parts++; |
939 | err = 0; | 939 | err = 0; |
940 | } | 940 | } |
941 | if (err == 1) { | 941 | if (err == 1) { |
942 | part_delall(&tmp_list); | 942 | part_delall(&tmp_list); |
943 | return 1; | 943 | return 1; |
944 | } | 944 | } |
945 | 945 | ||
946 | if (num_parts == 0) { | 946 | if (num_parts == 0) { |
947 | printf("no partitions for device %s%d (%s)\n", | 947 | printf("no partitions for device %s%d (%s)\n", |
948 | MTD_DEV_TYPE(id->type), id->num, id->mtd_id); | 948 | MTD_DEV_TYPE(id->type), id->num, id->mtd_id); |
949 | return 1; | 949 | return 1; |
950 | } | 950 | } |
951 | 951 | ||
952 | DEBUGF("\ntotal partitions: %d\n", num_parts); | 952 | DEBUGF("\ntotal partitions: %d\n", num_parts); |
953 | 953 | ||
954 | /* check for next device presence */ | 954 | /* check for next device presence */ |
955 | if (p) { | 955 | if (p) { |
956 | if (*p == ';') { | 956 | if (*p == ';') { |
957 | *ret = ++p; | 957 | *ret = ++p; |
958 | } else if (*p == '\0') { | 958 | } else if (*p == '\0') { |
959 | *ret = p; | 959 | *ret = p; |
960 | } else { | 960 | } else { |
961 | printf("unexpected character '%c' at the end of device\n", *p); | 961 | printf("unexpected character '%c' at the end of device\n", *p); |
962 | *ret = NULL; | 962 | *ret = NULL; |
963 | return 1; | 963 | return 1; |
964 | } | 964 | } |
965 | } | 965 | } |
966 | 966 | ||
967 | /* allocate memory for mtd_device structure */ | 967 | /* allocate memory for mtd_device structure */ |
968 | if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) { | 968 | if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) { |
969 | printf("out of memory\n"); | 969 | printf("out of memory\n"); |
970 | return 1; | 970 | return 1; |
971 | } | 971 | } |
972 | memset(dev, 0, sizeof(struct mtd_device)); | 972 | memset(dev, 0, sizeof(struct mtd_device)); |
973 | dev->id = id; | 973 | dev->id = id; |
974 | dev->num_parts = 0; /* part_sort_add increments num_parts */ | 974 | dev->num_parts = 0; /* part_sort_add increments num_parts */ |
975 | INIT_LIST_HEAD(&dev->parts); | 975 | INIT_LIST_HEAD(&dev->parts); |
976 | INIT_LIST_HEAD(&dev->link); | 976 | INIT_LIST_HEAD(&dev->link); |
977 | 977 | ||
978 | /* move partitions from tmp_list to dev->parts */ | 978 | /* move partitions from tmp_list to dev->parts */ |
979 | list_for_each_safe(entry, n, &tmp_list) { | 979 | list_for_each_safe(entry, n, &tmp_list) { |
980 | part = list_entry(entry, struct part_info, link); | 980 | part = list_entry(entry, struct part_info, link); |
981 | list_del(entry); | 981 | list_del(entry); |
982 | if (part_sort_add(dev, part) != 0) { | 982 | if (part_sort_add(dev, part) != 0) { |
983 | device_del(dev); | 983 | device_del(dev); |
984 | return 1; | 984 | return 1; |
985 | } | 985 | } |
986 | } | 986 | } |
987 | 987 | ||
988 | *retdev = dev; | 988 | *retdev = dev; |
989 | 989 | ||
990 | DEBUGF("===\n\n"); | 990 | DEBUGF("===\n\n"); |
991 | return 0; | 991 | return 0; |
992 | } | 992 | } |
993 | 993 | ||
994 | /** | 994 | /** |
995 | * Initialize global device list. | 995 | * Initialize global device list. |
996 | * | 996 | * |
997 | * @return 0 on success, 1 otherwise | 997 | * @return 0 on success, 1 otherwise |
998 | */ | 998 | */ |
999 | static int devices_init(void) | 999 | static int devices_init(void) |
1000 | { | 1000 | { |
1001 | last_parts[0] = '\0'; | 1001 | last_parts[0] = '\0'; |
1002 | current_dev = NULL; | 1002 | current_dev = NULL; |
1003 | current_save(); | 1003 | current_save(); |
1004 | 1004 | ||
1005 | return device_delall(&devices); | 1005 | return device_delall(&devices); |
1006 | } | 1006 | } |
1007 | 1007 | ||
1008 | /* | 1008 | /* |
1009 | * Search global mtdids list and find id of requested type and number. | 1009 | * Search global mtdids list and find id of requested type and number. |
1010 | * | 1010 | * |
1011 | * @return pointer to the id if it exists, NULL otherwise | 1011 | * @return pointer to the id if it exists, NULL otherwise |
1012 | */ | 1012 | */ |
1013 | static struct mtdids* id_find(u8 type, u8 num) | 1013 | static struct mtdids* id_find(u8 type, u8 num) |
1014 | { | 1014 | { |
1015 | struct list_head *entry; | 1015 | struct list_head *entry; |
1016 | struct mtdids *id; | 1016 | struct mtdids *id; |
1017 | 1017 | ||
1018 | list_for_each(entry, &mtdids) { | 1018 | list_for_each(entry, &mtdids) { |
1019 | id = list_entry(entry, struct mtdids, link); | 1019 | id = list_entry(entry, struct mtdids, link); |
1020 | 1020 | ||
1021 | if ((id->type == type) && (id->num == num)) | 1021 | if ((id->type == type) && (id->num == num)) |
1022 | return id; | 1022 | return id; |
1023 | } | 1023 | } |
1024 | 1024 | ||
1025 | return NULL; | 1025 | return NULL; |
1026 | } | 1026 | } |
1027 | 1027 | ||
1028 | /** | 1028 | /** |
1029 | * Search global mtdids list and find id of a requested mtd_id. | 1029 | * Search global mtdids list and find id of a requested mtd_id. |
1030 | * | 1030 | * |
1031 | * Note: first argument is not null terminated. | 1031 | * Note: first argument is not null terminated. |
1032 | * | 1032 | * |
1033 | * @param mtd_id string containing requested mtd_id | 1033 | * @param mtd_id string containing requested mtd_id |
1034 | * @param mtd_id_len length of supplied mtd_id | 1034 | * @param mtd_id_len length of supplied mtd_id |
1035 | * @return pointer to the id if it exists, NULL otherwise | 1035 | * @return pointer to the id if it exists, NULL otherwise |
1036 | */ | 1036 | */ |
1037 | static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len) | 1037 | static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len) |
1038 | { | 1038 | { |
1039 | struct list_head *entry; | 1039 | struct list_head *entry; |
1040 | struct mtdids *id; | 1040 | struct mtdids *id; |
1041 | 1041 | ||
1042 | DEBUGF("--- id_find_by_mtd_id: '%.*s' (len = %d)\n", | 1042 | DEBUGF("--- id_find_by_mtd_id: '%.*s' (len = %d)\n", |
1043 | mtd_id_len, mtd_id, mtd_id_len); | 1043 | mtd_id_len, mtd_id, mtd_id_len); |
1044 | 1044 | ||
1045 | list_for_each(entry, &mtdids) { | 1045 | list_for_each(entry, &mtdids) { |
1046 | id = list_entry(entry, struct mtdids, link); | 1046 | id = list_entry(entry, struct mtdids, link); |
1047 | 1047 | ||
1048 | DEBUGF("entry: '%s' (len = %d)\n", | 1048 | DEBUGF("entry: '%s' (len = %d)\n", |
1049 | id->mtd_id, strlen(id->mtd_id)); | 1049 | id->mtd_id, strlen(id->mtd_id)); |
1050 | 1050 | ||
1051 | if (mtd_id_len != strlen(id->mtd_id)) | 1051 | if (mtd_id_len != strlen(id->mtd_id)) |
1052 | continue; | 1052 | continue; |
1053 | if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0) | 1053 | if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0) |
1054 | return id; | 1054 | return id; |
1055 | } | 1055 | } |
1056 | 1056 | ||
1057 | return NULL; | 1057 | return NULL; |
1058 | } | 1058 | } |
1059 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ | 1059 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ |
1060 | 1060 | ||
1061 | /** | 1061 | /** |
1062 | * Parse device id string <dev-id> := 'nand'|'nor'<dev-num>, return device | 1062 | * Parse device id string <dev-id> := 'nand'|'nor'<dev-num>, return device |
1063 | * type and number. | 1063 | * type and number. |
1064 | * | 1064 | * |
1065 | * @param id string describing device id | 1065 | * @param id string describing device id |
1066 | * @param ret_id output pointer to next char after parse completes (output) | 1066 | * @param ret_id output pointer to next char after parse completes (output) |
1067 | * @param dev_type parsed device type (output) | 1067 | * @param dev_type parsed device type (output) |
1068 | * @param dev_num parsed device number (output) | 1068 | * @param dev_num parsed device number (output) |
1069 | * @return 0 on success, 1 otherwise | 1069 | * @return 0 on success, 1 otherwise |
1070 | */ | 1070 | */ |
1071 | int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num) | 1071 | int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num) |
1072 | { | 1072 | { |
1073 | const char *p = id; | 1073 | const char *p = id; |
1074 | 1074 | ||
1075 | *dev_type = 0; | 1075 | *dev_type = 0; |
1076 | if (strncmp(p, "nand", 4) == 0) { | 1076 | if (strncmp(p, "nand", 4) == 0) { |
1077 | *dev_type = MTD_DEV_TYPE_NAND; | 1077 | *dev_type = MTD_DEV_TYPE_NAND; |
1078 | p += 4; | 1078 | p += 4; |
1079 | } else if (strncmp(p, "nor", 3) == 0) { | 1079 | } else if (strncmp(p, "nor", 3) == 0) { |
1080 | *dev_type = MTD_DEV_TYPE_NOR; | 1080 | *dev_type = MTD_DEV_TYPE_NOR; |
1081 | p += 3; | 1081 | p += 3; |
1082 | } else { | 1082 | } else { |
1083 | printf("incorrect device type in %s\n", id); | 1083 | printf("incorrect device type in %s\n", id); |
1084 | return 1; | 1084 | return 1; |
1085 | } | 1085 | } |
1086 | 1086 | ||
1087 | if (!isdigit(*p)) { | 1087 | if (!isdigit(*p)) { |
1088 | printf("incorrect device number in %s\n", id); | 1088 | printf("incorrect device number in %s\n", id); |
1089 | return 1; | 1089 | return 1; |
1090 | } | 1090 | } |
1091 | 1091 | ||
1092 | *dev_num = simple_strtoul(p, (char **)&p, 0); | 1092 | *dev_num = simple_strtoul(p, (char **)&p, 0); |
1093 | if (ret_id) | 1093 | if (ret_id) |
1094 | *ret_id = p; | 1094 | *ret_id = p; |
1095 | return 0; | 1095 | return 0; |
1096 | } | 1096 | } |
1097 | 1097 | ||
1098 | #ifdef CONFIG_JFFS2_CMDLINE | 1098 | #ifdef CONFIG_JFFS2_CMDLINE |
1099 | /** | 1099 | /** |
1100 | * Process all devices and generate corresponding mtdparts string describing | 1100 | * Process all devices and generate corresponding mtdparts string describing |
1101 | * all partitions on all devices. | 1101 | * all partitions on all devices. |
1102 | * | 1102 | * |
1103 | * @param buf output buffer holding generated mtdparts string (output) | 1103 | * @param buf output buffer holding generated mtdparts string (output) |
1104 | * @param buflen buffer size | 1104 | * @param buflen buffer size |
1105 | * @return 0 on success, 1 otherwise | 1105 | * @return 0 on success, 1 otherwise |
1106 | */ | 1106 | */ |
1107 | static int generate_mtdparts(char *buf, u32 buflen) | 1107 | static int generate_mtdparts(char *buf, u32 buflen) |
1108 | { | 1108 | { |
1109 | struct list_head *pentry, *dentry; | 1109 | struct list_head *pentry, *dentry; |
1110 | struct mtd_device *dev; | 1110 | struct mtd_device *dev; |
1111 | struct part_info *part, *prev_part; | 1111 | struct part_info *part, *prev_part; |
1112 | char *p = buf; | 1112 | char *p = buf; |
1113 | char tmpbuf[32]; | 1113 | char tmpbuf[32]; |
1114 | u32 size, offset, len, part_cnt; | 1114 | u32 size, offset, len, part_cnt; |
1115 | u32 maxlen = buflen - 1; | 1115 | u32 maxlen = buflen - 1; |
1116 | 1116 | ||
1117 | DEBUGF("--- generate_mtdparts ---\n"); | 1117 | DEBUGF("--- generate_mtdparts ---\n"); |
1118 | 1118 | ||
1119 | if (list_empty(&devices)) { | 1119 | if (list_empty(&devices)) { |
1120 | buf[0] = '\0'; | 1120 | buf[0] = '\0'; |
1121 | return 0; | 1121 | return 0; |
1122 | } | 1122 | } |
1123 | 1123 | ||
1124 | sprintf(p, "mtdparts="); | 1124 | sprintf(p, "mtdparts="); |
1125 | p += 9; | 1125 | p += 9; |
1126 | 1126 | ||
1127 | list_for_each(dentry, &devices) { | 1127 | list_for_each(dentry, &devices) { |
1128 | dev = list_entry(dentry, struct mtd_device, link); | 1128 | dev = list_entry(dentry, struct mtd_device, link); |
1129 | 1129 | ||
1130 | /* copy mtd_id */ | 1130 | /* copy mtd_id */ |
1131 | len = strlen(dev->id->mtd_id) + 1; | 1131 | len = strlen(dev->id->mtd_id) + 1; |
1132 | if (len > maxlen) | 1132 | if (len > maxlen) |
1133 | goto cleanup; | 1133 | goto cleanup; |
1134 | memcpy(p, dev->id->mtd_id, len - 1); | 1134 | memcpy(p, dev->id->mtd_id, len - 1); |
1135 | p += len - 1; | 1135 | p += len - 1; |
1136 | *(p++) = ':'; | 1136 | *(p++) = ':'; |
1137 | maxlen -= len; | 1137 | maxlen -= len; |
1138 | 1138 | ||
1139 | /* format partitions */ | 1139 | /* format partitions */ |
1140 | prev_part = NULL; | 1140 | prev_part = NULL; |
1141 | part_cnt = 0; | 1141 | part_cnt = 0; |
1142 | list_for_each(pentry, &dev->parts) { | 1142 | list_for_each(pentry, &dev->parts) { |
1143 | part = list_entry(pentry, struct part_info, link); | 1143 | part = list_entry(pentry, struct part_info, link); |
1144 | size = part->size; | 1144 | size = part->size; |
1145 | offset = part->offset; | 1145 | offset = part->offset; |
1146 | part_cnt++; | 1146 | part_cnt++; |
1147 | 1147 | ||
1148 | /* partition size */ | 1148 | /* partition size */ |
1149 | memsize_format(tmpbuf, size); | 1149 | memsize_format(tmpbuf, size); |
1150 | len = strlen(tmpbuf); | 1150 | len = strlen(tmpbuf); |
1151 | if (len > maxlen) | 1151 | if (len > maxlen) |
1152 | goto cleanup; | 1152 | goto cleanup; |
1153 | memcpy(p, tmpbuf, len); | 1153 | memcpy(p, tmpbuf, len); |
1154 | p += len; | 1154 | p += len; |
1155 | maxlen -= len; | 1155 | maxlen -= len; |
1156 | 1156 | ||
1157 | 1157 | ||
1158 | /* add offset only when there is a gap between | 1158 | /* add offset only when there is a gap between |
1159 | * partitions */ | 1159 | * partitions */ |
1160 | if ((!prev_part && (offset != 0)) || | 1160 | if ((!prev_part && (offset != 0)) || |
1161 | (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) { | 1161 | (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) { |
1162 | 1162 | ||
1163 | memsize_format(tmpbuf, offset); | 1163 | memsize_format(tmpbuf, offset); |
1164 | len = strlen(tmpbuf) + 1; | 1164 | len = strlen(tmpbuf) + 1; |
1165 | if (len > maxlen) | 1165 | if (len > maxlen) |
1166 | goto cleanup; | 1166 | goto cleanup; |
1167 | *(p++) = '@'; | 1167 | *(p++) = '@'; |
1168 | memcpy(p, tmpbuf, len - 1); | 1168 | memcpy(p, tmpbuf, len - 1); |
1169 | p += len - 1; | 1169 | p += len - 1; |
1170 | maxlen -= len; | 1170 | maxlen -= len; |
1171 | } | 1171 | } |
1172 | 1172 | ||
1173 | /* copy name only if user supplied */ | 1173 | /* copy name only if user supplied */ |
1174 | if(!part->auto_name) { | 1174 | if(!part->auto_name) { |
1175 | len = strlen(part->name) + 2; | 1175 | len = strlen(part->name) + 2; |
1176 | if (len > maxlen) | 1176 | if (len > maxlen) |
1177 | goto cleanup; | 1177 | goto cleanup; |
1178 | 1178 | ||
1179 | *(p++) = '('; | 1179 | *(p++) = '('; |
1180 | memcpy(p, part->name, len - 2); | 1180 | memcpy(p, part->name, len - 2); |
1181 | p += len - 2; | 1181 | p += len - 2; |
1182 | *(p++) = ')'; | 1182 | *(p++) = ')'; |
1183 | maxlen -= len; | 1183 | maxlen -= len; |
1184 | } | 1184 | } |
1185 | 1185 | ||
1186 | /* ro mask flag */ | 1186 | /* ro mask flag */ |
1187 | if (part->mask_flags && MTD_WRITEABLE_CMD) { | 1187 | if (part->mask_flags && MTD_WRITEABLE_CMD) { |
1188 | len = 2; | 1188 | len = 2; |
1189 | if (len > maxlen) | 1189 | if (len > maxlen) |
1190 | goto cleanup; | 1190 | goto cleanup; |
1191 | *(p++) = 'r'; | 1191 | *(p++) = 'r'; |
1192 | *(p++) = 'o'; | 1192 | *(p++) = 'o'; |
1193 | maxlen -= 2; | 1193 | maxlen -= 2; |
1194 | } | 1194 | } |
1195 | 1195 | ||
1196 | /* print ',' separator if there are other partitions | 1196 | /* print ',' separator if there are other partitions |
1197 | * following */ | 1197 | * following */ |
1198 | if (dev->num_parts > part_cnt) { | 1198 | if (dev->num_parts > part_cnt) { |
1199 | if (1 > maxlen) | 1199 | if (1 > maxlen) |
1200 | goto cleanup; | 1200 | goto cleanup; |
1201 | *(p++) = ','; | 1201 | *(p++) = ','; |
1202 | maxlen--; | 1202 | maxlen--; |
1203 | } | 1203 | } |
1204 | prev_part = part; | 1204 | prev_part = part; |
1205 | } | 1205 | } |
1206 | /* print ';' separator if there are other devices following */ | 1206 | /* print ';' separator if there are other devices following */ |
1207 | if (dentry->next != &devices) { | 1207 | if (dentry->next != &devices) { |
1208 | if (1 > maxlen) | 1208 | if (1 > maxlen) |
1209 | goto cleanup; | 1209 | goto cleanup; |
1210 | *(p++) = ';'; | 1210 | *(p++) = ';'; |
1211 | maxlen--; | 1211 | maxlen--; |
1212 | } | 1212 | } |
1213 | } | 1213 | } |
1214 | 1214 | ||
1215 | /* we still have at least one char left, as we decremented maxlen at | 1215 | /* we still have at least one char left, as we decremented maxlen at |
1216 | * the begining */ | 1216 | * the begining */ |
1217 | *p = '\0'; | 1217 | *p = '\0'; |
1218 | 1218 | ||
1219 | return 0; | 1219 | return 0; |
1220 | 1220 | ||
1221 | cleanup: | 1221 | cleanup: |
1222 | last_parts[0] = '\0'; | 1222 | last_parts[0] = '\0'; |
1223 | return 1; | 1223 | return 1; |
1224 | } | 1224 | } |
1225 | 1225 | ||
1226 | /** | 1226 | /** |
1227 | * Call generate_mtdparts to process all devices and generate corresponding | 1227 | * Call generate_mtdparts to process all devices and generate corresponding |
1228 | * mtdparts string, save it in mtdparts environment variable. | 1228 | * mtdparts string, save it in mtdparts environment variable. |
1229 | * | 1229 | * |
1230 | * @param buf output buffer holding generated mtdparts string (output) | 1230 | * @param buf output buffer holding generated mtdparts string (output) |
1231 | * @param buflen buffer size | 1231 | * @param buflen buffer size |
1232 | * @return 0 on success, 1 otherwise | 1232 | * @return 0 on success, 1 otherwise |
1233 | */ | 1233 | */ |
1234 | static int generate_mtdparts_save(char *buf, u32 buflen) | 1234 | static int generate_mtdparts_save(char *buf, u32 buflen) |
1235 | { | 1235 | { |
1236 | int ret; | 1236 | int ret; |
1237 | 1237 | ||
1238 | ret = generate_mtdparts(buf, buflen); | 1238 | ret = generate_mtdparts(buf, buflen); |
1239 | 1239 | ||
1240 | if ((buf[0] != '\0') && (ret == 0)) | 1240 | if ((buf[0] != '\0') && (ret == 0)) |
1241 | setenv("mtdparts", buf); | 1241 | setenv("mtdparts", buf); |
1242 | else | 1242 | else |
1243 | setenv("mtdparts", NULL); | 1243 | setenv("mtdparts", NULL); |
1244 | 1244 | ||
1245 | return ret; | 1245 | return ret; |
1246 | } | 1246 | } |
1247 | 1247 | ||
1248 | /** | 1248 | /** |
1249 | * Format and print out a partition list for each device from global device | 1249 | * Format and print out a partition list for each device from global device |
1250 | * list. | 1250 | * list. |
1251 | */ | 1251 | */ |
1252 | static void list_partitions(void) | 1252 | static void list_partitions(void) |
1253 | { | 1253 | { |
1254 | struct list_head *dentry, *pentry; | 1254 | struct list_head *dentry, *pentry; |
1255 | struct part_info *part; | 1255 | struct part_info *part; |
1256 | struct mtd_device *dev; | 1256 | struct mtd_device *dev; |
1257 | int part_num; | 1257 | int part_num; |
1258 | 1258 | ||
1259 | DEBUGF("\n---list_partitions---\n"); | 1259 | DEBUGF("\n---list_partitions---\n"); |
1260 | list_for_each(dentry, &devices) { | 1260 | list_for_each(dentry, &devices) { |
1261 | dev = list_entry(dentry, struct mtd_device, link); | 1261 | dev = list_entry(dentry, struct mtd_device, link); |
1262 | printf("\ndevice %s%d <%s>, # parts = %d\n", | 1262 | printf("\ndevice %s%d <%s>, # parts = %d\n", |
1263 | MTD_DEV_TYPE(dev->id->type), dev->id->num, | 1263 | MTD_DEV_TYPE(dev->id->type), dev->id->num, |
1264 | dev->id->mtd_id, dev->num_parts); | 1264 | dev->id->mtd_id, dev->num_parts); |
1265 | printf(" #: name\t\t\tsize\t\toffset\t\tmask_flags\n"); | 1265 | printf(" #: name\t\t\tsize\t\toffset\t\tmask_flags\n"); |
1266 | 1266 | ||
1267 | /* list partitions for given device */ | 1267 | /* list partitions for given device */ |
1268 | part_num = 0; | 1268 | part_num = 0; |
1269 | list_for_each(pentry, &dev->parts) { | 1269 | list_for_each(pentry, &dev->parts) { |
1270 | part = list_entry(pentry, struct part_info, link); | 1270 | part = list_entry(pentry, struct part_info, link); |
1271 | printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", | 1271 | printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", |
1272 | part_num, part->name, part->size, | 1272 | part_num, part->name, part->size, |
1273 | part->offset, part->mask_flags); | 1273 | part->offset, part->mask_flags); |
1274 | 1274 | ||
1275 | part_num++; | 1275 | part_num++; |
1276 | } | 1276 | } |
1277 | } | 1277 | } |
1278 | if (list_empty(&devices)) | 1278 | if (list_empty(&devices)) |
1279 | printf("no partitions defined\n"); | 1279 | printf("no partitions defined\n"); |
1280 | 1280 | ||
1281 | /* current_dev is not NULL only when we have non empty device list */ | 1281 | /* current_dev is not NULL only when we have non empty device list */ |
1282 | if (current_dev) { | 1282 | if (current_dev) { |
1283 | part = jffs2_part_info(current_dev, current_partnum); | 1283 | part = jffs2_part_info(current_dev, current_partnum); |
1284 | if (part) { | 1284 | if (part) { |
1285 | printf("\nactive partition: %s%d,%d - (%s) 0x%08lx @ 0x%08lx\n", | 1285 | printf("\nactive partition: %s%d,%d - (%s) 0x%08lx @ 0x%08lx\n", |
1286 | MTD_DEV_TYPE(current_dev->id->type), | 1286 | MTD_DEV_TYPE(current_dev->id->type), |
1287 | current_dev->id->num, current_partnum, | 1287 | current_dev->id->num, current_partnum, |
1288 | part->name, part->size, part->offset); | 1288 | part->name, part->size, part->offset); |
1289 | } else { | 1289 | } else { |
1290 | printf("could not get current partition info\n\n"); | 1290 | printf("could not get current partition info\n\n"); |
1291 | } | 1291 | } |
1292 | } | 1292 | } |
1293 | 1293 | ||
1294 | printf("\ndefaults:\n"); | 1294 | printf("\ndefaults:\n"); |
1295 | printf("mtdids : %s\n", mtdids_default); | 1295 | printf("mtdids : %s\n", mtdids_default); |
1296 | printf("mtdparts: %s\n", mtdparts_default); | 1296 | printf("mtdparts: %s\n", mtdparts_default); |
1297 | } | 1297 | } |
1298 | 1298 | ||
1299 | /** | 1299 | /** |
1300 | * Given partition identifier in form of <dev_type><dev_num>,<part_num> find | 1300 | * Given partition identifier in form of <dev_type><dev_num>,<part_num> find |
1301 | * corresponding device and verify partition number. | 1301 | * corresponding device and verify partition number. |
1302 | * | 1302 | * |
1303 | * @param id string describing device and partition | 1303 | * @param id string describing device and partition or partition name |
1304 | * @param dev pointer to the requested device (output) | 1304 | * @param dev pointer to the requested device (output) |
1305 | * @param part_num verified partition number (output) | 1305 | * @param part_num verified partition number (output) |
1306 | * @param part pointer to requested partition (output) | 1306 | * @param part pointer to requested partition (output) |
1307 | * @return 0 on success, 1 otherwise | 1307 | * @return 0 on success, 1 otherwise |
1308 | */ | 1308 | */ |
1309 | int find_dev_and_part(const char *id, struct mtd_device **dev, | 1309 | int find_dev_and_part(const char *id, struct mtd_device **dev, |
1310 | u8 *part_num, struct part_info **part) | 1310 | u8 *part_num, struct part_info **part) |
1311 | { | 1311 | { |
1312 | struct list_head *dentry, *pentry; | ||
1312 | u8 type, dnum, pnum; | 1313 | u8 type, dnum, pnum; |
1313 | const char *p; | 1314 | const char *p; |
1314 | 1315 | ||
1315 | DEBUGF("--- find_dev_and_part ---\nid = %s\n", id); | 1316 | DEBUGF("--- find_dev_and_part ---\nid = %s\n", id); |
1317 | |||
1318 | list_for_each(dentry, &devices) { | ||
1319 | *part_num = 0; | ||
1320 | *dev = list_entry(dentry, struct mtd_device, link); | ||
1321 | list_for_each(pentry, &(*dev)->parts) { | ||
1322 | *part = list_entry(pentry, struct part_info, link); | ||
1323 | if (strcmp((*part)->name, id) == 0) | ||
1324 | return 0; | ||
1325 | (*part_num)++; | ||
1326 | } | ||
1327 | } | ||
1316 | 1328 | ||
1317 | p = id; | 1329 | p = id; |
1318 | *dev = NULL; | 1330 | *dev = NULL; |
1319 | *part = NULL; | 1331 | *part = NULL; |
1320 | *part_num = 0; | 1332 | *part_num = 0; |
1321 | 1333 | ||
1322 | if (id_parse(p, &p, &type, &dnum) != 0) | 1334 | if (id_parse(p, &p, &type, &dnum) != 0) |
1323 | return 1; | 1335 | return 1; |
1324 | 1336 | ||
1325 | if ((*p++ != ',') || (*p == '\0')) { | 1337 | if ((*p++ != ',') || (*p == '\0')) { |
1326 | printf("no partition number specified\n"); | 1338 | printf("no partition number specified\n"); |
1327 | return 1; | 1339 | return 1; |
1328 | } | 1340 | } |
1329 | pnum = simple_strtoul(p, (char **)&p, 0); | 1341 | pnum = simple_strtoul(p, (char **)&p, 0); |
1330 | if (*p != '\0') { | 1342 | if (*p != '\0') { |
1331 | printf("unexpected trailing character '%c'\n", *p); | 1343 | printf("unexpected trailing character '%c'\n", *p); |
1332 | return 1; | 1344 | return 1; |
1333 | } | 1345 | } |
1334 | 1346 | ||
1335 | if ((*dev = device_find(type, dnum)) == NULL) { | 1347 | if ((*dev = device_find(type, dnum)) == NULL) { |
1336 | printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum); | 1348 | printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum); |
1337 | return 1; | 1349 | return 1; |
1338 | } | 1350 | } |
1339 | 1351 | ||
1340 | if ((*part = jffs2_part_info(*dev, pnum)) == NULL) { | 1352 | if ((*part = jffs2_part_info(*dev, pnum)) == NULL) { |
1341 | printf("no such partition\n"); | 1353 | printf("no such partition\n"); |
1342 | *dev = NULL; | 1354 | *dev = NULL; |
1343 | return 1; | 1355 | return 1; |
1344 | } | 1356 | } |
1345 | 1357 | ||
1346 | *part_num = pnum; | 1358 | *part_num = pnum; |
1347 | 1359 | ||
1348 | return 0; | 1360 | return 0; |
1349 | } | 1361 | } |
1350 | 1362 | ||
1351 | /** | 1363 | /** |
1352 | * Find and delete partition. For partition id format see find_dev_and_part(). | 1364 | * Find and delete partition. For partition id format see find_dev_and_part(). |
1353 | * | 1365 | * |
1354 | * @param id string describing device and partition | 1366 | * @param id string describing device and partition |
1355 | * @return 0 on success, 1 otherwise | 1367 | * @return 0 on success, 1 otherwise |
1356 | */ | 1368 | */ |
1357 | static int delete_partition(const char *id) | 1369 | static int delete_partition(const char *id) |
1358 | { | 1370 | { |
1359 | u8 pnum; | 1371 | u8 pnum; |
1360 | struct mtd_device *dev; | 1372 | struct mtd_device *dev; |
1361 | struct part_info *part; | 1373 | struct part_info *part; |
1362 | 1374 | ||
1363 | if (find_dev_and_part(id, &dev, &pnum, &part) == 0) { | 1375 | if (find_dev_and_part(id, &dev, &pnum, &part) == 0) { |
1364 | 1376 | ||
1365 | DEBUGF("delete_partition: device = %s%d, partition %d = (%s) 0x%08lx@0x%08lx\n", | 1377 | DEBUGF("delete_partition: device = %s%d, partition %d = (%s) 0x%08lx@0x%08lx\n", |
1366 | MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum, | 1378 | MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum, |
1367 | part->name, part->size, part->offset); | 1379 | part->name, part->size, part->offset); |
1368 | 1380 | ||
1369 | if (part_del(dev, part) != 0) | 1381 | if (part_del(dev, part) != 0) |
1370 | return 1; | 1382 | return 1; |
1371 | 1383 | ||
1372 | if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { | 1384 | if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { |
1373 | printf("generated mtdparts too long, reseting to null\n"); | 1385 | printf("generated mtdparts too long, reseting to null\n"); |
1374 | return 1; | 1386 | return 1; |
1375 | } | 1387 | } |
1376 | return 0; | 1388 | return 0; |
1377 | } | 1389 | } |
1378 | 1390 | ||
1379 | printf("partition %s not found\n", id); | 1391 | printf("partition %s not found\n", id); |
1380 | return 1; | 1392 | return 1; |
1381 | } | 1393 | } |
1382 | 1394 | ||
1383 | /** | 1395 | /** |
1384 | * Accept character string describing mtd partitions and call device_parse() | 1396 | * Accept character string describing mtd partitions and call device_parse() |
1385 | * for each entry. Add created devices to the global devices list. | 1397 | * for each entry. Add created devices to the global devices list. |
1386 | * | 1398 | * |
1387 | * @param mtdparts string specifing mtd partitions | 1399 | * @param mtdparts string specifing mtd partitions |
1388 | * @return 0 on success, 1 otherwise | 1400 | * @return 0 on success, 1 otherwise |
1389 | */ | 1401 | */ |
1390 | static int parse_mtdparts(const char *const mtdparts) | 1402 | static int parse_mtdparts(const char *const mtdparts) |
1391 | { | 1403 | { |
1392 | const char *p = mtdparts; | 1404 | const char *p = mtdparts; |
1393 | struct mtd_device *dev; | 1405 | struct mtd_device *dev; |
1394 | int err = 1; | 1406 | int err = 1; |
1395 | 1407 | ||
1396 | DEBUGF("\n---parse_mtdparts---\nmtdparts = %s\n\n", p); | 1408 | DEBUGF("\n---parse_mtdparts---\nmtdparts = %s\n\n", p); |
1397 | 1409 | ||
1398 | /* delete all devices and partitions */ | 1410 | /* delete all devices and partitions */ |
1399 | if (devices_init() != 0) { | 1411 | if (devices_init() != 0) { |
1400 | printf("could not initialise device list\n"); | 1412 | printf("could not initialise device list\n"); |
1401 | return err; | 1413 | return err; |
1402 | } | 1414 | } |
1403 | 1415 | ||
1404 | /* re-read 'mtdparts' variable, devices_init may be updating env */ | 1416 | /* re-read 'mtdparts' variable, devices_init may be updating env */ |
1405 | p = getenv("mtdparts"); | 1417 | p = getenv("mtdparts"); |
1406 | 1418 | ||
1407 | if (strncmp(p, "mtdparts=", 9) != 0) { | 1419 | if (strncmp(p, "mtdparts=", 9) != 0) { |
1408 | printf("mtdparts variable doesn't start with 'mtdparts='\n"); | 1420 | printf("mtdparts variable doesn't start with 'mtdparts='\n"); |
1409 | return err; | 1421 | return err; |
1410 | } | 1422 | } |
1411 | p += 9; | 1423 | p += 9; |
1412 | 1424 | ||
1413 | while (p && (*p != '\0')) { | 1425 | while (p && (*p != '\0')) { |
1414 | err = 1; | 1426 | err = 1; |
1415 | if ((device_parse(p, &p, &dev) != 0) || (!dev)) | 1427 | if ((device_parse(p, &p, &dev) != 0) || (!dev)) |
1416 | break; | 1428 | break; |
1417 | 1429 | ||
1418 | DEBUGF("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), | 1430 | DEBUGF("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), |
1419 | dev->id->num, dev->id->mtd_id); | 1431 | dev->id->num, dev->id->mtd_id); |
1420 | 1432 | ||
1421 | /* check if parsed device is already on the list */ | 1433 | /* check if parsed device is already on the list */ |
1422 | if (device_find(dev->id->type, dev->id->num) != NULL) { | 1434 | if (device_find(dev->id->type, dev->id->num) != NULL) { |
1423 | printf("device %s%d redefined, please correct mtdparts variable\n", | 1435 | printf("device %s%d redefined, please correct mtdparts variable\n", |
1424 | MTD_DEV_TYPE(dev->id->type), dev->id->num); | 1436 | MTD_DEV_TYPE(dev->id->type), dev->id->num); |
1425 | break; | 1437 | break; |
1426 | } | 1438 | } |
1427 | 1439 | ||
1428 | list_add_tail(&dev->link, &devices); | 1440 | list_add_tail(&dev->link, &devices); |
1429 | err = 0; | 1441 | err = 0; |
1430 | } | 1442 | } |
1431 | if (err == 1) { | 1443 | if (err == 1) { |
1432 | device_delall(&devices); | 1444 | device_delall(&devices); |
1433 | return 1; | 1445 | return 1; |
1434 | } | 1446 | } |
1435 | 1447 | ||
1436 | return 0; | 1448 | return 0; |
1437 | } | 1449 | } |
1438 | 1450 | ||
1439 | /** | 1451 | /** |
1440 | * Parse provided string describing mtdids mapping (see file header for mtdids | 1452 | * Parse provided string describing mtdids mapping (see file header for mtdids |
1441 | * variable format). Allocate memory for each entry and add all found entries | 1453 | * variable format). Allocate memory for each entry and add all found entries |
1442 | * to the global mtdids list. | 1454 | * to the global mtdids list. |
1443 | * | 1455 | * |
1444 | * @param ids mapping string | 1456 | * @param ids mapping string |
1445 | * @return 0 on success, 1 otherwise | 1457 | * @return 0 on success, 1 otherwise |
1446 | */ | 1458 | */ |
1447 | static int parse_mtdids(const char *const ids) | 1459 | static int parse_mtdids(const char *const ids) |
1448 | { | 1460 | { |
1449 | const char *p = ids; | 1461 | const char *p = ids; |
1450 | const char *mtd_id; | 1462 | const char *mtd_id; |
1451 | int mtd_id_len; | 1463 | int mtd_id_len; |
1452 | struct mtdids *id; | 1464 | struct mtdids *id; |
1453 | struct list_head *entry, *n; | 1465 | struct list_head *entry, *n; |
1454 | struct mtdids *id_tmp; | 1466 | struct mtdids *id_tmp; |
1455 | u8 type, num; | 1467 | u8 type, num; |
1456 | u32 size; | 1468 | u32 size; |
1457 | int ret = 1; | 1469 | int ret = 1; |
1458 | 1470 | ||
1459 | DEBUGF("\n---parse_mtdids---\nmtdids = %s\n\n", ids); | 1471 | DEBUGF("\n---parse_mtdids---\nmtdids = %s\n\n", ids); |
1460 | 1472 | ||
1461 | /* clean global mtdids list */ | 1473 | /* clean global mtdids list */ |
1462 | list_for_each_safe(entry, n, &mtdids) { | 1474 | list_for_each_safe(entry, n, &mtdids) { |
1463 | id_tmp = list_entry(entry, struct mtdids, link); | 1475 | id_tmp = list_entry(entry, struct mtdids, link); |
1464 | DEBUGF("mtdids del: %d %d\n", id_tmp->type, id_tmp->num); | 1476 | DEBUGF("mtdids del: %d %d\n", id_tmp->type, id_tmp->num); |
1465 | list_del(entry); | 1477 | list_del(entry); |
1466 | free(id_tmp); | 1478 | free(id_tmp); |
1467 | } | 1479 | } |
1468 | last_ids[0] = '\0'; | 1480 | last_ids[0] = '\0'; |
1469 | INIT_LIST_HEAD(&mtdids); | 1481 | INIT_LIST_HEAD(&mtdids); |
1470 | 1482 | ||
1471 | while(p && (*p != '\0')) { | 1483 | while(p && (*p != '\0')) { |
1472 | 1484 | ||
1473 | ret = 1; | 1485 | ret = 1; |
1474 | /* parse 'nor'|'nand'<dev-num> */ | 1486 | /* parse 'nor'|'nand'<dev-num> */ |
1475 | if (id_parse(p, &p, &type, &num) != 0) | 1487 | if (id_parse(p, &p, &type, &num) != 0) |
1476 | break; | 1488 | break; |
1477 | 1489 | ||
1478 | if (*p != '=') { | 1490 | if (*p != '=') { |
1479 | printf("mtdids: incorrect <dev-num>\n"); | 1491 | printf("mtdids: incorrect <dev-num>\n"); |
1480 | break; | 1492 | break; |
1481 | } | 1493 | } |
1482 | p++; | 1494 | p++; |
1483 | 1495 | ||
1484 | /* check if requested device exists */ | 1496 | /* check if requested device exists */ |
1485 | if (device_validate(type, num, &size) != 0) | 1497 | if (device_validate(type, num, &size) != 0) |
1486 | return 1; | 1498 | return 1; |
1487 | 1499 | ||
1488 | /* locate <mtd-id> */ | 1500 | /* locate <mtd-id> */ |
1489 | mtd_id = p; | 1501 | mtd_id = p; |
1490 | if ((p = strchr(mtd_id, ',')) != NULL) { | 1502 | if ((p = strchr(mtd_id, ',')) != NULL) { |
1491 | mtd_id_len = p - mtd_id + 1; | 1503 | mtd_id_len = p - mtd_id + 1; |
1492 | p++; | 1504 | p++; |
1493 | } else { | 1505 | } else { |
1494 | mtd_id_len = strlen(mtd_id) + 1; | 1506 | mtd_id_len = strlen(mtd_id) + 1; |
1495 | } | 1507 | } |
1496 | if (mtd_id_len == 0) { | 1508 | if (mtd_id_len == 0) { |
1497 | printf("mtdids: no <mtd-id> identifier\n"); | 1509 | printf("mtdids: no <mtd-id> identifier\n"); |
1498 | break; | 1510 | break; |
1499 | } | 1511 | } |
1500 | 1512 | ||
1501 | /* check if this id is already on the list */ | 1513 | /* check if this id is already on the list */ |
1502 | int double_entry = 0; | 1514 | int double_entry = 0; |
1503 | list_for_each(entry, &mtdids) { | 1515 | list_for_each(entry, &mtdids) { |
1504 | id_tmp = list_entry(entry, struct mtdids, link); | 1516 | id_tmp = list_entry(entry, struct mtdids, link); |
1505 | if ((id_tmp->type == type) && (id_tmp->num == num)) { | 1517 | if ((id_tmp->type == type) && (id_tmp->num == num)) { |
1506 | double_entry = 1; | 1518 | double_entry = 1; |
1507 | break; | 1519 | break; |
1508 | } | 1520 | } |
1509 | } | 1521 | } |
1510 | if (double_entry) { | 1522 | if (double_entry) { |
1511 | printf("device id %s%d redefined, please correct mtdids variable\n", | 1523 | printf("device id %s%d redefined, please correct mtdids variable\n", |
1512 | MTD_DEV_TYPE(type), num); | 1524 | MTD_DEV_TYPE(type), num); |
1513 | break; | 1525 | break; |
1514 | } | 1526 | } |
1515 | 1527 | ||
1516 | /* allocate mtdids structure */ | 1528 | /* allocate mtdids structure */ |
1517 | if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) { | 1529 | if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) { |
1518 | printf("out of memory\n"); | 1530 | printf("out of memory\n"); |
1519 | break; | 1531 | break; |
1520 | } | 1532 | } |
1521 | memset(id, 0, sizeof(struct mtdids) + mtd_id_len); | 1533 | memset(id, 0, sizeof(struct mtdids) + mtd_id_len); |
1522 | id->num = num; | 1534 | id->num = num; |
1523 | id->type = type; | 1535 | id->type = type; |
1524 | id->size = size; | 1536 | id->size = size; |
1525 | id->mtd_id = (char *)(id + 1); | 1537 | id->mtd_id = (char *)(id + 1); |
1526 | strncpy(id->mtd_id, mtd_id, mtd_id_len - 1); | 1538 | strncpy(id->mtd_id, mtd_id, mtd_id_len - 1); |
1527 | id->mtd_id[mtd_id_len - 1] = '\0'; | 1539 | id->mtd_id[mtd_id_len - 1] = '\0'; |
1528 | INIT_LIST_HEAD(&id->link); | 1540 | INIT_LIST_HEAD(&id->link); |
1529 | 1541 | ||
1530 | DEBUGF("+ id %s%d\t%16d bytes\t%s\n", | 1542 | DEBUGF("+ id %s%d\t%16d bytes\t%s\n", |
1531 | MTD_DEV_TYPE(id->type), id->num, | 1543 | MTD_DEV_TYPE(id->type), id->num, |
1532 | id->size, id->mtd_id); | 1544 | id->size, id->mtd_id); |
1533 | 1545 | ||
1534 | list_add_tail(&id->link, &mtdids); | 1546 | list_add_tail(&id->link, &mtdids); |
1535 | ret = 0; | 1547 | ret = 0; |
1536 | } | 1548 | } |
1537 | if (ret == 1) { | 1549 | if (ret == 1) { |
1538 | /* clean mtdids list and free allocated memory */ | 1550 | /* clean mtdids list and free allocated memory */ |
1539 | list_for_each_safe(entry, n, &mtdids) { | 1551 | list_for_each_safe(entry, n, &mtdids) { |
1540 | id_tmp = list_entry(entry, struct mtdids, link); | 1552 | id_tmp = list_entry(entry, struct mtdids, link); |
1541 | list_del(entry); | 1553 | list_del(entry); |
1542 | free(id_tmp); | 1554 | free(id_tmp); |
1543 | } | 1555 | } |
1544 | return 1; | 1556 | return 1; |
1545 | } | 1557 | } |
1546 | 1558 | ||
1547 | return 0; | 1559 | return 0; |
1548 | } | 1560 | } |
1549 | 1561 | ||
1550 | /** | 1562 | /** |
1551 | * Parse and initialize global mtdids mapping and create global | 1563 | * Parse and initialize global mtdids mapping and create global |
1552 | * device/partition list. | 1564 | * device/partition list. |
1553 | * | 1565 | * |
1554 | * @return 0 on success, 1 otherwise | 1566 | * @return 0 on success, 1 otherwise |
1555 | */ | 1567 | */ |
1556 | int mtdparts_init(void) | 1568 | int mtdparts_init(void) |
1557 | { | 1569 | { |
1558 | static int initialized = 0; | 1570 | static int initialized = 0; |
1559 | const char *ids, *parts; | 1571 | const char *ids, *parts; |
1560 | const char *current_partition; | 1572 | const char *current_partition; |
1561 | int ids_changed; | 1573 | int ids_changed; |
1562 | char tmp_ep[PARTITION_MAXLEN]; | 1574 | char tmp_ep[PARTITION_MAXLEN]; |
1563 | 1575 | ||
1564 | DEBUGF("\n---mtdparts_init---\n"); | 1576 | DEBUGF("\n---mtdparts_init---\n"); |
1565 | if (!initialized) { | 1577 | if (!initialized) { |
1566 | INIT_LIST_HEAD(&mtdids); | 1578 | INIT_LIST_HEAD(&mtdids); |
1567 | INIT_LIST_HEAD(&devices); | 1579 | INIT_LIST_HEAD(&devices); |
1568 | memset(last_ids, 0, MTDIDS_MAXLEN); | 1580 | memset(last_ids, 0, MTDIDS_MAXLEN); |
1569 | memset(last_parts, 0, MTDPARTS_MAXLEN); | 1581 | memset(last_parts, 0, MTDPARTS_MAXLEN); |
1570 | memset(last_partition, 0, PARTITION_MAXLEN); | 1582 | memset(last_partition, 0, PARTITION_MAXLEN); |
1571 | initialized = 1; | 1583 | initialized = 1; |
1572 | } | 1584 | } |
1573 | 1585 | ||
1574 | /* get variables */ | 1586 | /* get variables */ |
1575 | ids = getenv("mtdids"); | 1587 | ids = getenv("mtdids"); |
1576 | parts = getenv("mtdparts"); | 1588 | parts = getenv("mtdparts"); |
1577 | current_partition = getenv("partition"); | 1589 | current_partition = getenv("partition"); |
1578 | 1590 | ||
1579 | /* save it for later parsing, cannot rely on current partition pointer | 1591 | /* save it for later parsing, cannot rely on current partition pointer |
1580 | * as 'partition' variable may be updated during init */ | 1592 | * as 'partition' variable may be updated during init */ |
1581 | tmp_ep[0] = '\0'; | 1593 | tmp_ep[0] = '\0'; |
1582 | if (current_partition) | 1594 | if (current_partition) |
1583 | strncpy(tmp_ep, current_partition, PARTITION_MAXLEN); | 1595 | strncpy(tmp_ep, current_partition, PARTITION_MAXLEN); |
1584 | 1596 | ||
1585 | DEBUGF("last_ids : %s\n", last_ids); | 1597 | DEBUGF("last_ids : %s\n", last_ids); |
1586 | DEBUGF("env_ids : %s\n", ids); | 1598 | DEBUGF("env_ids : %s\n", ids); |
1587 | DEBUGF("last_parts: %s\n", last_parts); | 1599 | DEBUGF("last_parts: %s\n", last_parts); |
1588 | DEBUGF("env_parts : %s\n\n", parts); | 1600 | DEBUGF("env_parts : %s\n\n", parts); |
1589 | 1601 | ||
1590 | DEBUGF("last_partition : %s\n", last_partition); | 1602 | DEBUGF("last_partition : %s\n", last_partition); |
1591 | DEBUGF("env_partition : %s\n", current_partition); | 1603 | DEBUGF("env_partition : %s\n", current_partition); |
1592 | 1604 | ||
1593 | /* if mtdids varible is empty try to use defaults */ | 1605 | /* if mtdids varible is empty try to use defaults */ |
1594 | if (!ids) { | 1606 | if (!ids) { |
1595 | if (mtdids_default) { | 1607 | if (mtdids_default) { |
1596 | DEBUGF("mtdids variable not defined, using default\n"); | 1608 | DEBUGF("mtdids variable not defined, using default\n"); |
1597 | ids = mtdids_default; | 1609 | ids = mtdids_default; |
1598 | setenv("mtdids", (char *)ids); | 1610 | setenv("mtdids", (char *)ids); |
1599 | } else { | 1611 | } else { |
1600 | printf("mtdids not defined, no default present\n"); | 1612 | printf("mtdids not defined, no default present\n"); |
1601 | return 1; | 1613 | return 1; |
1602 | } | 1614 | } |
1603 | } | 1615 | } |
1604 | if (strlen(ids) > MTDIDS_MAXLEN - 1) { | 1616 | if (strlen(ids) > MTDIDS_MAXLEN - 1) { |
1605 | printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN); | 1617 | printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN); |
1606 | return 1; | 1618 | return 1; |
1607 | } | 1619 | } |
1608 | 1620 | ||
1609 | /* do no try to use defaults when mtdparts variable is not defined, | 1621 | /* do no try to use defaults when mtdparts variable is not defined, |
1610 | * just check the length */ | 1622 | * just check the length */ |
1611 | if (!parts) | 1623 | if (!parts) |
1612 | printf("mtdparts variable not set, see 'help mtdparts'\n"); | 1624 | printf("mtdparts variable not set, see 'help mtdparts'\n"); |
1613 | 1625 | ||
1614 | if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) { | 1626 | if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) { |
1615 | printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN); | 1627 | printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN); |
1616 | return 1; | 1628 | return 1; |
1617 | } | 1629 | } |
1618 | 1630 | ||
1619 | /* check if we have already parsed those mtdids */ | 1631 | /* check if we have already parsed those mtdids */ |
1620 | if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) { | 1632 | if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) { |
1621 | ids_changed = 0; | 1633 | ids_changed = 0; |
1622 | } else { | 1634 | } else { |
1623 | ids_changed = 1; | 1635 | ids_changed = 1; |
1624 | 1636 | ||
1625 | if (parse_mtdids(ids) != 0) { | 1637 | if (parse_mtdids(ids) != 0) { |
1626 | devices_init(); | 1638 | devices_init(); |
1627 | return 1; | 1639 | return 1; |
1628 | } | 1640 | } |
1629 | 1641 | ||
1630 | /* ok it's good, save new ids */ | 1642 | /* ok it's good, save new ids */ |
1631 | strncpy(last_ids, ids, MTDIDS_MAXLEN); | 1643 | strncpy(last_ids, ids, MTDIDS_MAXLEN); |
1632 | } | 1644 | } |
1633 | 1645 | ||
1634 | /* parse partitions if either mtdparts or mtdids were updated */ | 1646 | /* parse partitions if either mtdparts or mtdids were updated */ |
1635 | if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) { | 1647 | if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) { |
1636 | if (parse_mtdparts(parts) != 0) | 1648 | if (parse_mtdparts(parts) != 0) |
1637 | return 1; | 1649 | return 1; |
1638 | 1650 | ||
1639 | if (list_empty(&devices)) { | 1651 | if (list_empty(&devices)) { |
1640 | printf("mtdparts_init: no valid partitions\n"); | 1652 | printf("mtdparts_init: no valid partitions\n"); |
1641 | return 1; | 1653 | return 1; |
1642 | } | 1654 | } |
1643 | 1655 | ||
1644 | /* ok it's good, save new parts */ | 1656 | /* ok it's good, save new parts */ |
1645 | strncpy(last_parts, parts, MTDPARTS_MAXLEN); | 1657 | strncpy(last_parts, parts, MTDPARTS_MAXLEN); |
1646 | 1658 | ||
1647 | /* reset first partition from first dev from the list as current */ | 1659 | /* reset first partition from first dev from the list as current */ |
1648 | current_dev = list_entry(devices.next, struct mtd_device, link); | 1660 | current_dev = list_entry(devices.next, struct mtd_device, link); |
1649 | current_partnum = 0; | 1661 | current_partnum = 0; |
1650 | current_save(); | 1662 | current_save(); |
1651 | 1663 | ||
1652 | DEBUGF("mtdparts_init: current_dev = %s%d, current_partnum = %d\n", | 1664 | DEBUGF("mtdparts_init: current_dev = %s%d, current_partnum = %d\n", |
1653 | MTD_DEV_TYPE(current_dev->id->type), | 1665 | MTD_DEV_TYPE(current_dev->id->type), |
1654 | current_dev->id->num, current_partnum); | 1666 | current_dev->id->num, current_partnum); |
1655 | } | 1667 | } |
1656 | 1668 | ||
1657 | /* mtdparts variable was reset to NULL, delete all devices/partitions */ | 1669 | /* mtdparts variable was reset to NULL, delete all devices/partitions */ |
1658 | if (!parts && (last_parts[0] != '\0')) | 1670 | if (!parts && (last_parts[0] != '\0')) |
1659 | return devices_init(); | 1671 | return devices_init(); |
1660 | 1672 | ||
1661 | /* do not process current partition if mtdparts variable is null */ | 1673 | /* do not process current partition if mtdparts variable is null */ |
1662 | if (!parts) | 1674 | if (!parts) |
1663 | return 0; | 1675 | return 0; |
1664 | 1676 | ||
1665 | /* is current partition set in environment? if so, use it */ | 1677 | /* is current partition set in environment? if so, use it */ |
1666 | if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) { | 1678 | if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) { |
1667 | struct part_info *p; | 1679 | struct part_info *p; |
1668 | struct mtd_device *cdev; | 1680 | struct mtd_device *cdev; |
1669 | u8 pnum; | 1681 | u8 pnum; |
1670 | 1682 | ||
1671 | DEBUGF("--- getting current partition: %s\n", tmp_ep); | 1683 | DEBUGF("--- getting current partition: %s\n", tmp_ep); |
1672 | 1684 | ||
1673 | if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) { | 1685 | if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) { |
1674 | current_dev = cdev; | 1686 | current_dev = cdev; |
1675 | current_partnum = pnum; | 1687 | current_partnum = pnum; |
1676 | current_save(); | 1688 | current_save(); |
1677 | } | 1689 | } |
1678 | } else if (getenv("partition") == NULL) { | 1690 | } else if (getenv("partition") == NULL) { |
1679 | DEBUGF("no partition variable set, setting...\n"); | 1691 | DEBUGF("no partition variable set, setting...\n"); |
1680 | current_save(); | 1692 | current_save(); |
1681 | } | 1693 | } |
1682 | 1694 | ||
1683 | return 0; | 1695 | return 0; |
1684 | } | 1696 | } |
1685 | #else /* #ifdef CONFIG_JFFS2_CMDLINE */ | 1697 | #else /* #ifdef CONFIG_JFFS2_CMDLINE */ |
1686 | /* | 1698 | /* |
1687 | * 'Static' version of command line mtdparts_init() routine. Single partition on | 1699 | * 'Static' version of command line mtdparts_init() routine. Single partition on |
1688 | * a single device configuration. | 1700 | * a single device configuration. |
1689 | */ | 1701 | */ |
1690 | 1702 | ||
1691 | /** | 1703 | /** |
1692 | * Parse and initialize global mtdids mapping and create global | 1704 | * Parse and initialize global mtdids mapping and create global |
1693 | * device/partition list. | 1705 | * device/partition list. |
1694 | * | 1706 | * |
1695 | * @return 0 on success, 1 otherwise | 1707 | * @return 0 on success, 1 otherwise |
1696 | */ | 1708 | */ |
1697 | int mtdparts_init(void) | 1709 | int mtdparts_init(void) |
1698 | { | 1710 | { |
1699 | static int initialized = 0; | 1711 | static int initialized = 0; |
1700 | u32 size; | 1712 | u32 size; |
1701 | char *dev_name; | 1713 | char *dev_name; |
1702 | 1714 | ||
1703 | DEBUGF("\n---mtdparts_init---\n"); | 1715 | DEBUGF("\n---mtdparts_init---\n"); |
1704 | if (!initialized) { | 1716 | if (!initialized) { |
1705 | struct mtdids *id; | 1717 | struct mtdids *id; |
1706 | struct part_info *part; | 1718 | struct part_info *part; |
1707 | 1719 | ||
1708 | initialized = 1; | 1720 | initialized = 1; |
1709 | current_dev = (struct mtd_device *) | 1721 | current_dev = (struct mtd_device *) |
1710 | malloc(sizeof(struct mtd_device) + | 1722 | malloc(sizeof(struct mtd_device) + |
1711 | sizeof(struct part_info) + | 1723 | sizeof(struct part_info) + |
1712 | sizeof(struct mtdids)); | 1724 | sizeof(struct mtdids)); |
1713 | if (!current_dev) { | 1725 | if (!current_dev) { |
1714 | printf("out of memory\n"); | 1726 | printf("out of memory\n"); |
1715 | return 1; | 1727 | return 1; |
1716 | } | 1728 | } |
1717 | memset(current_dev, 0, sizeof(struct mtd_device) + | 1729 | memset(current_dev, 0, sizeof(struct mtd_device) + |
1718 | sizeof(struct part_info) + sizeof(struct mtdids)); | 1730 | sizeof(struct part_info) + sizeof(struct mtdids)); |
1719 | 1731 | ||
1720 | id = (struct mtdids *)(current_dev + 1); | 1732 | id = (struct mtdids *)(current_dev + 1); |
1721 | part = (struct part_info *)(id + 1); | 1733 | part = (struct part_info *)(id + 1); |
1722 | 1734 | ||
1723 | /* id */ | 1735 | /* id */ |
1724 | id->mtd_id = "single part"; | 1736 | id->mtd_id = "single part"; |
1725 | 1737 | ||
1726 | #if defined(CONFIG_JFFS2_DEV) | 1738 | #if defined(CONFIG_JFFS2_DEV) |
1727 | dev_name = CONFIG_JFFS2_DEV; | 1739 | dev_name = CONFIG_JFFS2_DEV; |
1728 | #else | 1740 | #else |
1729 | dev_name = "nor0"; | 1741 | dev_name = "nor0"; |
1730 | #endif | 1742 | #endif |
1731 | 1743 | ||
1732 | if ((id_parse(dev_name, NULL, &id->type, &id->num) != 0) || | 1744 | if ((id_parse(dev_name, NULL, &id->type, &id->num) != 0) || |
1733 | (device_validate(id->type, id->num, &size) != 0)) { | 1745 | (device_validate(id->type, id->num, &size) != 0)) { |
1734 | printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num); | 1746 | printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num); |
1735 | free(current_dev); | 1747 | free(current_dev); |
1736 | return 1; | 1748 | return 1; |
1737 | } | 1749 | } |
1738 | id->size = size; | 1750 | id->size = size; |
1739 | INIT_LIST_HEAD(&id->link); | 1751 | INIT_LIST_HEAD(&id->link); |
1740 | 1752 | ||
1741 | DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n", | 1753 | DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n", |
1742 | id->type, id->num, id->size, id->mtd_id); | 1754 | id->type, id->num, id->size, id->mtd_id); |
1743 | 1755 | ||
1744 | /* partition */ | 1756 | /* partition */ |
1745 | part->name = "static"; | 1757 | part->name = "static"; |
1746 | part->auto_name = 0; | 1758 | part->auto_name = 0; |
1747 | 1759 | ||
1748 | #if defined(CONFIG_JFFS2_PART_SIZE) | 1760 | #if defined(CONFIG_JFFS2_PART_SIZE) |
1749 | part->size = CONFIG_JFFS2_PART_SIZE; | 1761 | part->size = CONFIG_JFFS2_PART_SIZE; |
1750 | #else | 1762 | #else |
1751 | part->size = SIZE_REMAINING; | 1763 | part->size = SIZE_REMAINING; |
1752 | #endif | 1764 | #endif |
1753 | 1765 | ||
1754 | #if defined(CONFIG_JFFS2_PART_OFFSET) | 1766 | #if defined(CONFIG_JFFS2_PART_OFFSET) |
1755 | part->offset = CONFIG_JFFS2_PART_OFFSET; | 1767 | part->offset = CONFIG_JFFS2_PART_OFFSET; |
1756 | #else | 1768 | #else |
1757 | part->offset = 0x00000000; | 1769 | part->offset = 0x00000000; |
1758 | #endif | 1770 | #endif |
1759 | 1771 | ||
1760 | part->dev = current_dev; | 1772 | part->dev = current_dev; |
1761 | INIT_LIST_HEAD(&part->link); | 1773 | INIT_LIST_HEAD(&part->link); |
1762 | 1774 | ||
1763 | /* recalculate size if needed */ | 1775 | /* recalculate size if needed */ |
1764 | if (part->size == SIZE_REMAINING) | 1776 | if (part->size == SIZE_REMAINING) |
1765 | part->size = id->size - part->offset; | 1777 | part->size = id->size - part->offset; |
1766 | 1778 | ||
1767 | DEBUGF("part : name = %s, size = 0x%08lx, offset = 0x%08lx\n", | 1779 | DEBUGF("part : name = %s, size = 0x%08lx, offset = 0x%08lx\n", |
1768 | part->name, part->size, part->offset); | 1780 | part->name, part->size, part->offset); |
1769 | 1781 | ||
1770 | /* device */ | 1782 | /* device */ |
1771 | current_dev->id = id; | 1783 | current_dev->id = id; |
1772 | INIT_LIST_HEAD(¤t_dev->link); | 1784 | INIT_LIST_HEAD(¤t_dev->link); |
1773 | current_dev->num_parts = 1; | 1785 | current_dev->num_parts = 1; |
1774 | INIT_LIST_HEAD(¤t_dev->parts); | 1786 | INIT_LIST_HEAD(¤t_dev->parts); |
1775 | list_add(&part->link, ¤t_dev->parts); | 1787 | list_add(&part->link, ¤t_dev->parts); |
1776 | } | 1788 | } |
1777 | 1789 | ||
1778 | return 0; | 1790 | return 0; |
1779 | } | 1791 | } |
1780 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ | 1792 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ |
1781 | 1793 | ||
1782 | /** | 1794 | /** |
1783 | * Return pointer to the partition of a requested number from a requested | 1795 | * Return pointer to the partition of a requested number from a requested |
1784 | * device. | 1796 | * device. |
1785 | * | 1797 | * |
1786 | * @param dev device that is to be searched for a partition | 1798 | * @param dev device that is to be searched for a partition |
1787 | * @param part_num requested partition number | 1799 | * @param part_num requested partition number |
1788 | * @return pointer to the part_info, NULL otherwise | 1800 | * @return pointer to the part_info, NULL otherwise |
1789 | */ | 1801 | */ |
1790 | static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num) | 1802 | static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num) |
1791 | { | 1803 | { |
1792 | struct list_head *entry; | 1804 | struct list_head *entry; |
1793 | struct part_info *part; | 1805 | struct part_info *part; |
1794 | int num; | 1806 | int num; |
1795 | 1807 | ||
1796 | if (!dev) | 1808 | if (!dev) |
1797 | return NULL; | 1809 | return NULL; |
1798 | 1810 | ||
1799 | DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n", | 1811 | DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n", |
1800 | part_num, MTD_DEV_TYPE(dev->id->type), | 1812 | part_num, MTD_DEV_TYPE(dev->id->type), |
1801 | dev->id->num, dev->id->mtd_id); | 1813 | dev->id->num, dev->id->mtd_id); |
1802 | 1814 | ||
1803 | if (part_num >= dev->num_parts) { | 1815 | if (part_num >= dev->num_parts) { |
1804 | printf("invalid partition number %d for device %s%d (%s)\n", | 1816 | printf("invalid partition number %d for device %s%d (%s)\n", |
1805 | part_num, MTD_DEV_TYPE(dev->id->type), | 1817 | part_num, MTD_DEV_TYPE(dev->id->type), |
1806 | dev->id->num, dev->id->mtd_id); | 1818 | dev->id->num, dev->id->mtd_id); |
1807 | return NULL; | 1819 | return NULL; |
1808 | } | 1820 | } |
1809 | 1821 | ||
1810 | /* locate partition number, return it */ | 1822 | /* locate partition number, return it */ |
1811 | num = 0; | 1823 | num = 0; |
1812 | list_for_each(entry, &dev->parts) { | 1824 | list_for_each(entry, &dev->parts) { |
1813 | part = list_entry(entry, struct part_info, link); | 1825 | part = list_entry(entry, struct part_info, link); |
1814 | 1826 | ||
1815 | if (part_num == num++) { | 1827 | if (part_num == num++) { |
1816 | return part; | 1828 | return part; |
1817 | } | 1829 | } |
1818 | } | 1830 | } |
1819 | 1831 | ||
1820 | return NULL; | 1832 | return NULL; |
1821 | } | 1833 | } |
1822 | 1834 | ||
1823 | /***************************************************/ | 1835 | /***************************************************/ |
1824 | /* U-boot commands */ | 1836 | /* U-boot commands */ |
1825 | /***************************************************/ | 1837 | /***************************************************/ |
1826 | 1838 | ||
1827 | /** | 1839 | /** |
1828 | * Routine implementing fsload u-boot command. This routine tries to load | 1840 | * Routine implementing fsload u-boot command. This routine tries to load |
1829 | * a requested file from jffs2/cramfs filesystem on a current partition. | 1841 | * a requested file from jffs2/cramfs filesystem on a current partition. |
1830 | * | 1842 | * |
1831 | * @param cmdtp command internal data | 1843 | * @param cmdtp command internal data |
1832 | * @param flag command flag | 1844 | * @param flag command flag |
1833 | * @param argc number of arguments supplied to the command | 1845 | * @param argc number of arguments supplied to the command |
1834 | * @param argv arguments list | 1846 | * @param argv arguments list |
1835 | * @return 0 on success, 1 otherwise | 1847 | * @return 0 on success, 1 otherwise |
1836 | */ | 1848 | */ |
1837 | int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 1849 | int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
1838 | { | 1850 | { |
1839 | char *fsname; | 1851 | char *fsname; |
1840 | char *filename; | 1852 | char *filename; |
1841 | int size; | 1853 | int size; |
1842 | struct part_info *part; | 1854 | struct part_info *part; |
1843 | ulong offset = load_addr; | 1855 | ulong offset = load_addr; |
1844 | 1856 | ||
1845 | /* pre-set Boot file name */ | 1857 | /* pre-set Boot file name */ |
1846 | if ((filename = getenv("bootfile")) == NULL) { | 1858 | if ((filename = getenv("bootfile")) == NULL) { |
1847 | filename = "uImage"; | 1859 | filename = "uImage"; |
1848 | } | 1860 | } |
1849 | 1861 | ||
1850 | if (argc == 2) { | 1862 | if (argc == 2) { |
1851 | filename = argv[1]; | 1863 | filename = argv[1]; |
1852 | } | 1864 | } |
1853 | if (argc == 3) { | 1865 | if (argc == 3) { |
1854 | offset = simple_strtoul(argv[1], NULL, 16); | 1866 | offset = simple_strtoul(argv[1], NULL, 16); |
1855 | load_addr = offset; | 1867 | load_addr = offset; |
1856 | filename = argv[2]; | 1868 | filename = argv[2]; |
1857 | } | 1869 | } |
1858 | 1870 | ||
1859 | /* make sure we are in sync with env variables */ | 1871 | /* make sure we are in sync with env variables */ |
1860 | if (mtdparts_init() !=0) | 1872 | if (mtdparts_init() !=0) |
1861 | return 1; | 1873 | return 1; |
1862 | 1874 | ||
1863 | if ((part = jffs2_part_info(current_dev, current_partnum))){ | 1875 | if ((part = jffs2_part_info(current_dev, current_partnum))){ |
1864 | 1876 | ||
1865 | /* check partition type for cramfs */ | 1877 | /* check partition type for cramfs */ |
1866 | fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); | 1878 | fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); |
1867 | printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset); | 1879 | printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset); |
1868 | 1880 | ||
1869 | if (cramfs_check(part)) { | 1881 | if (cramfs_check(part)) { |
1870 | size = cramfs_load ((char *) offset, part, filename); | 1882 | size = cramfs_load ((char *) offset, part, filename); |
1871 | } else { | 1883 | } else { |
1872 | /* if this is not cramfs assume jffs2 */ | 1884 | /* if this is not cramfs assume jffs2 */ |
1873 | size = jffs2_1pass_load((char *)offset, part, filename); | 1885 | size = jffs2_1pass_load((char *)offset, part, filename); |
1874 | } | 1886 | } |
1875 | 1887 | ||
1876 | if (size > 0) { | 1888 | if (size > 0) { |
1877 | char buf[10]; | 1889 | char buf[10]; |
1878 | printf("### %s load complete: %d bytes loaded to 0x%lx\n", | 1890 | printf("### %s load complete: %d bytes loaded to 0x%lx\n", |
1879 | fsname, size, offset); | 1891 | fsname, size, offset); |
1880 | sprintf(buf, "%x", size); | 1892 | sprintf(buf, "%x", size); |
1881 | setenv("filesize", buf); | 1893 | setenv("filesize", buf); |
1882 | } else { | 1894 | } else { |
1883 | printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename); | 1895 | printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename); |
1884 | } | 1896 | } |
1885 | 1897 | ||
1886 | return !(size > 0); | 1898 | return !(size > 0); |
1887 | } | 1899 | } |
1888 | return 1; | 1900 | return 1; |
1889 | } | 1901 | } |
1890 | 1902 | ||
1891 | /** | 1903 | /** |
1892 | * Routine implementing u-boot ls command which lists content of a given | 1904 | * Routine implementing u-boot ls command which lists content of a given |
1893 | * directory on a current partition. | 1905 | * directory on a current partition. |
1894 | * | 1906 | * |
1895 | * @param cmdtp command internal data | 1907 | * @param cmdtp command internal data |
1896 | * @param flag command flag | 1908 | * @param flag command flag |
1897 | * @param argc number of arguments supplied to the command | 1909 | * @param argc number of arguments supplied to the command |
1898 | * @param argv arguments list | 1910 | * @param argv arguments list |
1899 | * @return 0 on success, 1 otherwise | 1911 | * @return 0 on success, 1 otherwise |
1900 | */ | 1912 | */ |
1901 | int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 1913 | int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
1902 | { | 1914 | { |
1903 | char *filename = "/"; | 1915 | char *filename = "/"; |
1904 | int ret; | 1916 | int ret; |
1905 | struct part_info *part; | 1917 | struct part_info *part; |
1906 | 1918 | ||
1907 | if (argc == 2) | 1919 | if (argc == 2) |
1908 | filename = argv[1]; | 1920 | filename = argv[1]; |
1909 | 1921 | ||
1910 | /* make sure we are in sync with env variables */ | 1922 | /* make sure we are in sync with env variables */ |
1911 | if (mtdparts_init() !=0) | 1923 | if (mtdparts_init() !=0) |
1912 | return 1; | 1924 | return 1; |
1913 | 1925 | ||
1914 | if ((part = jffs2_part_info(current_dev, current_partnum))){ | 1926 | if ((part = jffs2_part_info(current_dev, current_partnum))){ |
1915 | 1927 | ||
1916 | /* check partition type for cramfs */ | 1928 | /* check partition type for cramfs */ |
1917 | if (cramfs_check(part)) { | 1929 | if (cramfs_check(part)) { |
1918 | ret = cramfs_ls (part, filename); | 1930 | ret = cramfs_ls (part, filename); |
1919 | } else { | 1931 | } else { |
1920 | /* if this is not cramfs assume jffs2 */ | 1932 | /* if this is not cramfs assume jffs2 */ |
1921 | ret = jffs2_1pass_ls(part, filename); | 1933 | ret = jffs2_1pass_ls(part, filename); |
1922 | } | 1934 | } |
1923 | 1935 | ||
1924 | return ret ? 0 : 1; | 1936 | return ret ? 0 : 1; |
1925 | } | 1937 | } |
1926 | return 1; | 1938 | return 1; |
1927 | } | 1939 | } |
1928 | 1940 | ||
1929 | /** | 1941 | /** |
1930 | * Routine implementing u-boot fsinfo command. This routine prints out | 1942 | * Routine implementing u-boot fsinfo command. This routine prints out |
1931 | * miscellaneous filesystem informations/statistics. | 1943 | * miscellaneous filesystem informations/statistics. |
1932 | * | 1944 | * |
1933 | * @param cmdtp command internal data | 1945 | * @param cmdtp command internal data |
1934 | * @param flag command flag | 1946 | * @param flag command flag |
1935 | * @param argc number of arguments supplied to the command | 1947 | * @param argc number of arguments supplied to the command |
1936 | * @param argv arguments list | 1948 | * @param argv arguments list |
1937 | * @return 0 on success, 1 otherwise | 1949 | * @return 0 on success, 1 otherwise |
1938 | */ | 1950 | */ |
1939 | int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 1951 | int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
1940 | { | 1952 | { |
1941 | struct part_info *part; | 1953 | struct part_info *part; |
1942 | char *fsname; | 1954 | char *fsname; |
1943 | int ret; | 1955 | int ret; |
1944 | 1956 | ||
1945 | /* make sure we are in sync with env variables */ | 1957 | /* make sure we are in sync with env variables */ |
1946 | if (mtdparts_init() !=0) | 1958 | if (mtdparts_init() !=0) |
1947 | return 1; | 1959 | return 1; |
1948 | 1960 | ||
1949 | if ((part = jffs2_part_info(current_dev, current_partnum))){ | 1961 | if ((part = jffs2_part_info(current_dev, current_partnum))){ |
1950 | 1962 | ||
1951 | /* check partition type for cramfs */ | 1963 | /* check partition type for cramfs */ |
1952 | fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); | 1964 | fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); |
1953 | printf("### filesystem type is %s\n", fsname); | 1965 | printf("### filesystem type is %s\n", fsname); |
1954 | 1966 | ||
1955 | if (cramfs_check(part)) { | 1967 | if (cramfs_check(part)) { |
1956 | ret = cramfs_info (part); | 1968 | ret = cramfs_info (part); |
1957 | } else { | 1969 | } else { |
1958 | /* if this is not cramfs assume jffs2 */ | 1970 | /* if this is not cramfs assume jffs2 */ |
1959 | ret = jffs2_1pass_info(part); | 1971 | ret = jffs2_1pass_info(part); |
1960 | } | 1972 | } |
1961 | 1973 | ||
1962 | return ret ? 0 : 1; | 1974 | return ret ? 0 : 1; |
1963 | } | 1975 | } |
1964 | return 1; | 1976 | return 1; |
1965 | } | 1977 | } |
1966 | 1978 | ||
1967 | /* command line only */ | 1979 | /* command line only */ |
1968 | #ifdef CONFIG_JFFS2_CMDLINE | 1980 | #ifdef CONFIG_JFFS2_CMDLINE |
1969 | /** | 1981 | /** |
1970 | * Routine implementing u-boot chpart command. Sets new current partition based | 1982 | * Routine implementing u-boot chpart command. Sets new current partition based |
1971 | * on the user supplied partition id. For partition id format see find_dev_and_part(). | 1983 | * on the user supplied partition id. For partition id format see find_dev_and_part(). |
1972 | * | 1984 | * |
1973 | * @param cmdtp command internal data | 1985 | * @param cmdtp command internal data |
1974 | * @param flag command flag | 1986 | * @param flag command flag |
1975 | * @param argc number of arguments supplied to the command | 1987 | * @param argc number of arguments supplied to the command |
1976 | * @param argv arguments list | 1988 | * @param argv arguments list |
1977 | * @return 0 on success, 1 otherwise | 1989 | * @return 0 on success, 1 otherwise |
1978 | */ | 1990 | */ |
1979 | int do_jffs2_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 1991 | int do_jffs2_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
1980 | { | 1992 | { |
1981 | /* command line only */ | 1993 | /* command line only */ |
1982 | struct mtd_device *dev; | 1994 | struct mtd_device *dev; |
1983 | struct part_info *part; | 1995 | struct part_info *part; |
1984 | u8 pnum; | 1996 | u8 pnum; |
1985 | 1997 | ||
1986 | if (mtdparts_init() !=0) | 1998 | if (mtdparts_init() !=0) |
1987 | return 1; | 1999 | return 1; |
1988 | 2000 | ||
1989 | if (argc < 2) { | 2001 | if (argc < 2) { |
1990 | printf("no partition id specified\n"); | 2002 | printf("no partition id specified\n"); |
1991 | return 1; | 2003 | return 1; |
1992 | } | 2004 | } |
1993 | 2005 | ||
1994 | if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0) | 2006 | if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0) |
1995 | return 1; | 2007 | return 1; |
1996 | 2008 | ||
1997 | current_dev = dev; | 2009 | current_dev = dev; |
1998 | current_partnum = pnum; | 2010 | current_partnum = pnum; |
1999 | current_save(); | 2011 | current_save(); |
2000 | 2012 | ||
2001 | printf("partition changed to %s%d,%d\n", | 2013 | printf("partition changed to %s%d,%d\n", |
2002 | MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum); | 2014 | MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum); |
2003 | 2015 | ||
2004 | return 0; | 2016 | return 0; |
2005 | } | 2017 | } |
2006 | 2018 | ||
2007 | /** | 2019 | /** |
2008 | * Routine implementing u-boot mtdparts command. Initialize/update default global | 2020 | * Routine implementing u-boot mtdparts command. Initialize/update default global |
2009 | * partition list and process user partition request (list, add, del). | 2021 | * partition list and process user partition request (list, add, del). |
2010 | * | 2022 | * |
2011 | * @param cmdtp command internal data | 2023 | * @param cmdtp command internal data |
2012 | * @param flag command flag | 2024 | * @param flag command flag |
2013 | * @param argc number of arguments supplied to the command | 2025 | * @param argc number of arguments supplied to the command |
2014 | * @param argv arguments list | 2026 | * @param argv arguments list |
2015 | * @return 0 on success, 1 otherwise | 2027 | * @return 0 on success, 1 otherwise |
2016 | */ | 2028 | */ |
2017 | int do_jffs2_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 2029 | int do_jffs2_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
2018 | { | 2030 | { |
2019 | if (argc == 2) { | 2031 | if (argc == 2) { |
2020 | if (strcmp(argv[1], "default") == 0) { | 2032 | if (strcmp(argv[1], "default") == 0) { |
2021 | setenv("mtdids", (char *)mtdids_default); | 2033 | setenv("mtdids", (char *)mtdids_default); |
2022 | setenv("mtdparts", (char *)mtdparts_default); | 2034 | setenv("mtdparts", (char *)mtdparts_default); |
2023 | setenv("partition", NULL); | 2035 | setenv("partition", NULL); |
2024 | 2036 | ||
2025 | mtdparts_init(); | 2037 | mtdparts_init(); |
2026 | return 0; | 2038 | return 0; |
2027 | } else if (strcmp(argv[1], "delall") == 0) { | 2039 | } else if (strcmp(argv[1], "delall") == 0) { |
2028 | /* this may be the first run, initialize lists if needed */ | 2040 | /* this may be the first run, initialize lists if needed */ |
2029 | mtdparts_init(); | 2041 | mtdparts_init(); |
2030 | 2042 | ||
2031 | setenv("mtdparts", NULL); | 2043 | setenv("mtdparts", NULL); |
2032 | 2044 | ||
2033 | /* devices_init() calls current_save() */ | 2045 | /* devices_init() calls current_save() */ |
2034 | return devices_init(); | 2046 | return devices_init(); |
2035 | } | 2047 | } |
2036 | } | 2048 | } |
2037 | 2049 | ||
2038 | /* make sure we are in sync with env variables */ | 2050 | /* make sure we are in sync with env variables */ |
2039 | if (mtdparts_init() != 0) | 2051 | if (mtdparts_init() != 0) |
2040 | return 1; | 2052 | return 1; |
2041 | 2053 | ||
2042 | if (argc == 1) { | 2054 | if (argc == 1) { |
2043 | list_partitions(); | 2055 | list_partitions(); |
2044 | return 0; | 2056 | return 0; |
2045 | } | 2057 | } |
2046 | 2058 | ||
2047 | /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ | 2059 | /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ |
2048 | if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { | 2060 | if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { |
2049 | #define PART_ADD_DESC_MAXLEN 64 | 2061 | #define PART_ADD_DESC_MAXLEN 64 |
2050 | char tmpbuf[PART_ADD_DESC_MAXLEN]; | 2062 | char tmpbuf[PART_ADD_DESC_MAXLEN]; |
2051 | u8 type, num, len; | 2063 | u8 type, num, len; |
2052 | struct mtd_device *dev; | 2064 | struct mtd_device *dev; |
2053 | struct mtd_device *dev_tmp; | 2065 | struct mtd_device *dev_tmp; |
2054 | struct mtdids *id; | 2066 | struct mtdids *id; |
2055 | struct part_info *p; | 2067 | struct part_info *p; |
2056 | 2068 | ||
2057 | if (id_parse(argv[2], NULL, &type, &num) != 0) | 2069 | if (id_parse(argv[2], NULL, &type, &num) != 0) |
2058 | return 1; | 2070 | return 1; |
2059 | 2071 | ||
2060 | if ((id = id_find(type, num)) == NULL) { | 2072 | if ((id = id_find(type, num)) == NULL) { |
2061 | printf("no such device %s defined in mtdids variable\n", argv[2]); | 2073 | printf("no such device %s defined in mtdids variable\n", argv[2]); |
2062 | return 1; | 2074 | return 1; |
2063 | } | 2075 | } |
2064 | 2076 | ||
2065 | len = strlen(id->mtd_id) + 1; /* 'mtd_id:' */ | 2077 | len = strlen(id->mtd_id) + 1; /* 'mtd_id:' */ |
2066 | len += strlen(argv[3]); /* size@offset */ | 2078 | len += strlen(argv[3]); /* size@offset */ |
2067 | len += strlen(argv[4]) + 2; /* '(' name ')' */ | 2079 | len += strlen(argv[4]) + 2; /* '(' name ')' */ |
2068 | if (argv[5] && (strlen(argv[5]) == 2)) | 2080 | if (argv[5] && (strlen(argv[5]) == 2)) |
2069 | len += 2; /* 'ro' */ | 2081 | len += 2; /* 'ro' */ |
2070 | 2082 | ||
2071 | if (len >= PART_ADD_DESC_MAXLEN) { | 2083 | if (len >= PART_ADD_DESC_MAXLEN) { |
2072 | printf("too long partition description\n"); | 2084 | printf("too long partition description\n"); |
2073 | return 1; | 2085 | return 1; |
2074 | } | 2086 | } |
2075 | sprintf(tmpbuf, "%s:%s(%s)%s", | 2087 | sprintf(tmpbuf, "%s:%s(%s)%s", |
2076 | id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : ""); | 2088 | id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : ""); |
2077 | DEBUGF("add tmpbuf: %s\n", tmpbuf); | 2089 | DEBUGF("add tmpbuf: %s\n", tmpbuf); |
2078 | 2090 | ||
2079 | if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev)) | 2091 | if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev)) |
2080 | return 1; | 2092 | return 1; |
2081 | 2093 | ||
2082 | DEBUGF("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), | 2094 | DEBUGF("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), |
2083 | dev->id->num, dev->id->mtd_id); | 2095 | dev->id->num, dev->id->mtd_id); |
2084 | 2096 | ||
2085 | if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { | 2097 | if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { |
2086 | device_add(dev); | 2098 | device_add(dev); |
2087 | } else { | 2099 | } else { |
2088 | /* merge new partition with existing ones*/ | 2100 | /* merge new partition with existing ones*/ |
2089 | p = list_entry(dev->parts.next, struct part_info, link); | 2101 | p = list_entry(dev->parts.next, struct part_info, link); |
2090 | if (part_add(dev_tmp, p) != 0) { | 2102 | if (part_add(dev_tmp, p) != 0) { |
2091 | device_del(dev); | 2103 | device_del(dev); |
2092 | return 1; | 2104 | return 1; |
2093 | } | 2105 | } |
2094 | } | 2106 | } |
2095 | 2107 | ||
2096 | if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { | 2108 | if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { |
2097 | printf("generated mtdparts too long, reseting to null\n"); | 2109 | printf("generated mtdparts too long, reseting to null\n"); |
2098 | return 1; | 2110 | return 1; |
2099 | } | 2111 | } |
2100 | 2112 | ||
2101 | return 0; | 2113 | return 0; |
2102 | } | 2114 | } |
2103 | 2115 | ||
2104 | /* mtdparts del part-id */ | 2116 | /* mtdparts del part-id */ |
2105 | if ((argc == 3) && (strcmp(argv[1], "del") == 0)) { | 2117 | if ((argc == 3) && (strcmp(argv[1], "del") == 0)) { |
2106 | DEBUGF("del: part-id = %s\n", argv[2]); | 2118 | DEBUGF("del: part-id = %s\n", argv[2]); |
2107 | 2119 | ||
2108 | return delete_partition(argv[2]); | 2120 | return delete_partition(argv[2]); |
2109 | } | 2121 | } |
2110 | 2122 | ||
2111 | printf ("Usage:\n%s\n", cmdtp->usage); | 2123 | printf ("Usage:\n%s\n", cmdtp->usage); |
2112 | return 1; | 2124 | return 1; |
2113 | } | 2125 | } |
2114 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ | 2126 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ |
2115 | 2127 | ||
2116 | /***************************************************/ | 2128 | /***************************************************/ |
2117 | U_BOOT_CMD( | 2129 | U_BOOT_CMD( |
2118 | fsload, 3, 0, do_jffs2_fsload, | 2130 | fsload, 3, 0, do_jffs2_fsload, |
2119 | "fsload\t- load binary file from a filesystem image\n", | 2131 | "fsload\t- load binary file from a filesystem image\n", |
2120 | "[ off ] [ filename ]\n" | 2132 | "[ off ] [ filename ]\n" |
2121 | " - load binary file from flash bank\n" | 2133 | " - load binary file from flash bank\n" |
2122 | " with offset 'off'\n" | 2134 | " with offset 'off'\n" |
2123 | ); | 2135 | ); |
2124 | U_BOOT_CMD( | 2136 | U_BOOT_CMD( |
2125 | ls, 2, 1, do_jffs2_ls, | 2137 | ls, 2, 1, do_jffs2_ls, |
2126 | "ls\t- list files in a directory (default /)\n", | 2138 | "ls\t- list files in a directory (default /)\n", |
2127 | "[ directory ]\n" | 2139 | "[ directory ]\n" |
2128 | " - list files in a directory.\n" | 2140 | " - list files in a directory.\n" |
2129 | ); | 2141 | ); |
2130 | 2142 | ||
2131 | U_BOOT_CMD( | 2143 | U_BOOT_CMD( |
2132 | fsinfo, 1, 1, do_jffs2_fsinfo, | 2144 | fsinfo, 1, 1, do_jffs2_fsinfo, |
2133 | "fsinfo\t- print information about filesystems\n", | 2145 | "fsinfo\t- print information about filesystems\n", |
2134 | " - print information about filesystems\n" | 2146 | " - print information about filesystems\n" |
2135 | ); | 2147 | ); |
2136 | 2148 | ||
2137 | #ifdef CONFIG_JFFS2_CMDLINE | 2149 | #ifdef CONFIG_JFFS2_CMDLINE |
2138 | U_BOOT_CMD( | 2150 | U_BOOT_CMD( |
2139 | chpart, 2, 0, do_jffs2_chpart, | 2151 | chpart, 2, 0, do_jffs2_chpart, |
2140 | "chpart\t- change active partition\n", | 2152 | "chpart\t- change active partition\n", |
2141 | "part-id\n" | 2153 | "part-id\n" |
2142 | " - change active partition (e.g. part-id = nand0,1)\n" | 2154 | " - change active partition (e.g. part-id = nand0,1)\n" |
2143 | ); | 2155 | ); |
2144 | 2156 | ||
2145 | U_BOOT_CMD( | 2157 | U_BOOT_CMD( |
2146 | mtdparts, 6, 0, do_jffs2_mtdparts, | 2158 | mtdparts, 6, 0, do_jffs2_mtdparts, |
2147 | "mtdparts- define flash/nand partitions\n", | 2159 | "mtdparts- define flash/nand partitions\n", |
2148 | "\n" | 2160 | "\n" |
2149 | " - list partition table\n" | 2161 | " - list partition table\n" |
2150 | "mtdparts delall\n" | 2162 | "mtdparts delall\n" |
2151 | " - delete all partitions\n" | 2163 | " - delete all partitions\n" |
2152 | "mtdparts del part-id\n" | 2164 | "mtdparts del part-id\n" |
2153 | " - delete partition (e.g. part-id = nand0,1)\n" | 2165 | " - delete partition (e.g. part-id = nand0,1)\n" |
2154 | "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" | 2166 | "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" |
2155 | " - add partition\n" | 2167 | " - add partition\n" |
2156 | "mtdparts default\n" | 2168 | "mtdparts default\n" |
2157 | " - reset partition table to defaults\n\n" | 2169 | " - reset partition table to defaults\n\n" |
2158 | "-----\n\n" | 2170 | "-----\n\n" |
2159 | "this command uses three environment variables:\n\n" | 2171 | "this command uses three environment variables:\n\n" |
2160 | "'partition' - keeps current partition identifier\n\n" | 2172 | "'partition' - keeps current partition identifier\n\n" |
2161 | "partition := <part-id>\n" | 2173 | "partition := <part-id>\n" |
2162 | "<part-id> := <dev-id>,part_num\n\n" | 2174 | "<part-id> := <dev-id>,part_num\n\n" |
2163 | "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n" | 2175 | "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n" |
2164 | "mtdids=<idmap>[,<idmap>,...]\n\n" | 2176 | "mtdids=<idmap>[,<idmap>,...]\n\n" |
2165 | "<idmap> := <dev-id>=<mtd-id>\n" | 2177 | "<idmap> := <dev-id>=<mtd-id>\n" |
2166 | "<dev-id> := 'nand'|'nor'<dev-num>\n" | 2178 | "<dev-id> := 'nand'|'nor'<dev-num>\n" |
2167 | "<dev-num> := mtd device number, 0...\n" | 2179 | "<dev-num> := mtd device number, 0...\n" |
2168 | "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n" | 2180 | "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n" |
2169 | "'mtdparts' - partition list\n\n" | 2181 | "'mtdparts' - partition list\n\n" |
2170 | "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n" | 2182 | "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n" |
2171 | "<mtd-def> := <mtd-id>:<part-def>[,<part-def>...]\n" | 2183 | "<mtd-def> := <mtd-id>:<part-def>[,<part-def>...]\n" |
2172 | "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n" | 2184 | "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n" |
2173 | "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n" | 2185 | "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n" |
2174 | "<size> := standard linux memsize OR '-' to denote all remaining space\n" | 2186 | "<size> := standard linux memsize OR '-' to denote all remaining space\n" |
2175 | "<offset> := partition start offset within the device\n" | 2187 | "<offset> := partition start offset within the device\n" |
2176 | "<name> := '(' NAME ')'\n" | 2188 | "<name> := '(' NAME ')'\n" |
2177 | "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)\n" | 2189 | "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)\n" |
2178 | ); | 2190 | ); |
2179 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ | 2191 | #endif /* #ifdef CONFIG_JFFS2_CMDLINE */ |
2180 | 2192 | ||
2181 | /***************************************************/ | 2193 | /***************************************************/ |
2182 | 2194 | ||
2183 | #endif /* CFG_CMD_JFFS2 */ | 2195 | #endif /* CFG_CMD_JFFS2 */ |
2184 | 2196 |
common/cmd_nand.c
1 | /* | 1 | /* |
2 | * Driver for NAND support, Rick Bronson | 2 | * Driver for NAND support, Rick Bronson |
3 | * borrowed heavily from: | 3 | * borrowed heavily from: |
4 | * (c) 1999 Machine Vision Holdings, Inc. | 4 | * (c) 1999 Machine Vision Holdings, Inc. |
5 | * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> | 5 | * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> |
6 | * | 6 | * |
7 | * Added 16-bit nand support | 7 | * Added 16-bit nand support |
8 | * (C) 2004 Texas Instruments | 8 | * (C) 2004 Texas Instruments |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <common.h> | 11 | #include <common.h> |
12 | 12 | ||
13 | 13 | ||
14 | #ifndef CFG_NAND_LEGACY | 14 | #ifndef CFG_NAND_LEGACY |
15 | /* | 15 | /* |
16 | * | 16 | * |
17 | * New NAND support | 17 | * New NAND support |
18 | * | 18 | * |
19 | */ | 19 | */ |
20 | #include <common.h> | 20 | #include <common.h> |
21 | 21 | ||
22 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) | 22 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) |
23 | 23 | ||
24 | #include <command.h> | 24 | #include <command.h> |
25 | #include <watchdog.h> | 25 | #include <watchdog.h> |
26 | #include <malloc.h> | 26 | #include <malloc.h> |
27 | #include <asm/byteorder.h> | 27 | #include <asm/byteorder.h> |
28 | 28 | ||
29 | #ifdef CONFIG_SHOW_BOOT_PROGRESS | 29 | #ifdef CONFIG_SHOW_BOOT_PROGRESS |
30 | # include <status_led.h> | 30 | # include <status_led.h> |
31 | # define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg) | 31 | # define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg) |
32 | #else | 32 | #else |
33 | # define SHOW_BOOT_PROGRESS(arg) | 33 | # define SHOW_BOOT_PROGRESS(arg) |
34 | #endif | 34 | #endif |
35 | 35 | ||
36 | #include <jffs2/jffs2.h> | 36 | #include <jffs2/jffs2.h> |
37 | #include <nand.h> | 37 | #include <nand.h> |
38 | 38 | ||
39 | #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | ||
40 | |||
41 | /* parition handling routines */ | ||
42 | int mtdparts_init(void); | ||
43 | int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); | ||
44 | int find_dev_and_part(const char *id, struct mtd_device **dev, | ||
45 | u8 *part_num, struct part_info **part); | ||
46 | #endif | ||
47 | |||
39 | extern nand_info_t nand_info[]; /* info for NAND chips */ | 48 | extern nand_info_t nand_info[]; /* info for NAND chips */ |
40 | 49 | ||
41 | static int nand_dump_oob(nand_info_t *nand, ulong off) | 50 | static int nand_dump_oob(nand_info_t *nand, ulong off) |
42 | { | 51 | { |
43 | return 0; | 52 | return 0; |
44 | } | 53 | } |
45 | 54 | ||
46 | static int nand_dump(nand_info_t *nand, ulong off) | 55 | static int nand_dump(nand_info_t *nand, ulong off) |
47 | { | 56 | { |
48 | int i; | 57 | int i; |
49 | u_char *buf, *p; | 58 | u_char *buf, *p; |
50 | 59 | ||
51 | buf = malloc(nand->oobblock + nand->oobsize); | 60 | buf = malloc(nand->oobblock + nand->oobsize); |
52 | if (!buf) { | 61 | if (!buf) { |
53 | puts("No memory for page buffer\n"); | 62 | puts("No memory for page buffer\n"); |
54 | return 1; | 63 | return 1; |
55 | } | 64 | } |
56 | off &= ~(nand->oobblock - 1); | 65 | off &= ~(nand->oobblock - 1); |
57 | i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize); | 66 | i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize); |
58 | if (i < 0) { | 67 | if (i < 0) { |
59 | printf("Error (%d) reading page %08x\n", i, off); | 68 | printf("Error (%d) reading page %08x\n", i, off); |
60 | free(buf); | 69 | free(buf); |
61 | return 1; | 70 | return 1; |
62 | } | 71 | } |
63 | printf("Page %08x dump:\n", off); | 72 | printf("Page %08x dump:\n", off); |
64 | i = nand->oobblock >> 4; p = buf; | 73 | i = nand->oobblock >> 4; p = buf; |
65 | while (i--) { | 74 | while (i--) { |
66 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x" | 75 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x" |
67 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", | 76 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", |
68 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], | 77 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], |
69 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); | 78 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); |
70 | p += 16; | 79 | p += 16; |
71 | } | 80 | } |
72 | puts("OOB:\n"); | 81 | puts("OOB:\n"); |
73 | i = nand->oobsize >> 3; | 82 | i = nand->oobsize >> 3; |
74 | while (i--) { | 83 | while (i--) { |
75 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x\n", | 84 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x\n", |
76 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); | 85 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); |
77 | p += 8; | 86 | p += 8; |
78 | } | 87 | } |
79 | free(buf); | 88 | free(buf); |
80 | 89 | ||
81 | return 0; | 90 | return 0; |
82 | } | 91 | } |
83 | 92 | ||
84 | /* ------------------------------------------------------------------------- */ | 93 | /* ------------------------------------------------------------------------- */ |
85 | 94 | ||
86 | static void | 95 | static inline int str2long(char *p, ulong *num) |
87 | arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize) | ||
88 | { | 96 | { |
89 | *off = 0; | 97 | char *endptr; |
90 | *size = 0; | ||
91 | 98 | ||
92 | #if defined(CONFIG_JFFS2_NAND) && defined(CFG_JFFS_CUSTOM_PART) | 99 | *num = simple_strtoul(p, &endptr, 16); |
93 | if (argc >= 1 && strcmp(argv[0], "partition") == 0) { | 100 | return (*p != '\0' && *endptr == '\0') ? 1 : 0; |
94 | int part_num; | 101 | } |
95 | struct part_info *part; | ||
96 | const char *partstr; | ||
97 | 102 | ||
98 | if (argc >= 2) | 103 | static int |
99 | partstr = argv[1]; | 104 | arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, ulong *size) |
100 | else | 105 | { |
101 | partstr = getenv("partition"); | 106 | int idx = nand_curr_device; |
107 | #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | ||
108 | struct mtd_device *dev; | ||
109 | struct part_info *part; | ||
110 | u8 pnum; | ||
102 | 111 | ||
103 | if (partstr) | 112 | if (argc >= 1 && !(str2long(argv[0], off))) { |
104 | part_num = (int)simple_strtoul(partstr, NULL, 10); | 113 | if ((mtdparts_init() == 0) && |
105 | else | 114 | (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) { |
106 | part_num = 0; | 115 | if (dev->id->type != MTD_DEV_TYPE_NAND) { |
107 | 116 | puts("not a NAND device\n"); | |
108 | part = jffs2_part_info(part_num); | 117 | return -1; |
109 | if (part == NULL) { | 118 | } |
110 | printf("\nInvalid partition %d\n", part_num); | 119 | *off = part->offset; |
111 | return; | 120 | if (argc >= 2) { |
121 | if (!(str2long(argv[1], size))) { | ||
122 | printf("'%s' is not a number\n", argv[1]); | ||
123 | return -1; | ||
124 | } | ||
125 | if (*size > part->size) | ||
126 | *size = part->size; | ||
127 | } else { | ||
128 | *size = part->size; | ||
129 | } | ||
130 | idx = dev->id->num; | ||
131 | *nand = nand_info[idx]; | ||
132 | goto out; | ||
112 | } | 133 | } |
113 | *size = part->size; | 134 | } |
114 | *off = (ulong)part->offset; | ||
115 | } else | ||
116 | #endif | 135 | #endif |
117 | { | ||
118 | if (argc >= 1) | ||
119 | *off = (ulong)simple_strtoul(argv[0], NULL, 16); | ||
120 | else | ||
121 | *off = 0; | ||
122 | 136 | ||
123 | if (argc >= 2) | 137 | if (argc >= 1) { |
124 | *size = (ulong)simple_strtoul(argv[1], NULL, 16); | 138 | if (!(str2long(argv[0], off))) { |
125 | else | 139 | printf("'%s' is not a number\n", argv[0]); |
126 | *size = totsize - *off; | 140 | return -1; |
141 | } | ||
142 | } else { | ||
143 | *off = 0; | ||
144 | } | ||
127 | 145 | ||
146 | if (argc >= 2) { | ||
147 | if (!(str2long(argv[1], size))) { | ||
148 | printf("'%s' is not a number\n", argv[1]); | ||
149 | return -1; | ||
150 | } | ||
151 | } else { | ||
152 | *size = nand->size - *off; | ||
128 | } | 153 | } |
129 | 154 | ||
155 | #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | ||
156 | out: | ||
157 | #endif | ||
158 | printf("device %d ", idx); | ||
159 | if (*size == nand->size) | ||
160 | puts("whole chip\n"); | ||
161 | else | ||
162 | printf("offset 0x%x, size 0x%x\n", *off, *size); | ||
163 | return 0; | ||
130 | } | 164 | } |
131 | 165 | ||
132 | int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | 166 | int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
133 | { | 167 | { |
134 | int i, dev, ret; | 168 | int i, dev, ret; |
135 | ulong addr, off, size; | 169 | ulong addr, off, size; |
136 | char *cmd, *s; | 170 | char *cmd, *s; |
137 | nand_info_t *nand; | 171 | nand_info_t *nand; |
138 | int quiet = 0; | 172 | int quiet = 0; |
139 | const char *quiet_str = getenv("quiet"); | 173 | const char *quiet_str = getenv("quiet"); |
140 | 174 | ||
141 | /* at least two arguments please */ | 175 | /* at least two arguments please */ |
142 | if (argc < 2) | 176 | if (argc < 2) |
143 | goto usage; | 177 | goto usage; |
144 | 178 | ||
145 | if (quiet_str) | 179 | if (quiet_str) |
146 | quiet = simple_strtoul(quiet_str, NULL, 0) != 0; | 180 | quiet = simple_strtoul(quiet_str, NULL, 0) != 0; |
147 | 181 | ||
148 | cmd = argv[1]; | 182 | cmd = argv[1]; |
149 | 183 | ||
150 | if (strcmp(cmd, "info") == 0) { | 184 | if (strcmp(cmd, "info") == 0) { |
151 | 185 | ||
152 | putc('\n'); | 186 | putc('\n'); |
153 | for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { | 187 | for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { |
154 | if (nand_info[i].name) | 188 | if (nand_info[i].name) |
155 | printf("Device %d: %s, sector size %lu KiB\n", | 189 | printf("Device %d: %s, sector size %lu KiB\n", |
156 | i, nand_info[i].name, | 190 | i, nand_info[i].name, |
157 | nand_info[i].erasesize >> 10); | 191 | nand_info[i].erasesize >> 10); |
158 | } | 192 | } |
159 | return 0; | 193 | return 0; |
160 | } | 194 | } |
161 | 195 | ||
162 | if (strcmp(cmd, "device") == 0) { | 196 | if (strcmp(cmd, "device") == 0) { |
163 | 197 | ||
164 | if (argc < 3) { | 198 | if (argc < 3) { |
165 | if ((nand_curr_device < 0) || | 199 | if ((nand_curr_device < 0) || |
166 | (nand_curr_device >= CFG_MAX_NAND_DEVICE)) | 200 | (nand_curr_device >= CFG_MAX_NAND_DEVICE)) |
167 | puts("\nno devices available\n"); | 201 | puts("\nno devices available\n"); |
168 | else | 202 | else |
169 | printf("\nDevice %d: %s\n", nand_curr_device, | 203 | printf("\nDevice %d: %s\n", nand_curr_device, |
170 | nand_info[nand_curr_device].name); | 204 | nand_info[nand_curr_device].name); |
171 | return 0; | 205 | return 0; |
172 | } | 206 | } |
173 | dev = (int)simple_strtoul(argv[2], NULL, 10); | 207 | dev = (int)simple_strtoul(argv[2], NULL, 10); |
174 | if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { | 208 | if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { |
175 | puts("No such device\n"); | 209 | puts("No such device\n"); |
176 | return 1; | 210 | return 1; |
177 | } | 211 | } |
178 | printf("Device %d: %s", dev, nand_info[dev].name); | 212 | printf("Device %d: %s", dev, nand_info[dev].name); |
179 | puts("... is now current device\n"); | 213 | puts("... is now current device\n"); |
180 | nand_curr_device = dev; | 214 | nand_curr_device = dev; |
181 | 215 | ||
182 | #ifdef CFG_NAND_SELECT_DEVICE | 216 | #ifdef CFG_NAND_SELECT_DEVICE |
183 | /* | 217 | /* |
184 | * Select the chip in the board/cpu specific driver | 218 | * Select the chip in the board/cpu specific driver |
185 | */ | 219 | */ |
186 | board_nand_select_device(nand_info[dev].priv, dev); | 220 | board_nand_select_device(nand_info[dev].priv, dev); |
187 | #endif | 221 | #endif |
188 | 222 | ||
189 | return 0; | 223 | return 0; |
190 | } | 224 | } |
191 | 225 | ||
192 | if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && | 226 | if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && |
193 | strncmp(cmd, "dump", 4) != 0 && | 227 | strncmp(cmd, "dump", 4) != 0 && |
194 | strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && | 228 | strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && |
195 | strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && | 229 | strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && |
196 | strcmp(cmd, "biterr") != 0 && | 230 | strcmp(cmd, "biterr") != 0 && |
197 | strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) | 231 | strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) |
198 | goto usage; | 232 | goto usage; |
199 | 233 | ||
200 | /* the following commands operate on the current device */ | 234 | /* the following commands operate on the current device */ |
201 | if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE || | 235 | if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE || |
202 | !nand_info[nand_curr_device].name) { | 236 | !nand_info[nand_curr_device].name) { |
203 | puts("\nno devices available\n"); | 237 | puts("\nno devices available\n"); |
204 | return 1; | 238 | return 1; |
205 | } | 239 | } |
206 | nand = &nand_info[nand_curr_device]; | 240 | nand = &nand_info[nand_curr_device]; |
207 | 241 | ||
208 | if (strcmp(cmd, "bad") == 0) { | 242 | if (strcmp(cmd, "bad") == 0) { |
209 | printf("\nDevice %d bad blocks:\n", nand_curr_device); | 243 | printf("\nDevice %d bad blocks:\n", nand_curr_device); |
210 | for (off = 0; off < nand->size; off += nand->erasesize) | 244 | for (off = 0; off < nand->size; off += nand->erasesize) |
211 | if (nand_block_isbad(nand, off)) | 245 | if (nand_block_isbad(nand, off)) |
212 | printf(" %08x\n", off); | 246 | printf(" %08x\n", off); |
213 | return 0; | 247 | return 0; |
214 | } | 248 | } |
215 | 249 | ||
250 | /* | ||
251 | * Syntax is: | ||
252 | * 0 1 2 3 4 | ||
253 | * nand erase [clean] [off size] | ||
254 | */ | ||
216 | if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { | 255 | if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { |
217 | nand_erase_options_t opts; | 256 | nand_erase_options_t opts; |
218 | int clean = argc >= 3 && !strcmp("clean", argv[2]); | 257 | /* "clean" at index 2 means request to write cleanmarker */ |
219 | int rest_argc = argc - 2; | 258 | int clean = argc > 2 && !strcmp("clean", argv[2]); |
220 | char **rest_argv = argv + 2; | 259 | int o = clean ? 3 : 2; |
221 | int scrub = !strcmp(cmd, "scrub"); | 260 | int scrub = !strcmp(cmd, "scrub"); |
222 | 261 | ||
223 | if (clean) { | 262 | printf("\nNAND %s: ", scrub ? "scrub" : "erase"); |
224 | rest_argc--; | 263 | /* skip first two or three arguments, look for offset and size */ |
225 | rest_argv++; | 264 | if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) |
226 | } | 265 | return 1; |
227 | 266 | ||
228 | if (rest_argc == 0) { | ||
229 | |||
230 | printf("\nNAND %s: device %d whole chip\n", | ||
231 | cmd, | ||
232 | nand_curr_device); | ||
233 | |||
234 | off = size = 0; | ||
235 | } else { | ||
236 | arg_off_size(rest_argc, rest_argv, &off, &size, | ||
237 | nand->size); | ||
238 | |||
239 | if (off == 0 && size == 0) | ||
240 | return 1; | ||
241 | |||
242 | printf("\nNAND %s: device %d offset 0x%x, size 0x%x\n", | ||
243 | cmd, nand_curr_device, off, size); | ||
244 | } | ||
245 | |||
246 | memset(&opts, 0, sizeof(opts)); | 267 | memset(&opts, 0, sizeof(opts)); |
247 | opts.offset = off; | 268 | opts.offset = off; |
248 | opts.length = size; | 269 | opts.length = size; |
249 | opts.jffs2 = clean; | 270 | opts.jffs2 = clean; |
250 | opts.quiet = quiet; | 271 | opts.quiet = quiet; |
251 | 272 | ||
252 | if (scrub) { | 273 | if (scrub) { |
253 | printf("Warning: " | 274 | puts("Warning: " |
254 | "scrub option will erase all factory set " | 275 | "scrub option will erase all factory set " |
255 | "bad blocks!\n" | 276 | "bad blocks!\n" |
256 | " " | 277 | " " |
257 | "There is no reliable way to recover them.\n" | 278 | "There is no reliable way to recover them.\n" |
258 | " " | 279 | " " |
259 | "Use this command only for testing purposes " | 280 | "Use this command only for testing purposes " |
260 | "if you\n" | 281 | "if you\n" |
261 | " " | 282 | " " |
262 | "are sure of what you are doing!\n" | 283 | "are sure of what you are doing!\n" |
263 | "\nReally scrub this NAND flash? <y/N>\n" | 284 | "\nReally scrub this NAND flash? <y/N>\n"); |
264 | ); | ||
265 | 285 | ||
266 | if (getc() == 'y' && getc() == '\r') { | 286 | if (getc() == 'y' && getc() == '\r') { |
267 | opts.scrub = 1; | 287 | opts.scrub = 1; |
268 | } else { | 288 | } else { |
269 | printf("scrub aborted\n"); | 289 | puts("scrub aborted\n"); |
270 | return -1; | 290 | return -1; |
271 | } | 291 | } |
272 | } | 292 | } |
273 | ret = nand_erase_opts(nand, &opts); | 293 | ret = nand_erase_opts(nand, &opts); |
274 | printf("%s\n", ret ? "ERROR" : "OK"); | 294 | printf("%s\n", ret ? "ERROR" : "OK"); |
275 | 295 | ||
276 | return ret == 0 ? 0 : 1; | 296 | return ret == 0 ? 0 : 1; |
277 | } | 297 | } |
278 | 298 | ||
279 | if (strncmp(cmd, "dump", 4) == 0) { | 299 | if (strncmp(cmd, "dump", 4) == 0) { |
280 | if (argc < 3) | 300 | if (argc < 3) |
281 | goto usage; | 301 | goto usage; |
282 | 302 | ||
283 | s = strchr(cmd, '.'); | 303 | s = strchr(cmd, '.'); |
284 | off = (int)simple_strtoul(argv[2], NULL, 16); | 304 | off = (int)simple_strtoul(argv[2], NULL, 16); |
285 | 305 | ||
286 | if (s != NULL && strcmp(s, ".oob") == 0) | 306 | if (s != NULL && strcmp(s, ".oob") == 0) |
287 | ret = nand_dump_oob(nand, off); | 307 | ret = nand_dump_oob(nand, off); |
288 | else | 308 | else |
289 | ret = nand_dump(nand, off); | 309 | ret = nand_dump(nand, off); |
290 | 310 | ||
291 | return ret == 0 ? 1 : 0; | 311 | return ret == 0 ? 1 : 0; |
292 | 312 | ||
293 | } | 313 | } |
294 | 314 | ||
295 | /* read write */ | 315 | /* read write */ |
296 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { | 316 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { |
297 | int read; | 317 | int read; |
298 | 318 | ||
299 | if (argc < 4) | 319 | if (argc < 4) |
300 | goto usage; | 320 | goto usage; |
301 | 321 | ||
302 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); | 322 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); |
303 | 323 | ||
304 | arg_off_size(argc - 3, argv + 3, &off, &size, nand->size); | 324 | read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ |
305 | if (off == 0 && size == 0) | 325 | printf("\nNAND %s: ", read ? "read" : "write"); |
326 | if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) | ||
306 | return 1; | 327 | return 1; |
307 | 328 | ||
308 | read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ | ||
309 | printf("\nNAND %s: device %d offset %u, size %u ... ", | ||
310 | read ? "read" : "write", nand_curr_device, off, size); | ||
311 | |||
312 | s = strchr(cmd, '.'); | 329 | s = strchr(cmd, '.'); |
313 | if (s != NULL && | 330 | if (s != NULL && |
314 | (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) { | 331 | (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) { |
315 | if (read) { | 332 | if (read) { |
316 | /* read */ | 333 | /* read */ |
317 | nand_read_options_t opts; | 334 | nand_read_options_t opts; |
318 | memset(&opts, 0, sizeof(opts)); | 335 | memset(&opts, 0, sizeof(opts)); |
319 | opts.buffer = (u_char*) addr; | 336 | opts.buffer = (u_char*) addr; |
320 | opts.length = size; | 337 | opts.length = size; |
321 | opts.offset = off; | 338 | opts.offset = off; |
322 | opts.quiet = quiet; | 339 | opts.quiet = quiet; |
323 | ret = nand_read_opts(nand, &opts); | 340 | ret = nand_read_opts(nand, &opts); |
324 | } else { | 341 | } else { |
325 | /* write */ | 342 | /* write */ |
326 | nand_write_options_t opts; | 343 | nand_write_options_t opts; |
327 | memset(&opts, 0, sizeof(opts)); | 344 | memset(&opts, 0, sizeof(opts)); |
328 | opts.buffer = (u_char*) addr; | 345 | opts.buffer = (u_char*) addr; |
329 | opts.length = size; | 346 | opts.length = size; |
330 | opts.offset = off; | 347 | opts.offset = off; |
331 | /* opts.forcejffs2 = 1; */ | 348 | /* opts.forcejffs2 = 1; */ |
332 | opts.pad = 1; | 349 | opts.pad = 1; |
333 | opts.blockalign = 1; | 350 | opts.blockalign = 1; |
334 | opts.quiet = quiet; | 351 | opts.quiet = quiet; |
335 | ret = nand_write_opts(nand, &opts); | 352 | ret = nand_write_opts(nand, &opts); |
336 | } | 353 | } |
337 | printf("%s\n", ret ? "ERROR" : "OK"); | 354 | } else { |
338 | return ret == 0 ? 0 : 1; | 355 | if (read) |
356 | ret = nand_read(nand, off, &size, (u_char *)addr); | ||
357 | else | ||
358 | ret = nand_write(nand, off, &size, (u_char *)addr); | ||
339 | } | 359 | } |
340 | 360 | ||
341 | if (read) | ||
342 | ret = nand_read(nand, off, &size, (u_char *)addr); | ||
343 | else | ||
344 | ret = nand_write(nand, off, &size, (u_char *)addr); | ||
345 | |||
346 | printf(" %d bytes %s: %s\n", size, | 361 | printf(" %d bytes %s: %s\n", size, |
347 | read ? "read" : "written", ret ? "ERROR" : "OK"); | 362 | read ? "read" : "written", ret ? "ERROR" : "OK"); |
348 | 363 | ||
349 | return ret == 0 ? 0 : 1; | 364 | return ret == 0 ? 0 : 1; |
350 | } | 365 | } |
351 | 366 | ||
352 | if (strcmp(cmd, "markbad") == 0) { | 367 | if (strcmp(cmd, "markbad") == 0) { |
353 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); | 368 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); |
354 | 369 | ||
355 | int ret = nand->block_markbad(nand, addr); | 370 | int ret = nand->block_markbad(nand, addr); |
356 | if (ret == 0) { | 371 | if (ret == 0) { |
357 | printf("block 0x%08lx successfully marked as bad\n", | 372 | printf("block 0x%08lx successfully marked as bad\n", |
358 | (ulong) addr); | 373 | (ulong) addr); |
359 | return 0; | 374 | return 0; |
360 | } else { | 375 | } else { |
361 | printf("block 0x%08lx NOT marked as bad! ERROR %d\n", | 376 | printf("block 0x%08lx NOT marked as bad! ERROR %d\n", |
362 | (ulong) addr, ret); | 377 | (ulong) addr, ret); |
363 | } | 378 | } |
364 | return 1; | 379 | return 1; |
365 | } | 380 | } |
366 | if (strcmp(cmd, "biterr") == 0) { | 381 | if (strcmp(cmd, "biterr") == 0) { |
367 | /* todo */ | 382 | /* todo */ |
368 | return 1; | 383 | return 1; |
369 | } | 384 | } |
370 | 385 | ||
371 | if (strcmp(cmd, "lock") == 0) { | 386 | if (strcmp(cmd, "lock") == 0) { |
372 | int tight = 0; | 387 | int tight = 0; |
373 | int status = 0; | 388 | int status = 0; |
374 | if (argc == 3) { | 389 | if (argc == 3) { |
375 | if (!strcmp("tight", argv[2])) | 390 | if (!strcmp("tight", argv[2])) |
376 | tight = 1; | 391 | tight = 1; |
377 | if (!strcmp("status", argv[2])) | 392 | if (!strcmp("status", argv[2])) |
378 | status = 1; | 393 | status = 1; |
379 | } | 394 | } |
380 | 395 | ||
381 | if (status) { | 396 | if (status) { |
382 | ulong block_start = 0; | 397 | ulong block_start = 0; |
383 | ulong off; | 398 | ulong off; |
384 | int last_status = -1; | 399 | int last_status = -1; |
385 | 400 | ||
386 | struct nand_chip *nand_chip = nand->priv; | 401 | struct nand_chip *nand_chip = nand->priv; |
387 | /* check the WP bit */ | 402 | /* check the WP bit */ |
388 | nand_chip->cmdfunc (nand, NAND_CMD_STATUS, -1, -1); | 403 | nand_chip->cmdfunc (nand, NAND_CMD_STATUS, -1, -1); |
389 | printf("device is %swrite protected\n", | 404 | printf("device is %swrite protected\n", |
390 | (nand_chip->read_byte(nand) & 0x80 ? | 405 | (nand_chip->read_byte(nand) & 0x80 ? |
391 | "NOT " : "" ) ); | 406 | "NOT " : "" ) ); |
392 | 407 | ||
393 | for (off = 0; off < nand->size; off += nand->oobblock) { | 408 | for (off = 0; off < nand->size; off += nand->oobblock) { |
394 | int s = nand_get_lock_status(nand, off); | 409 | int s = nand_get_lock_status(nand, off); |
395 | 410 | ||
396 | /* print message only if status has changed | 411 | /* print message only if status has changed |
397 | * or at end of chip | 412 | * or at end of chip |
398 | */ | 413 | */ |
399 | if (off == nand->size - nand->oobblock | 414 | if (off == nand->size - nand->oobblock |
400 | || (s != last_status && off != 0)) { | 415 | || (s != last_status && off != 0)) { |
401 | 416 | ||
402 | printf("%08x - %08x: %8d pages %s%s%s\n", | 417 | printf("%08x - %08x: %8d pages %s%s%s\n", |
403 | block_start, | 418 | block_start, |
404 | off-1, | 419 | off-1, |
405 | (off-block_start)/nand->oobblock, | 420 | (off-block_start)/nand->oobblock, |
406 | ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), | 421 | ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), |
407 | ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), | 422 | ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), |
408 | ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); | 423 | ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); |
409 | } | 424 | } |
410 | 425 | ||
411 | last_status = s; | 426 | last_status = s; |
412 | } | 427 | } |
413 | } else { | 428 | } else { |
414 | if (!nand_lock(nand, tight)) { | 429 | if (!nand_lock(nand, tight)) { |
415 | printf ("NAND flash successfully locked\n"); | 430 | puts("NAND flash successfully locked\n"); |
416 | } else { | 431 | } else { |
417 | printf ("Error locking NAND flash. \n"); | 432 | puts("Error locking NAND flash\n"); |
418 | return 1; | 433 | return 1; |
419 | } | 434 | } |
420 | } | 435 | } |
421 | return 0; | 436 | return 0; |
422 | } | 437 | } |
423 | 438 | ||
424 | if (strcmp(cmd, "unlock") == 0) { | 439 | if (strcmp(cmd, "unlock") == 0) { |
425 | if (argc == 2) { | 440 | if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) |
426 | off = 0; | 441 | return 1; |
427 | size = nand->size; | ||
428 | } else { | ||
429 | arg_off_size(argc - 2, argv + 2, &off, &size, | ||
430 | nand->size); | ||
431 | } | ||
432 | 442 | ||
433 | if (!nand_unlock(nand, off, size)) { | 443 | if (!nand_unlock(nand, off, size)) { |
434 | printf("NAND flash successfully unlocked\n"); | 444 | puts("NAND flash successfully unlocked\n"); |
435 | } else { | 445 | } else { |
436 | printf("Error unlocking NAND flash. " | 446 | puts("Error unlocking NAND flash, " |
437 | "Write and erase will probably fail\n"); | 447 | "write and erase will probably fail\n"); |
438 | return 1; | 448 | return 1; |
439 | } | 449 | } |
440 | return 0; | 450 | return 0; |
441 | } | 451 | } |
442 | 452 | ||
443 | usage: | 453 | usage: |
444 | printf("Usage:\n%s\n", cmdtp->usage); | 454 | printf("Usage:\n%s\n", cmdtp->usage); |
445 | return 1; | 455 | return 1; |
446 | } | 456 | } |
447 | 457 | ||
448 | U_BOOT_CMD(nand, 5, 1, do_nand, | 458 | U_BOOT_CMD(nand, 5, 1, do_nand, |
449 | "nand - NAND sub-system\n", | 459 | "nand - NAND sub-system\n", |
450 | "info - show available NAND devices\n" | 460 | "info - show available NAND devices\n" |
451 | "nand device [dev] - show or set current device\n" | 461 | "nand device [dev] - show or set current device\n" |
452 | "nand read[.jffs2] - addr off size\n" | 462 | "nand read[.jffs2] - addr off|partition size\n" |
453 | "nand write[.jffs2] - addr off size - read/write `size' bytes starting\n" | 463 | "nand write[.jffs2] - addr off|partiton size - read/write `size' bytes starting\n" |
454 | " at offset `off' to/from memory address `addr'\n" | 464 | " at offset `off' to/from memory address `addr'\n" |
455 | "nand erase [clean] [off size] - erase `size' bytes from\n" | 465 | "nand erase [clean] [off size] - erase `size' bytes from\n" |
456 | " offset `off' (entire device if not specified)\n" | 466 | " offset `off' (entire device if not specified)\n" |
457 | "nand bad - show bad blocks\n" | 467 | "nand bad - show bad blocks\n" |
458 | "nand dump[.oob] off - dump page\n" | 468 | "nand dump[.oob] off - dump page\n" |
459 | "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" | 469 | "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" |
460 | "nand markbad off - mark bad block at offset (UNSAFE)\n" | 470 | "nand markbad off - mark bad block at offset (UNSAFE)\n" |
461 | "nand biterr off - make a bit error at offset (UNSAFE)\n" | 471 | "nand biterr off - make a bit error at offset (UNSAFE)\n" |
462 | "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" | 472 | "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" |
463 | "nand unlock [offset] [size] - unlock section\n"); | 473 | "nand unlock [offset] [size] - unlock section\n"); |
464 | 474 | ||
465 | int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | 475 | static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, |
476 | ulong offset, ulong addr, char *cmd) | ||
466 | { | 477 | { |
467 | char *boot_device = NULL; | ||
468 | char *ep; | ||
469 | int dev; | ||
470 | int r; | 478 | int r; |
471 | ulong addr, cnt, offset = 0; | 479 | char *ep; |
480 | ulong cnt; | ||
472 | image_header_t *hdr; | 481 | image_header_t *hdr; |
473 | nand_info_t *nand; | ||
474 | 482 | ||
475 | switch (argc) { | 483 | printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); |
476 | case 1: | ||
477 | addr = CFG_LOAD_ADDR; | ||
478 | boot_device = getenv("bootdevice"); | ||
479 | break; | ||
480 | case 2: | ||
481 | addr = simple_strtoul(argv[1], NULL, 16); | ||
482 | boot_device = getenv("bootdevice"); | ||
483 | break; | ||
484 | case 3: | ||
485 | addr = simple_strtoul(argv[1], NULL, 16); | ||
486 | boot_device = argv[2]; | ||
487 | break; | ||
488 | case 4: | ||
489 | addr = simple_strtoul(argv[1], NULL, 16); | ||
490 | boot_device = argv[2]; | ||
491 | offset = simple_strtoul(argv[3], NULL, 16); | ||
492 | break; | ||
493 | default: | ||
494 | printf("Usage:\n%s\n", cmdtp->usage); | ||
495 | SHOW_BOOT_PROGRESS(-1); | ||
496 | return 1; | ||
497 | } | ||
498 | 484 | ||
499 | if (!boot_device) { | ||
500 | puts("\n** No boot device **\n"); | ||
501 | SHOW_BOOT_PROGRESS(-1); | ||
502 | return 1; | ||
503 | } | ||
504 | |||
505 | dev = simple_strtoul(boot_device, &ep, 16); | ||
506 | |||
507 | if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { | ||
508 | printf("\n** Device %d not available\n", dev); | ||
509 | SHOW_BOOT_PROGRESS(-1); | ||
510 | return 1; | ||
511 | } | ||
512 | |||
513 | nand = &nand_info[dev]; | ||
514 | printf("\nLoading from device %d: %s (offset 0x%lx)\n", | ||
515 | dev, nand->name, offset); | ||
516 | |||
517 | cnt = nand->oobblock; | 485 | cnt = nand->oobblock; |
518 | r = nand_read(nand, offset, &cnt, (u_char *) addr); | 486 | r = nand_read(nand, offset, &cnt, (u_char *) addr); |
519 | if (r) { | 487 | if (r) { |
520 | printf("** Read error on %d\n", dev); | 488 | puts("** Read error\n"); |
521 | SHOW_BOOT_PROGRESS(-1); | 489 | SHOW_BOOT_PROGRESS(-1); |
522 | return 1; | 490 | return 1; |
523 | } | 491 | } |
524 | 492 | ||
525 | hdr = (image_header_t *) addr; | 493 | hdr = (image_header_t *) addr; |
526 | 494 | ||
527 | if (ntohl(hdr->ih_magic) != IH_MAGIC) { | 495 | if (ntohl(hdr->ih_magic) != IH_MAGIC) { |
528 | printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic); | 496 | printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic); |
529 | SHOW_BOOT_PROGRESS(-1); | 497 | SHOW_BOOT_PROGRESS(-1); |
530 | return 1; | 498 | return 1; |
531 | } | 499 | } |
532 | 500 | ||
533 | print_image_hdr(hdr); | 501 | print_image_hdr(hdr); |
534 | 502 | ||
535 | cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t)); | 503 | cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t)); |
536 | 504 | ||
537 | r = nand_read(nand, offset, &cnt, (u_char *) addr); | 505 | r = nand_read(nand, offset, &cnt, (u_char *) addr); |
538 | if (r) { | 506 | if (r) { |
539 | printf("** Read error on %d\n", dev); | 507 | puts("** Read error\n"); |
540 | SHOW_BOOT_PROGRESS(-1); | 508 | SHOW_BOOT_PROGRESS(-1); |
541 | return 1; | 509 | return 1; |
542 | } | 510 | } |
543 | 511 | ||
544 | /* Loading ok, update default load address */ | 512 | /* Loading ok, update default load address */ |
545 | 513 | ||
546 | load_addr = addr; | 514 | load_addr = addr; |
547 | 515 | ||
548 | /* Check if we should attempt an auto-start */ | 516 | /* Check if we should attempt an auto-start */ |
549 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { | 517 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { |
550 | char *local_args[2]; | 518 | char *local_args[2]; |
551 | extern int do_bootm(cmd_tbl_t *, int, int, char *[]); | 519 | extern int do_bootm(cmd_tbl_t *, int, int, char *[]); |
552 | 520 | ||
553 | local_args[0] = argv[0]; | 521 | local_args[0] = cmd; |
554 | local_args[1] = NULL; | 522 | local_args[1] = NULL; |
555 | 523 | ||
556 | printf("Automatic boot of image at addr 0x%08lx ...\n", addr); | 524 | printf("Automatic boot of image at addr 0x%08lx ...\n", addr); |
557 | 525 | ||
558 | do_bootm(cmdtp, 0, 1, local_args); | 526 | do_bootm(cmdtp, 0, 1, local_args); |
559 | return 1; | 527 | return 1; |
560 | } | 528 | } |
561 | return 0; | 529 | return 0; |
562 | } | 530 | } |
563 | 531 | ||
564 | U_BOOT_CMD(nboot, 4, 1, do_nandboot, | 532 | int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
565 | "nboot - boot from NAND device\n", "loadAddr dev\n"); | 533 | { |
534 | char *boot_device = NULL; | ||
535 | int idx; | ||
536 | ulong addr, offset = 0; | ||
537 | #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | ||
538 | struct mtd_device *dev; | ||
539 | struct part_info *part; | ||
540 | u8 pnum; | ||
566 | 541 | ||
542 | if (argc >= 2) { | ||
543 | char *p = (argc == 2) ? argv[1] : argv[2]; | ||
544 | if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && | ||
545 | (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { | ||
546 | if (dev->id->type != MTD_DEV_TYPE_NAND) { | ||
547 | puts("Not a NAND device\n"); | ||
548 | return 1; | ||
549 | } | ||
550 | if (argc > 3) | ||
551 | goto usage; | ||
552 | if (argc == 3) | ||
553 | addr = simple_strtoul(argv[2], NULL, 16); | ||
554 | else | ||
555 | addr = CFG_LOAD_ADDR; | ||
556 | return nand_load_image(cmdtp, &nand_info[dev->id->num], | ||
557 | part->offset, addr, argv[0]); | ||
558 | } | ||
559 | } | ||
560 | #endif | ||
561 | |||
562 | switch (argc) { | ||
563 | case 1: | ||
564 | addr = CFG_LOAD_ADDR; | ||
565 | boot_device = getenv("bootdevice"); | ||
566 | break; | ||
567 | case 2: | ||
568 | addr = simple_strtoul(argv[1], NULL, 16); | ||
569 | boot_device = getenv("bootdevice"); | ||
570 | break; | ||
571 | case 3: | ||
572 | addr = simple_strtoul(argv[1], NULL, 16); | ||
573 | boot_device = argv[2]; | ||
574 | break; | ||
575 | case 4: | ||
576 | addr = simple_strtoul(argv[1], NULL, 16); | ||
577 | boot_device = argv[2]; | ||
578 | offset = simple_strtoul(argv[3], NULL, 16); | ||
579 | break; | ||
580 | default: | ||
581 | #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | ||
582 | usage: | ||
583 | #endif | ||
584 | printf("Usage:\n%s\n", cmdtp->usage); | ||
585 | SHOW_BOOT_PROGRESS(-1); | ||
586 | return 1; | ||
587 | } | ||
588 | |||
589 | if (!boot_device) { | ||
590 | puts("\n** No boot device **\n"); | ||
591 | SHOW_BOOT_PROGRESS(-1); | ||
592 | return 1; | ||
593 | } | ||
594 | |||
595 | idx = simple_strtoul(boot_device, NULL, 16); | ||
596 | |||
597 | if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) { | ||
598 | printf("\n** Device %d not available\n", idx); | ||
599 | SHOW_BOOT_PROGRESS(-1); | ||
600 | return 1; | ||
601 | } | ||
602 | |||
603 | return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); | ||
604 | } | ||
605 | |||
606 | U_BOOT_CMD(nboot, 4, 1, do_nandboot, | ||
607 | "nboot - boot from NAND device\n", | ||
608 | "[partition] | [[[loadAddr] dev] offset]\n"); | ||
567 | 609 | ||
568 | #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ | 610 | #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ |
569 | 611 | ||
570 | #else /* CFG_NAND_LEGACY */ | 612 | #else /* CFG_NAND_LEGACY */ |
571 | /* | 613 | /* |
572 | * | 614 | * |
573 | * Legacy NAND support - to be phased out | 615 | * Legacy NAND support - to be phased out |
574 | * | 616 | * |
575 | */ | 617 | */ |
576 | #include <command.h> | 618 | #include <command.h> |
577 | #include <malloc.h> | 619 | #include <malloc.h> |
578 | #include <asm/io.h> | 620 | #include <asm/io.h> |
579 | #include <watchdog.h> | 621 | #include <watchdog.h> |
580 | 622 | ||
581 | #ifdef CONFIG_SHOW_BOOT_PROGRESS | 623 | #ifdef CONFIG_SHOW_BOOT_PROGRESS |
582 | # include <status_led.h> | 624 | # include <status_led.h> |
583 | # define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg) | 625 | # define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg) |
584 | #else | 626 | #else |
585 | # define SHOW_BOOT_PROGRESS(arg) | 627 | # define SHOW_BOOT_PROGRESS(arg) |
586 | #endif | 628 | #endif |
587 | 629 | ||
588 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) | 630 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) |
589 | #include <linux/mtd/nand_legacy.h> | 631 | #include <linux/mtd/nand_legacy.h> |
590 | #if 0 | 632 | #if 0 |
591 | #include <linux/mtd/nand_ids.h> | 633 | #include <linux/mtd/nand_ids.h> |
592 | #include <jffs2/jffs2.h> | 634 | #include <jffs2/jffs2.h> |
593 | #endif | 635 | #endif |
594 | 636 | ||
595 | #ifdef CONFIG_OMAP1510 | 637 | #ifdef CONFIG_OMAP1510 |
596 | void archflashwp(void *archdata, int wp); | 638 | void archflashwp(void *archdata, int wp); |
597 | #endif | 639 | #endif |
598 | 640 | ||
599 | #define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) | 641 | #define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) |
600 | 642 | ||
601 | #undef NAND_DEBUG | 643 | #undef NAND_DEBUG |
602 | #undef PSYCHO_DEBUG | 644 | #undef PSYCHO_DEBUG |
603 | 645 | ||
604 | /* ****************** WARNING ********************* | 646 | /* ****************** WARNING ********************* |
605 | * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will | 647 | * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will |
606 | * erase (or at least attempt to erase) blocks that are marked | 648 | * erase (or at least attempt to erase) blocks that are marked |
607 | * bad. This can be very handy if you are _sure_ that the block | 649 | * bad. This can be very handy if you are _sure_ that the block |
608 | * is OK, say because you marked a good block bad to test bad | 650 | * is OK, say because you marked a good block bad to test bad |
609 | * block handling and you are done testing, or if you have | 651 | * block handling and you are done testing, or if you have |
610 | * accidentally marked blocks bad. | 652 | * accidentally marked blocks bad. |
611 | * | 653 | * |
612 | * Erasing factory marked bad blocks is a _bad_ idea. If the | 654 | * Erasing factory marked bad blocks is a _bad_ idea. If the |
613 | * erase succeeds there is no reliable way to find them again, | 655 | * erase succeeds there is no reliable way to find them again, |
614 | * and attempting to program or erase bad blocks can affect | 656 | * and attempting to program or erase bad blocks can affect |
615 | * the data in _other_ (good) blocks. | 657 | * the data in _other_ (good) blocks. |
616 | */ | 658 | */ |
617 | #define ALLOW_ERASE_BAD_DEBUG 0 | 659 | #define ALLOW_ERASE_BAD_DEBUG 0 |
618 | 660 | ||
619 | #define CONFIG_MTD_NAND_ECC /* enable ECC */ | 661 | #define CONFIG_MTD_NAND_ECC /* enable ECC */ |
620 | #define CONFIG_MTD_NAND_ECC_JFFS2 | 662 | #define CONFIG_MTD_NAND_ECC_JFFS2 |
621 | 663 | ||
622 | /* bits for nand_legacy_rw() `cmd'; or together as needed */ | 664 | /* bits for nand_legacy_rw() `cmd'; or together as needed */ |
623 | #define NANDRW_READ 0x01 | 665 | #define NANDRW_READ 0x01 |
624 | #define NANDRW_WRITE 0x00 | 666 | #define NANDRW_WRITE 0x00 |
625 | #define NANDRW_JFFS2 0x02 | 667 | #define NANDRW_JFFS2 0x02 |
626 | #define NANDRW_JFFS2_SKIP 0x04 | 668 | #define NANDRW_JFFS2_SKIP 0x04 |
627 | 669 | ||
628 | /* | 670 | /* |
629 | * Imports from nand_legacy.c | 671 | * Imports from nand_legacy.c |
630 | */ | 672 | */ |
631 | extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; | 673 | extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; |
632 | extern int curr_device; | 674 | extern int curr_device; |
633 | extern int nand_legacy_erase(struct nand_chip *nand, size_t ofs, | 675 | extern int nand_legacy_erase(struct nand_chip *nand, size_t ofs, |
634 | size_t len, int clean); | 676 | size_t len, int clean); |
635 | extern int nand_legacy_rw(struct nand_chip *nand, int cmd, size_t start, | 677 | extern int nand_legacy_rw(struct nand_chip *nand, int cmd, size_t start, |
636 | size_t len, size_t *retlen, u_char *buf); | 678 | size_t len, size_t *retlen, u_char *buf); |
637 | extern void nand_print(struct nand_chip *nand); | 679 | extern void nand_print(struct nand_chip *nand); |
638 | extern void nand_print_bad(struct nand_chip *nand); | 680 | extern void nand_print_bad(struct nand_chip *nand); |
639 | extern int nand_read_oob(struct nand_chip *nand, size_t ofs, | 681 | extern int nand_read_oob(struct nand_chip *nand, size_t ofs, |
640 | size_t len, size_t *retlen, u_char *buf); | 682 | size_t len, size_t *retlen, u_char *buf); |
641 | extern int nand_write_oob(struct nand_chip *nand, size_t ofs, | 683 | extern int nand_write_oob(struct nand_chip *nand, size_t ofs, |
642 | size_t len, size_t *retlen, const u_char *buf); | 684 | size_t len, size_t *retlen, const u_char *buf); |
643 | 685 | ||
644 | 686 | ||
645 | int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 687 | int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
646 | { | 688 | { |
647 | int rcode = 0; | 689 | int rcode = 0; |
648 | 690 | ||
649 | switch (argc) { | 691 | switch (argc) { |
650 | case 0: | 692 | case 0: |
651 | case 1: | 693 | case 1: |
652 | printf ("Usage:\n%s\n", cmdtp->usage); | 694 | printf ("Usage:\n%s\n", cmdtp->usage); |
653 | return 1; | 695 | return 1; |
654 | case 2: | 696 | case 2: |
655 | if (strcmp(argv[1],"info") == 0) { | 697 | if (strcmp(argv[1],"info") == 0) { |
656 | int i; | 698 | int i; |
657 | 699 | ||
658 | putc ('\n'); | 700 | putc ('\n'); |
659 | 701 | ||
660 | for (i=0; i<CFG_MAX_NAND_DEVICE; ++i) { | 702 | for (i=0; i<CFG_MAX_NAND_DEVICE; ++i) { |
661 | if(nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN) | 703 | if(nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN) |
662 | continue; /* list only known devices */ | 704 | continue; /* list only known devices */ |
663 | printf ("Device %d: ", i); | 705 | printf ("Device %d: ", i); |
664 | nand_print(&nand_dev_desc[i]); | 706 | nand_print(&nand_dev_desc[i]); |
665 | } | 707 | } |
666 | return 0; | 708 | return 0; |
667 | 709 | ||
668 | } else if (strcmp(argv[1],"device") == 0) { | 710 | } else if (strcmp(argv[1],"device") == 0) { |
669 | if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { | 711 | if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { |
670 | puts ("\nno devices available\n"); | 712 | puts ("\nno devices available\n"); |
671 | return 1; | 713 | return 1; |
672 | } | 714 | } |
673 | printf ("\nDevice %d: ", curr_device); | 715 | printf ("\nDevice %d: ", curr_device); |
674 | nand_print(&nand_dev_desc[curr_device]); | 716 | nand_print(&nand_dev_desc[curr_device]); |
675 | return 0; | 717 | return 0; |
676 | 718 | ||
677 | } else if (strcmp(argv[1],"bad") == 0) { | 719 | } else if (strcmp(argv[1],"bad") == 0) { |
678 | if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { | 720 | if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { |
679 | puts ("\nno devices available\n"); | 721 | puts ("\nno devices available\n"); |
680 | return 1; | 722 | return 1; |
681 | } | 723 | } |
682 | printf ("\nDevice %d bad blocks:\n", curr_device); | 724 | printf ("\nDevice %d bad blocks:\n", curr_device); |
683 | nand_print_bad(&nand_dev_desc[curr_device]); | 725 | nand_print_bad(&nand_dev_desc[curr_device]); |
684 | return 0; | 726 | return 0; |
685 | 727 | ||
686 | } | 728 | } |
687 | printf ("Usage:\n%s\n", cmdtp->usage); | 729 | printf ("Usage:\n%s\n", cmdtp->usage); |
688 | return 1; | 730 | return 1; |
689 | case 3: | 731 | case 3: |
690 | if (strcmp(argv[1],"device") == 0) { | 732 | if (strcmp(argv[1],"device") == 0) { |
691 | int dev = (int)simple_strtoul(argv[2], NULL, 10); | 733 | int dev = (int)simple_strtoul(argv[2], NULL, 10); |
692 | 734 | ||
693 | printf ("\nDevice %d: ", dev); | 735 | printf ("\nDevice %d: ", dev); |
694 | if (dev >= CFG_MAX_NAND_DEVICE) { | 736 | if (dev >= CFG_MAX_NAND_DEVICE) { |
695 | puts ("unknown device\n"); | 737 | puts ("unknown device\n"); |
696 | return 1; | 738 | return 1; |
697 | } | 739 | } |
698 | nand_print(&nand_dev_desc[dev]); | 740 | nand_print(&nand_dev_desc[dev]); |
699 | /*nand_print (dev);*/ | 741 | /*nand_print (dev);*/ |
700 | 742 | ||
701 | if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { | 743 | if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { |
702 | return 1; | 744 | return 1; |
703 | } | 745 | } |
704 | 746 | ||
705 | curr_device = dev; | 747 | curr_device = dev; |
706 | 748 | ||
707 | puts ("... is now current device\n"); | 749 | puts ("... is now current device\n"); |
708 | 750 | ||
709 | return 0; | 751 | return 0; |
710 | } | 752 | } |
711 | else if (strcmp(argv[1],"erase") == 0 && strcmp(argv[2], "clean") == 0) { | 753 | else if (strcmp(argv[1],"erase") == 0 && strcmp(argv[2], "clean") == 0) { |
712 | struct nand_chip* nand = &nand_dev_desc[curr_device]; | 754 | struct nand_chip* nand = &nand_dev_desc[curr_device]; |
713 | ulong off = 0; | 755 | ulong off = 0; |
714 | ulong size = nand->totlen; | 756 | ulong size = nand->totlen; |
715 | int ret; | 757 | int ret; |
716 | 758 | ||
717 | printf ("\nNAND erase: device %d offset %ld, size %ld ... ", | 759 | printf ("\nNAND erase: device %d offset %ld, size %ld ... ", |
718 | curr_device, off, size); | 760 | curr_device, off, size); |
719 | 761 | ||
720 | ret = nand_legacy_erase (nand, off, size, 1); | 762 | ret = nand_legacy_erase (nand, off, size, 1); |
721 | 763 | ||
722 | printf("%s\n", ret ? "ERROR" : "OK"); | 764 | printf("%s\n", ret ? "ERROR" : "OK"); |
723 | 765 | ||
724 | return ret; | 766 | return ret; |
725 | } | 767 | } |
726 | 768 | ||
727 | printf ("Usage:\n%s\n", cmdtp->usage); | 769 | printf ("Usage:\n%s\n", cmdtp->usage); |
728 | return 1; | 770 | return 1; |
729 | default: | 771 | default: |
730 | /* at least 4 args */ | 772 | /* at least 4 args */ |
731 | 773 | ||
732 | if (strncmp(argv[1], "read", 4) == 0 || | 774 | if (strncmp(argv[1], "read", 4) == 0 || |
733 | strncmp(argv[1], "write", 5) == 0) { | 775 | strncmp(argv[1], "write", 5) == 0) { |
734 | ulong addr = simple_strtoul(argv[2], NULL, 16); | 776 | ulong addr = simple_strtoul(argv[2], NULL, 16); |
735 | ulong off = simple_strtoul(argv[3], NULL, 16); | 777 | ulong off = simple_strtoul(argv[3], NULL, 16); |
736 | ulong size = simple_strtoul(argv[4], NULL, 16); | 778 | ulong size = simple_strtoul(argv[4], NULL, 16); |
737 | int cmd = (strncmp(argv[1], "read", 4) == 0) ? | 779 | int cmd = (strncmp(argv[1], "read", 4) == 0) ? |
738 | NANDRW_READ : NANDRW_WRITE; | 780 | NANDRW_READ : NANDRW_WRITE; |
739 | int ret, total; | 781 | int ret, total; |
740 | char* cmdtail = strchr(argv[1], '.'); | 782 | char* cmdtail = strchr(argv[1], '.'); |
741 | 783 | ||
742 | if (cmdtail && !strncmp(cmdtail, ".oob", 2)) { | 784 | if (cmdtail && !strncmp(cmdtail, ".oob", 2)) { |
743 | /* read out-of-band data */ | 785 | /* read out-of-band data */ |
744 | if (cmd & NANDRW_READ) { | 786 | if (cmd & NANDRW_READ) { |
745 | ret = nand_read_oob(nand_dev_desc + curr_device, | 787 | ret = nand_read_oob(nand_dev_desc + curr_device, |
746 | off, size, (size_t *)&total, | 788 | off, size, (size_t *)&total, |
747 | (u_char*)addr); | 789 | (u_char*)addr); |
748 | } | 790 | } |
749 | else { | 791 | else { |
750 | ret = nand_write_oob(nand_dev_desc + curr_device, | 792 | ret = nand_write_oob(nand_dev_desc + curr_device, |
751 | off, size, (size_t *)&total, | 793 | off, size, (size_t *)&total, |
752 | (u_char*)addr); | 794 | (u_char*)addr); |
753 | } | 795 | } |
754 | return ret; | 796 | return ret; |
755 | } | 797 | } |
756 | else if (cmdtail && !strncmp(cmdtail, ".jffs2", 2)) | 798 | else if (cmdtail && !strncmp(cmdtail, ".jffs2", 2)) |
757 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ | 799 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ |
758 | else if (cmdtail && !strncmp(cmdtail, ".jffs2s", 2)) { | 800 | else if (cmdtail && !strncmp(cmdtail, ".jffs2s", 2)) { |
759 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ | 801 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ |
760 | if (cmd & NANDRW_READ) | 802 | if (cmd & NANDRW_READ) |
761 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ | 803 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ |
762 | } | 804 | } |
763 | #ifdef SXNI855T | 805 | #ifdef SXNI855T |
764 | /* need ".e" same as ".j" for compatibility with older units */ | 806 | /* need ".e" same as ".j" for compatibility with older units */ |
765 | else if (cmdtail && !strcmp(cmdtail, ".e")) | 807 | else if (cmdtail && !strcmp(cmdtail, ".e")) |
766 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ | 808 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ |
767 | #endif | 809 | #endif |
768 | #ifdef CFG_NAND_SKIP_BAD_DOT_I | 810 | #ifdef CFG_NAND_SKIP_BAD_DOT_I |
769 | /* need ".i" same as ".jffs2s" for compatibility with older units (esd) */ | 811 | /* need ".i" same as ".jffs2s" for compatibility with older units (esd) */ |
770 | /* ".i" for image -> read skips bad block (no 0xff) */ | 812 | /* ".i" for image -> read skips bad block (no 0xff) */ |
771 | else if (cmdtail && !strcmp(cmdtail, ".i")) { | 813 | else if (cmdtail && !strcmp(cmdtail, ".i")) { |
772 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ | 814 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ |
773 | if (cmd & NANDRW_READ) | 815 | if (cmd & NANDRW_READ) |
774 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ | 816 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ |
775 | } | 817 | } |
776 | #endif /* CFG_NAND_SKIP_BAD_DOT_I */ | 818 | #endif /* CFG_NAND_SKIP_BAD_DOT_I */ |
777 | else if (cmdtail) { | 819 | else if (cmdtail) { |
778 | printf ("Usage:\n%s\n", cmdtp->usage); | 820 | printf ("Usage:\n%s\n", cmdtp->usage); |
779 | return 1; | 821 | return 1; |
780 | } | 822 | } |
781 | 823 | ||
782 | printf ("\nNAND %s: device %d offset %ld, size %ld ...\n", | 824 | printf ("\nNAND %s: device %d offset %ld, size %ld ...\n", |
783 | (cmd & NANDRW_READ) ? "read" : "write", | 825 | (cmd & NANDRW_READ) ? "read" : "write", |
784 | curr_device, off, size); | 826 | curr_device, off, size); |
785 | 827 | ||
786 | ret = nand_legacy_rw(nand_dev_desc + curr_device, cmd, off, size, | 828 | ret = nand_legacy_rw(nand_dev_desc + curr_device, cmd, off, size, |
787 | (size_t *)&total, (u_char*)addr); | 829 | (size_t *)&total, (u_char*)addr); |
788 | 830 | ||
789 | printf (" %d bytes %s: %s\n", total, | 831 | printf (" %d bytes %s: %s\n", total, |
790 | (cmd & NANDRW_READ) ? "read" : "written", | 832 | (cmd & NANDRW_READ) ? "read" : "written", |
791 | ret ? "ERROR" : "OK"); | 833 | ret ? "ERROR" : "OK"); |
792 | 834 | ||
793 | return ret; | 835 | return ret; |
794 | } else if (strcmp(argv[1],"erase") == 0 && | 836 | } else if (strcmp(argv[1],"erase") == 0 && |
795 | (argc == 4 || strcmp("clean", argv[2]) == 0)) { | 837 | (argc == 4 || strcmp("clean", argv[2]) == 0)) { |
796 | int clean = argc == 5; | 838 | int clean = argc == 5; |
797 | ulong off = simple_strtoul(argv[2 + clean], NULL, 16); | 839 | ulong off = simple_strtoul(argv[2 + clean], NULL, 16); |
798 | ulong size = simple_strtoul(argv[3 + clean], NULL, 16); | 840 | ulong size = simple_strtoul(argv[3 + clean], NULL, 16); |
799 | int ret; | 841 | int ret; |
800 | 842 | ||
801 | printf ("\nNAND erase: device %d offset %ld, size %ld ...\n", | 843 | printf ("\nNAND erase: device %d offset %ld, size %ld ...\n", |
802 | curr_device, off, size); | 844 | curr_device, off, size); |
803 | 845 | ||
804 | ret = nand_legacy_erase (nand_dev_desc + curr_device, | 846 | ret = nand_legacy_erase (nand_dev_desc + curr_device, |
805 | off, size, clean); | 847 | off, size, clean); |
806 | 848 | ||
807 | printf("%s\n", ret ? "ERROR" : "OK"); | 849 | printf("%s\n", ret ? "ERROR" : "OK"); |
808 | 850 | ||
809 | return ret; | 851 | return ret; |
810 | } else { | 852 | } else { |
811 | printf ("Usage:\n%s\n", cmdtp->usage); | 853 | printf ("Usage:\n%s\n", cmdtp->usage); |
812 | rcode = 1; | 854 | rcode = 1; |
813 | } | 855 | } |
814 | 856 | ||
815 | return rcode; | 857 | return rcode; |
816 | } | 858 | } |
817 | } | 859 | } |
818 | 860 | ||
819 | U_BOOT_CMD( | 861 | U_BOOT_CMD( |
820 | nand, 5, 1, do_nand, | 862 | nand, 5, 1, do_nand, |
821 | "nand - legacy NAND sub-system\n", | 863 | "nand - legacy NAND sub-system\n", |
822 | "info - show available NAND devices\n" | 864 | "info - show available NAND devices\n" |
823 | "nand device [dev] - show or set current device\n" | 865 | "nand device [dev] - show or set current device\n" |
824 | "nand read[.jffs2[s]] addr off size\n" | 866 | "nand read[.jffs2[s]] addr off size\n" |
825 | "nand write[.jffs2] addr off size - read/write `size' bytes starting\n" | 867 | "nand write[.jffs2] addr off size - read/write `size' bytes starting\n" |
826 | " at offset `off' to/from memory address `addr'\n" | 868 | " at offset `off' to/from memory address `addr'\n" |
827 | "nand erase [clean] [off size] - erase `size' bytes from\n" | 869 | "nand erase [clean] [off size] - erase `size' bytes from\n" |
828 | " offset `off' (entire device if not specified)\n" | 870 | " offset `off' (entire device if not specified)\n" |
829 | "nand bad - show bad blocks\n" | 871 | "nand bad - show bad blocks\n" |
830 | "nand read.oob addr off size - read out-of-band data\n" | 872 | "nand read.oob addr off size - read out-of-band data\n" |
831 | "nand write.oob addr off size - read out-of-band data\n" | 873 | "nand write.oob addr off size - read out-of-band data\n" |
832 | ); | 874 | ); |
833 | 875 | ||
834 | int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 876 | int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
835 | { | 877 | { |
836 | char *boot_device = NULL; | 878 | char *boot_device = NULL; |
837 | char *ep; | 879 | char *ep; |
838 | int dev; | 880 | int dev; |
839 | ulong cnt; | 881 | ulong cnt; |
840 | ulong addr; | 882 | ulong addr; |
841 | ulong offset = 0; | 883 | ulong offset = 0; |
842 | image_header_t *hdr; | 884 | image_header_t *hdr; |
843 | int rcode = 0; | 885 | int rcode = 0; |
844 | switch (argc) { | 886 | switch (argc) { |
845 | case 1: | 887 | case 1: |
846 | addr = CFG_LOAD_ADDR; | 888 | addr = CFG_LOAD_ADDR; |
847 | boot_device = getenv ("bootdevice"); | 889 | boot_device = getenv ("bootdevice"); |
848 | break; | 890 | break; |
849 | case 2: | 891 | case 2: |
850 | addr = simple_strtoul(argv[1], NULL, 16); | 892 | addr = simple_strtoul(argv[1], NULL, 16); |
851 | boot_device = getenv ("bootdevice"); | 893 | boot_device = getenv ("bootdevice"); |
852 | break; | 894 | break; |
853 | case 3: | 895 | case 3: |
854 | addr = simple_strtoul(argv[1], NULL, 16); | 896 | addr = simple_strtoul(argv[1], NULL, 16); |
855 | boot_device = argv[2]; | 897 | boot_device = argv[2]; |
856 | break; | 898 | break; |
857 | case 4: | 899 | case 4: |
cpu/arm925t/omap925.c
1 | /* | 1 | /* |
2 | * (C) Copyright 2003 | 2 | * (C) Copyright 2003 |
3 | * Texas Instruments <www.ti.com> | 3 | * Texas Instruments <www.ti.com> |
4 | * | 4 | * |
5 | * See file CREDITS for list of people who contributed to this | 5 | * See file CREDITS for list of people who contributed to this |
6 | * project. | 6 | * project. |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or | 8 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public License as | 9 | * modify it under the terms of the GNU General Public License as |
10 | * published by the Free Software Foundation; either version 2 of | 10 | * published by the Free Software Foundation; either version 2 of |
11 | * the License, or (at your option) any later version. | 11 | * the License, or (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
21 | * MA 02111-1307 USA | 21 | * MA 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <common.h> | 24 | #include <common.h> |
25 | #include <command.h> | 25 | #include <command.h> |
26 | #include <arm925t.h> | 26 | #include <arm925t.h> |
27 | 27 | ||
28 | ushort gpioreserved; | ||
29 | |||
30 | void gpioreserve(ushort mask) | ||
31 | { | ||
32 | gpioreserved |= mask; | ||
33 | } | ||
34 | |||
35 | void gpiosetdir(ushort mask, ushort in) | ||
36 | { | ||
37 | *(ushort *)GPIO_DIR_CONTROL_REG = (*(ushort *)GPIO_DIR_CONTROL_REG & ~mask) | (in & mask); | ||
38 | } | ||
39 | |||
40 | |||
41 | void gpiosetout(ushort mask, ushort out) | ||
42 | { | ||
43 | ushort *r_ptr, r_val; | ||
44 | |||
45 | r_ptr = (ushort *)GPIO_DATA_OUTPUT_REG; /* set pointer */ | ||
46 | r_val = *r_ptr & ~mask; /* get previous val, clear bits we want to change */ | ||
47 | r_val |= (out & mask); /* set specified bits in value + plus origional ones */ | ||
48 | *r_ptr = r_val; /* write it out */ | ||
49 | /* | ||
50 | * gcc screwed this one up :(. | ||
51 | * | ||
52 | * *(ushort *)GPIO_DATA_OUTPUT_REG = (*(ushort *)GPIO_DATA_OUTPUT_REG & ~mask) | (out & mask); | ||
53 | */ | ||
54 | |||
55 | } | ||
56 | |||
57 | void gpioinit(void) | ||
58 | { | ||
59 | } | ||
60 | |||
61 | |||
62 | #define MIF_CONFIG_REG 0xFFFECC0C | 28 | #define MIF_CONFIG_REG 0xFFFECC0C |
63 | #define FLASH_GLOBAL_CTRL_NWP 1 | 29 | #define FLASH_GLOBAL_CTRL_NWP 1 |
64 | 30 | ||
65 | void archflashwp (void *archdata, int wp) | 31 | void archflashwp (void *archdata, int wp) |
66 | { | 32 | { |
67 | ulong *fgc = (ulong *) MIF_CONFIG_REG; | 33 | ulong *fgc = (ulong *) MIF_CONFIG_REG; |
68 | 34 | ||
69 | if (wp == 1) | 35 | if (wp == 1) |
70 | *fgc &= ~FLASH_GLOBAL_CTRL_NWP; | 36 | *fgc &= ~FLASH_GLOBAL_CTRL_NWP; |
71 | else | 37 | else |
72 | *fgc |= FLASH_GLOBAL_CTRL_NWP; | 38 | *fgc |= FLASH_GLOBAL_CTRL_NWP; |
73 | } | 39 | } |
74 | 40 |
doc/README.nand
1 | NAND FLASH commands and notes | 1 | NAND FLASH commands and notes |
2 | 2 | ||
3 | See NOTE below!!! | 3 | See NOTE below!!! |
4 | 4 | ||
5 | # (C) Copyright 2003 | 5 | # (C) Copyright 2003 |
6 | # Dave Ellis, SIXNET, dge@sixnetio.com | 6 | # Dave Ellis, SIXNET, dge@sixnetio.com |
7 | # | 7 | # |
8 | # See file CREDITS for list of people who contributed to this | 8 | # See file CREDITS for list of people who contributed to this |
9 | # project. | 9 | # project. |
10 | # | 10 | # |
11 | # This program is free software; you can redistribute it and/or | 11 | # This program is free software; you can redistribute it and/or |
12 | # modify it under the terms of the GNU General Public License as | 12 | # modify it under the terms of the GNU General Public License as |
13 | # published by the Free Software Foundation; either version 2 of | 13 | # published by the Free Software Foundation; either version 2 of |
14 | # the License, or (at your option) any later version. | 14 | # the License, or (at your option) any later version. |
15 | # | 15 | # |
16 | # This program is distributed in the hope that it will be useful, | 16 | # This program is distributed in the hope that it will be useful, |
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | # GNU General Public License for more details. | 19 | # GNU General Public License for more details. |
20 | # | 20 | # |
21 | # You should have received a copy of the GNU General Public License | 21 | # You should have received a copy of the GNU General Public License |
22 | # along with this program; if not, write to the Free Software | 22 | # along with this program; if not, write to the Free Software |
23 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 23 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
24 | # MA 02111-1307 USA | 24 | # MA 02111-1307 USA |
25 | 25 | ||
26 | Commands: | 26 | Commands: |
27 | 27 | ||
28 | nand bad | 28 | nand bad |
29 | Print a list of all of the bad blocks in the current device. | 29 | Print a list of all of the bad blocks in the current device. |
30 | 30 | ||
31 | nand device | 31 | nand device |
32 | Print information about the current NAND device. | 32 | Print information about the current NAND device. |
33 | 33 | ||
34 | nand device num | 34 | nand device num |
35 | Make device `num' the current device and print information about it. | 35 | Make device `num' the current device and print information about it. |
36 | 36 | ||
37 | nand erase off size | 37 | nand erase off|partition size |
38 | nand erase clean [off size] | 38 | nand erase clean [off|partition size] |
39 | Erase `size' bytes starting at offset `off'. Only complete erase | 39 | Erase `size' bytes starting at offset `off'. Alternatively partition |
40 | blocks can be erased. | 40 | name can be specified, in this case size will be eventually limited |
41 | to not exceed partition size (this behaviour applies also to read | ||
42 | and write commands). Only complete erase blocks can be erased. | ||
41 | 43 | ||
44 | If `erase' is specified without an offset or size, the entire flash | ||
45 | is erased. If `erase' is specified with partition but without an | ||
46 | size, the entire partition is erased. | ||
47 | |||
42 | If `clean' is specified, a JFFS2-style clean marker is written to | 48 | If `clean' is specified, a JFFS2-style clean marker is written to |
43 | each block after it is erased. If `clean' is specified without an | 49 | each block after it is erased. |
44 | offset or size, the entire flash is erased. | ||
45 | 50 | ||
46 | This command will not erase blocks that are marked bad. There is | 51 | This command will not erase blocks that are marked bad. There is |
47 | a debug option in cmd_nand.c to allow bad blocks to be erased. | 52 | a debug option in cmd_nand.c to allow bad blocks to be erased. |
48 | Please read the warning there before using it, as blocks marked | 53 | Please read the warning there before using it, as blocks marked |
49 | bad by the manufacturer must _NEVER_ be erased. | 54 | bad by the manufacturer must _NEVER_ be erased. |
50 | 55 | ||
51 | nand info | 56 | nand info |
52 | Print information about all of the NAND devices found. | 57 | Print information about all of the NAND devices found. |
53 | 58 | ||
54 | nand read addr ofs size | 59 | nand read addr ofs|partition size |
55 | Read `size' bytes from `ofs' in NAND flash to `addr'. If a page | 60 | Read `size' bytes from `ofs' in NAND flash to `addr'. If a page |
56 | cannot be read because it is marked bad or an uncorrectable data | 61 | cannot be read because it is marked bad or an uncorrectable data |
57 | error is found the command stops with an error. | 62 | error is found the command stops with an error. |
58 | 63 | ||
59 | nand read.jffs2 addr ofs size | 64 | nand read.jffs2 addr ofs|partition size |
60 | Like `read', but the data for blocks that are marked bad is read as | 65 | Like `read', but the data for blocks that are marked bad is read as |
61 | 0xff. This gives a readable JFFS2 image that can be processed by | 66 | 0xff. This gives a readable JFFS2 image that can be processed by |
62 | the JFFS2 commands such as ls and fsload. | 67 | the JFFS2 commands such as ls and fsload. |
63 | 68 | ||
64 | nand read.oob addr ofs size | 69 | nand read.oob addr ofs|partition size |
65 | Read `size' bytes from the out-of-band data area corresponding to | 70 | Read `size' bytes from the out-of-band data area corresponding to |
66 | `ofs' in NAND flash to `addr'. This is limited to the 16 bytes of | 71 | `ofs' in NAND flash to `addr'. This is limited to the 16 bytes of |
67 | data for one 512-byte page or 2 256-byte pages. There is no check | 72 | data for one 512-byte page or 2 256-byte pages. There is no check |
68 | for bad blocks or ECC errors. | 73 | for bad blocks or ECC errors. |
69 | 74 | ||
70 | nand write addr ofs size | 75 | nand write addr ofs|partition size |
71 | Write `size' bytes from `addr' to `ofs' in NAND flash. If a page | 76 | Write `size' bytes from `addr' to `ofs' in NAND flash. If a page |
72 | cannot be written because it is marked bad or the write fails the | 77 | cannot be written because it is marked bad or the write fails the |
73 | command stops with an error. | 78 | command stops with an error. |
74 | 79 | ||
75 | nand write.jffs2 addr ofs size | 80 | nand write.jffs2 addr ofs|partition size |
76 | Like `write', but blocks that are marked bad are skipped and the | 81 | Like `write', but blocks that are marked bad are skipped and the |
77 | is written to the next block instead. This allows writing writing | 82 | is written to the next block instead. This allows writing writing |
78 | a JFFS2 image, as long as the image is short enough to fit even | 83 | a JFFS2 image, as long as the image is short enough to fit even |
79 | after skipping the bad blocks. Compact images, such as those | 84 | after skipping the bad blocks. Compact images, such as those |
80 | produced by mkfs.jffs2 should work well, but loading an image copied | 85 | produced by mkfs.jffs2 should work well, but loading an image copied |
81 | from another flash is going to be trouble if there are any bad blocks. | 86 | from another flash is going to be trouble if there are any bad blocks. |
82 | 87 | ||
83 | nand write.oob addr ofs size | 88 | nand write.oob addr ofs|partition size |
84 | Write `size' bytes from `addr' to the out-of-band data area | 89 | Write `size' bytes from `addr' to the out-of-band data area |
85 | corresponding to `ofs' in NAND flash. This is limited to the 16 bytes | 90 | corresponding to `ofs' in NAND flash. This is limited to the 16 bytes |
86 | of data for one 512-byte page or 2 256-byte pages. There is no check | 91 | of data for one 512-byte page or 2 256-byte pages. There is no check |
87 | for bad blocks. | 92 | for bad blocks. |
88 | 93 | ||
89 | Configuration Options: | 94 | Configuration Options: |
90 | 95 | ||
91 | CFG_CMD_NAND | 96 | CFG_CMD_NAND |
92 | A good one to add to CONFIG_COMMANDS since it enables NAND support. | 97 | A good one to add to CONFIG_COMMANDS since it enables NAND support. |
93 | 98 | ||
94 | CONFIG_MTD_NAND_ECC_JFFS2 | 99 | CONFIG_MTD_NAND_ECC_JFFS2 |
95 | Define this if you want the Error Correction Code information in | 100 | Define this if you want the Error Correction Code information in |
96 | the out-of-band data to be formatted to match the JFFS2 file system. | 101 | the out-of-band data to be formatted to match the JFFS2 file system. |
97 | CONFIG_MTD_NAND_ECC_YAFFS would be another useful choice for | 102 | CONFIG_MTD_NAND_ECC_YAFFS would be another useful choice for |
98 | someone to implement. | 103 | someone to implement. |
99 | 104 | ||
100 | CFG_MAX_NAND_DEVICE | 105 | CFG_MAX_NAND_DEVICE |
101 | The maximum number of NAND devices you want to support. | 106 | The maximum number of NAND devices you want to support. |
102 | 107 | ||
103 | NAND Interface: | 108 | NAND Interface: |
104 | 109 | ||
105 | #define NAND_WAIT_READY(nand) | 110 | #define NAND_WAIT_READY(nand) |
106 | Wait until the NAND flash is ready. Typically this would be a | 111 | Wait until the NAND flash is ready. Typically this would be a |
107 | loop waiting for the READY/BUSY line from the flash to indicate it | 112 | loop waiting for the READY/BUSY line from the flash to indicate it |
108 | it is ready. | 113 | it is ready. |
109 | 114 | ||
110 | #define WRITE_NAND_COMMAND(d, adr) | 115 | #define WRITE_NAND_COMMAND(d, adr) |
111 | Write the command byte `d' to the flash at `adr' with the | 116 | Write the command byte `d' to the flash at `adr' with the |
112 | CLE (command latch enable) line true. If your board uses writes to | 117 | CLE (command latch enable) line true. If your board uses writes to |
113 | different addresses to control CLE and ALE, you can modify `adr' | 118 | different addresses to control CLE and ALE, you can modify `adr' |
114 | to be the appropriate address here. If your board uses I/O registers | 119 | to be the appropriate address here. If your board uses I/O registers |
115 | to control them, it is probably better to let NAND_CTL_SETCLE() | 120 | to control them, it is probably better to let NAND_CTL_SETCLE() |
116 | and company do it. | 121 | and company do it. |
117 | 122 | ||
118 | #define WRITE_NAND_ADDRESS(d, adr) | 123 | #define WRITE_NAND_ADDRESS(d, adr) |
119 | Write the address byte `d' to the flash at `adr' with the | 124 | Write the address byte `d' to the flash at `adr' with the |
120 | ALE (address latch enable) line true. If your board uses writes to | 125 | ALE (address latch enable) line true. If your board uses writes to |
121 | different addresses to control CLE and ALE, you can modify `adr' | 126 | different addresses to control CLE and ALE, you can modify `adr' |
122 | to be the appropriate address here. If your board uses I/O registers | 127 | to be the appropriate address here. If your board uses I/O registers |
123 | to control them, it is probably better to let NAND_CTL_SETALE() | 128 | to control them, it is probably better to let NAND_CTL_SETALE() |
124 | and company do it. | 129 | and company do it. |
125 | 130 | ||
126 | #define WRITE_NAND(d, adr) | 131 | #define WRITE_NAND(d, adr) |
127 | Write the data byte `d' to the flash at `adr' with the | 132 | Write the data byte `d' to the flash at `adr' with the |
128 | ALE and CLE lines false. If your board uses writes to | 133 | ALE and CLE lines false. If your board uses writes to |
129 | different addresses to control CLE and ALE, you can modify `adr' | 134 | different addresses to control CLE and ALE, you can modify `adr' |
130 | to be the appropriate address here. If your board uses I/O registers | 135 | to be the appropriate address here. If your board uses I/O registers |
131 | to control them, it is probably better to let NAND_CTL_CLRALE() | 136 | to control them, it is probably better to let NAND_CTL_CLRALE() |
132 | and company do it. | 137 | and company do it. |
133 | 138 | ||
134 | #define READ_NAND(adr) | 139 | #define READ_NAND(adr) |
135 | Read a data byte from the flash at `adr' with the | 140 | Read a data byte from the flash at `adr' with the |
136 | ALE and CLE lines false. If your board uses reads from | 141 | ALE and CLE lines false. If your board uses reads from |
137 | different addresses to control CLE and ALE, you can modify `adr' | 142 | different addresses to control CLE and ALE, you can modify `adr' |
138 | to be the appropriate address here. If your board uses I/O registers | 143 | to be the appropriate address here. If your board uses I/O registers |
139 | to control them, it is probably better to let NAND_CTL_CLRALE() | 144 | to control them, it is probably better to let NAND_CTL_CLRALE() |
140 | and company do it. | 145 | and company do it. |
141 | 146 | ||
142 | #define NAND_DISABLE_CE(nand) | 147 | #define NAND_DISABLE_CE(nand) |
143 | Set CE (Chip Enable) low to enable the NAND flash. | 148 | Set CE (Chip Enable) low to enable the NAND flash. |
144 | 149 | ||
145 | #define NAND_ENABLE_CE(nand) | 150 | #define NAND_ENABLE_CE(nand) |
146 | Set CE (Chip Enable) high to disable the NAND flash. | 151 | Set CE (Chip Enable) high to disable the NAND flash. |
147 | 152 | ||
148 | #define NAND_CTL_CLRALE(nandptr) | 153 | #define NAND_CTL_CLRALE(nandptr) |
149 | Set ALE (address latch enable) low. If ALE control is handled by | 154 | Set ALE (address latch enable) low. If ALE control is handled by |
150 | WRITE_NAND_ADDRESS() this can be empty. | 155 | WRITE_NAND_ADDRESS() this can be empty. |
151 | 156 | ||
152 | #define NAND_CTL_SETALE(nandptr) | 157 | #define NAND_CTL_SETALE(nandptr) |
153 | Set ALE (address latch enable) high. If ALE control is handled by | 158 | Set ALE (address latch enable) high. If ALE control is handled by |
154 | WRITE_NAND_ADDRESS() this can be empty. | 159 | WRITE_NAND_ADDRESS() this can be empty. |
155 | 160 | ||
156 | #define NAND_CTL_CLRCLE(nandptr) | 161 | #define NAND_CTL_CLRCLE(nandptr) |
157 | Set CLE (command latch enable) low. If CLE control is handled by | 162 | Set CLE (command latch enable) low. If CLE control is handled by |
158 | WRITE_NAND_ADDRESS() this can be empty. | 163 | WRITE_NAND_ADDRESS() this can be empty. |
159 | 164 | ||
160 | #define NAND_CTL_SETCLE(nandptr) | 165 | #define NAND_CTL_SETCLE(nandptr) |
161 | Set CLE (command latch enable) high. If CLE control is handled by | 166 | Set CLE (command latch enable) high. If CLE control is handled by |
162 | WRITE_NAND_ADDRESS() this can be empty. | 167 | WRITE_NAND_ADDRESS() this can be empty. |
163 | 168 | ||
164 | More Definitions: | 169 | More Definitions: |
165 | 170 | ||
166 | These definitions are needed in the board configuration for now, but | 171 | These definitions are needed in the board configuration for now, but |
167 | may really belong in a header file. | 172 | may really belong in a header file. |
168 | TODO: Figure which ones are truly configuration settings and rename | 173 | TODO: Figure which ones are truly configuration settings and rename |
169 | them to CFG_NAND_... and move the rest somewhere appropriate. | 174 | them to CFG_NAND_... and move the rest somewhere appropriate. |
170 | 175 | ||
171 | #define SECTORSIZE 512 | 176 | #define SECTORSIZE 512 |
172 | #define ADDR_COLUMN 1 | 177 | #define ADDR_COLUMN 1 |
173 | #define ADDR_PAGE 2 | 178 | #define ADDR_PAGE 2 |
174 | #define ADDR_COLUMN_PAGE 3 | 179 | #define ADDR_COLUMN_PAGE 3 |
175 | #define NAND_ChipID_UNKNOWN 0x00 | 180 | #define NAND_ChipID_UNKNOWN 0x00 |
176 | #define NAND_MAX_FLOORS 1 | 181 | #define NAND_MAX_FLOORS 1 |
177 | #define NAND_MAX_CHIPS 1 | 182 | #define NAND_MAX_CHIPS 1 |
178 | 183 | ||
179 | 184 | ||
180 | NOTE: | 185 | NOTE: |
181 | ===== | 186 | ===== |
182 | 187 | ||
183 | We now use a complete rewrite of the NAND code based on what is in | 188 | We now use a complete rewrite of the NAND code based on what is in |
184 | 2.6.12 Linux kernel. | 189 | 2.6.12 Linux kernel. |
185 | 190 | ||
186 | The old NAND handling code has been re-factored and is now confined | 191 | The old NAND handling code has been re-factored and is now confined |
187 | to only board-specific files and - unfortunately - to the DoC code | 192 | to only board-specific files and - unfortunately - to the DoC code |
188 | (see below). A new configuration variable has been introduced: | 193 | (see below). A new configuration variable has been introduced: |
189 | CFG_NAND_LEGACY, which has to be defined in the board config file if | 194 | CFG_NAND_LEGACY, which has to be defined in the board config file if |
190 | that board uses legacy code. If CFG_NAND_LEGACY is defined, the board | 195 | that board uses legacy code. If CFG_NAND_LEGACY is defined, the board |
191 | specific config.mk file should also have "BOARDLIBS = | 196 | specific config.mk file should also have "BOARDLIBS = |
192 | drivers/nand_legacy/libnand_legacy.a". For boards using the new NAND | 197 | drivers/nand_legacy/libnand_legacy.a". For boards using the new NAND |
193 | approach (PPChameleon and netstar at the moment) no variable is | 198 | approach (PPChameleon and netstar at the moment) no variable is |
194 | necessary, but the config.mk should have "BOARDLIBS = | 199 | necessary, but the config.mk should have "BOARDLIBS = |
195 | drivers/nand/libnand.a". | 200 | drivers/nand/libnand.a". |
196 | 201 | ||
197 | The necessary changes have been made to all affected boards, and no | 202 | The necessary changes have been made to all affected boards, and no |
198 | build breakage has been introduced, except for NETTA and NETTA_ISDN | 203 | build breakage has been introduced, except for NETTA and NETTA_ISDN |
199 | targets from MAKEALL. This is due to the fact that these two boards | 204 | targets from MAKEALL. This is due to the fact that these two boards |
200 | use JFFS, which has been adopted to use the new NAND, and at the same | 205 | use JFFS, which has been adopted to use the new NAND, and at the same |
201 | time use NAND in legacy mode. The breakage will disappear when the | 206 | time use NAND in legacy mode. The breakage will disappear when the |
202 | board-specific code is changed to the new NAND. | 207 | board-specific code is changed to the new NAND. |
203 | 208 | ||
204 | As mentioned above, the legacy code is still used by the DoC subsystem. | 209 | As mentioned above, the legacy code is still used by the DoC subsystem. |
205 | The consequence of this is that the legacy NAND can't be removed from | 210 | The consequence of this is that the legacy NAND can't be removed from |
206 | the tree until the DoC is ported to use the new NAND support (or boards | 211 | the tree until the DoC is ported to use the new NAND support (or boards |
207 | with DoC will break). | 212 | with DoC will break). |
208 | 213 | ||
209 | 214 | ||
210 | Additional improvements to the NAND subsystem by Guido Classen, 10-10-2006 | 215 | Additional improvements to the NAND subsystem by Guido Classen, 10-10-2006 |
211 | 216 | ||
212 | JFFS2 related commands: | 217 | JFFS2 related commands: |
213 | 218 | ||
214 | implement "nand erase clean" and old "nand erase" | 219 | implement "nand erase clean" and old "nand erase" |
215 | using both the new code which is able to skip bad blocks | 220 | using both the new code which is able to skip bad blocks |
216 | "nand erase clean" additionally writes JFFS2-cleanmarkers in the oob. | 221 | "nand erase clean" additionally writes JFFS2-cleanmarkers in the oob. |
217 | 222 | ||
218 | "nand write.jffs2" | 223 | "nand write.jffs2" |
219 | like "nand write" but skip found bad eraseblocks | 224 | like "nand write" but skip found bad eraseblocks |
220 | 225 | ||
221 | "nand read.jffs2" | 226 | "nand read.jffs2" |
222 | like "nand read" but skip found bad eraseblocks | 227 | like "nand read" but skip found bad eraseblocks |
223 | 228 | ||
224 | Miscellaneous and testing commands: | 229 | Miscellaneous and testing commands: |
225 | "markbad [offset]" | 230 | "markbad [offset]" |
226 | create an artificial bad block (for testing bad block handling) | 231 | create an artificial bad block (for testing bad block handling) |
227 | 232 | ||
228 | "scrub [offset length]" | 233 | "scrub [offset length]" |
229 | like "erase" but don't skip bad block. Instead erase them. | 234 | like "erase" but don't skip bad block. Instead erase them. |
230 | DANGEROUS!!! Factory set bad blocks will be lost. Use only | 235 | DANGEROUS!!! Factory set bad blocks will be lost. Use only |
231 | to remove artificial bad blocks created with the "markbad" command. | 236 | to remove artificial bad blocks created with the "markbad" command. |
232 | 237 | ||
233 | 238 | ||
234 | NAND locking command (for chips with active LOCKPRE pin) | 239 | NAND locking command (for chips with active LOCKPRE pin) |
235 | 240 | ||
236 | "nand lock" | 241 | "nand lock" |
237 | set NAND chip to lock state (all pages locked) | 242 | set NAND chip to lock state (all pages locked) |
238 | 243 | ||
239 | "nand lock tight" | 244 | "nand lock tight" |
240 | set NAND chip to lock tight state (software can't change locking anymore) | 245 | set NAND chip to lock tight state (software can't change locking anymore) |
241 | 246 | ||
242 | "nand lock status" | 247 | "nand lock status" |
243 | displays current locking status of all pages | 248 | displays current locking status of all pages |
244 | 249 | ||
245 | "nand unlock [offset] [size]" | 250 | "nand unlock [offset] [size]" |
246 | unlock consecutive area (can be called multiple times for different areas) | 251 | unlock consecutive area (can be called multiple times for different areas) |
247 | 252 | ||
248 | 253 | ||
249 | I have tested the code with board containing 128MiB NAND large page chips | 254 | I have tested the code with board containing 128MiB NAND large page chips |
250 | and 32MiB small page chips. | 255 | and 32MiB small page chips. |
drivers/nand/nand_util.c
1 | /* | 1 | /* |
2 | * drivers/nand/nand_util.c | 2 | * drivers/nand/nand_util.c |
3 | * | 3 | * |
4 | * Copyright (C) 2006 by Weiss-Electronic GmbH. | 4 | * Copyright (C) 2006 by Weiss-Electronic GmbH. |
5 | * All rights reserved. | 5 | * All rights reserved. |
6 | * | 6 | * |
7 | * @author: Guido Classen <clagix@gmail.com> | 7 | * @author: Guido Classen <clagix@gmail.com> |
8 | * @descr: NAND Flash support | 8 | * @descr: NAND Flash support |
9 | * @references: borrowed heavily from Linux mtd-utils code: | 9 | * @references: borrowed heavily from Linux mtd-utils code: |
10 | * flash_eraseall.c by Arcom Control System Ltd | 10 | * flash_eraseall.c by Arcom Control System Ltd |
11 | * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) | 11 | * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) |
12 | * and Thomas Gleixner (tglx@linutronix.de) | 12 | * and Thomas Gleixner (tglx@linutronix.de) |
13 | * | 13 | * |
14 | * See file CREDITS for list of people who contributed to this | 14 | * See file CREDITS for list of people who contributed to this |
15 | * project. | 15 | * project. |
16 | * | 16 | * |
17 | * This program is free software; you can redistribute it and/or | 17 | * This program is free software; you can redistribute it and/or |
18 | * modify it under the terms of the GNU General Public License version | 18 | * modify it under the terms of the GNU General Public License version |
19 | * 2 as published by the Free Software Foundation. | 19 | * 2 as published by the Free Software Foundation. |
20 | * | 20 | * |
21 | * This program is distributed in the hope that it will be useful, | 21 | * This program is distributed in the hope that it will be useful, |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 | * GNU General Public License for more details. | 24 | * GNU General Public License for more details. |
25 | * | 25 | * |
26 | * You should have received a copy of the GNU General Public License | 26 | * You should have received a copy of the GNU General Public License |
27 | * along with this program; if not, write to the Free Software | 27 | * along with this program; if not, write to the Free Software |
28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
29 | * MA 02111-1307 USA | 29 | * MA 02111-1307 USA |
30 | * | 30 | * |
31 | */ | 31 | */ |
32 | 32 | ||
33 | #include <common.h> | 33 | #include <common.h> |
34 | 34 | ||
35 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) | 35 | #if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) |
36 | 36 | ||
37 | #include <command.h> | 37 | #include <command.h> |
38 | #include <watchdog.h> | 38 | #include <watchdog.h> |
39 | #include <malloc.h> | 39 | #include <malloc.h> |
40 | 40 | ||
41 | #include <nand.h> | 41 | #include <nand.h> |
42 | #include <jffs2/jffs2.h> | 42 | #include <jffs2/jffs2.h> |
43 | 43 | ||
44 | typedef struct erase_info erase_info_t; | 44 | typedef struct erase_info erase_info_t; |
45 | typedef struct mtd_info mtd_info_t; | 45 | typedef struct mtd_info mtd_info_t; |
46 | 46 | ||
47 | /* support only for native endian JFFS2 */ | 47 | /* support only for native endian JFFS2 */ |
48 | #define cpu_to_je16(x) (x) | 48 | #define cpu_to_je16(x) (x) |
49 | #define cpu_to_je32(x) (x) | 49 | #define cpu_to_je32(x) (x) |
50 | 50 | ||
51 | /*****************************************************************************/ | 51 | /*****************************************************************************/ |
52 | static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) | 52 | static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) |
53 | { | 53 | { |
54 | return 0; | 54 | return 0; |
55 | } | 55 | } |
56 | 56 | ||
57 | /** | 57 | /** |
58 | * nand_erase_opts: - erase NAND flash with support for various options | 58 | * nand_erase_opts: - erase NAND flash with support for various options |
59 | * (jffs2 formating) | 59 | * (jffs2 formating) |
60 | * | 60 | * |
61 | * @param meminfo NAND device to erase | 61 | * @param meminfo NAND device to erase |
62 | * @param opts options, @see struct nand_erase_options | 62 | * @param opts options, @see struct nand_erase_options |
63 | * @return 0 in case of success | 63 | * @return 0 in case of success |
64 | * | 64 | * |
65 | * This code is ported from flash_eraseall.c from Linux mtd utils by | 65 | * This code is ported from flash_eraseall.c from Linux mtd utils by |
66 | * Arcom Control System Ltd. | 66 | * Arcom Control System Ltd. |
67 | */ | 67 | */ |
68 | int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) | 68 | int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) |
69 | { | 69 | { |
70 | struct jffs2_unknown_node cleanmarker; | 70 | struct jffs2_unknown_node cleanmarker; |
71 | int clmpos = 0; | 71 | int clmpos = 0; |
72 | int clmlen = 8; | 72 | int clmlen = 8; |
73 | erase_info_t erase; | 73 | erase_info_t erase; |
74 | ulong erase_length; | 74 | ulong erase_length; |
75 | int isNAND; | 75 | int isNAND; |
76 | int bbtest = 1; | 76 | int bbtest = 1; |
77 | int result; | 77 | int result; |
78 | int percent_complete = -1; | 78 | int percent_complete = -1; |
79 | int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; | 79 | int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; |
80 | const char *mtd_device = meminfo->name; | 80 | const char *mtd_device = meminfo->name; |
81 | 81 | ||
82 | memset(&erase, 0, sizeof(erase)); | 82 | memset(&erase, 0, sizeof(erase)); |
83 | 83 | ||
84 | erase.mtd = meminfo; | 84 | erase.mtd = meminfo; |
85 | erase.len = meminfo->erasesize; | 85 | erase.len = meminfo->erasesize; |
86 | if (opts->offset == 0 && opts->length == 0) { | 86 | erase.addr = opts->offset; |
87 | /* erase complete chip */ | 87 | erase_length = opts->length; |
88 | erase.addr = 0; | ||
89 | erase_length = meminfo->size; | ||
90 | } else { | ||
91 | /* erase specified region */ | ||
92 | erase.addr = opts->offset; | ||
93 | erase_length = opts->length; | ||
94 | } | ||
95 | 88 | ||
96 | isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; | 89 | isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; |
97 | 90 | ||
98 | if (opts->jffs2) { | 91 | if (opts->jffs2) { |
99 | cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); | 92 | cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); |
100 | cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); | 93 | cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); |
101 | if (isNAND) { | 94 | if (isNAND) { |
102 | struct nand_oobinfo *oobinfo = &meminfo->oobinfo; | 95 | struct nand_oobinfo *oobinfo = &meminfo->oobinfo; |
103 | 96 | ||
104 | /* check for autoplacement */ | 97 | /* check for autoplacement */ |
105 | if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { | 98 | if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { |
106 | /* get the position of the free bytes */ | 99 | /* get the position of the free bytes */ |
107 | if (!oobinfo->oobfree[0][1]) { | 100 | if (!oobinfo->oobfree[0][1]) { |
108 | printf(" Eeep. Autoplacement selected " | 101 | printf(" Eeep. Autoplacement selected " |
109 | "and no empty space in oob\n"); | 102 | "and no empty space in oob\n"); |
110 | return -1; | 103 | return -1; |
111 | } | 104 | } |
112 | clmpos = oobinfo->oobfree[0][0]; | 105 | clmpos = oobinfo->oobfree[0][0]; |
113 | clmlen = oobinfo->oobfree[0][1]; | 106 | clmlen = oobinfo->oobfree[0][1]; |
114 | if (clmlen > 8) | 107 | if (clmlen > 8) |
115 | clmlen = 8; | 108 | clmlen = 8; |
116 | } else { | 109 | } else { |
117 | /* legacy mode */ | 110 | /* legacy mode */ |
118 | switch (meminfo->oobsize) { | 111 | switch (meminfo->oobsize) { |
119 | case 8: | 112 | case 8: |
120 | clmpos = 6; | 113 | clmpos = 6; |
121 | clmlen = 2; | 114 | clmlen = 2; |
122 | break; | 115 | break; |
123 | case 16: | 116 | case 16: |
124 | clmpos = 8; | 117 | clmpos = 8; |
125 | clmlen = 8; | 118 | clmlen = 8; |
126 | break; | 119 | break; |
127 | case 64: | 120 | case 64: |
128 | clmpos = 16; | 121 | clmpos = 16; |
129 | clmlen = 8; | 122 | clmlen = 8; |
130 | break; | 123 | break; |
131 | } | 124 | } |
132 | } | 125 | } |
133 | 126 | ||
134 | cleanmarker.totlen = cpu_to_je32(8); | 127 | cleanmarker.totlen = cpu_to_je32(8); |
135 | } else { | 128 | } else { |
136 | cleanmarker.totlen = | 129 | cleanmarker.totlen = |
137 | cpu_to_je32(sizeof(struct jffs2_unknown_node)); | 130 | cpu_to_je32(sizeof(struct jffs2_unknown_node)); |
138 | } | 131 | } |
139 | cleanmarker.hdr_crc = cpu_to_je32( | 132 | cleanmarker.hdr_crc = cpu_to_je32( |
140 | crc32_no_comp(0, (unsigned char *) &cleanmarker, | 133 | crc32_no_comp(0, (unsigned char *) &cleanmarker, |
141 | sizeof(struct jffs2_unknown_node) - 4)); | 134 | sizeof(struct jffs2_unknown_node) - 4)); |
142 | } | 135 | } |
143 | 136 | ||
144 | /* scrub option allows to erase badblock. To prevent internal | 137 | /* scrub option allows to erase badblock. To prevent internal |
145 | * check from erase() method, set block check method to dummy | 138 | * check from erase() method, set block check method to dummy |
146 | * and disable bad block table while erasing. | 139 | * and disable bad block table while erasing. |
147 | */ | 140 | */ |
148 | if (opts->scrub) { | 141 | if (opts->scrub) { |
149 | struct nand_chip *priv_nand = meminfo->priv; | 142 | struct nand_chip *priv_nand = meminfo->priv; |
150 | 143 | ||
151 | nand_block_bad_old = priv_nand->block_bad; | 144 | nand_block_bad_old = priv_nand->block_bad; |
152 | priv_nand->block_bad = nand_block_bad_scrub; | 145 | priv_nand->block_bad = nand_block_bad_scrub; |
153 | /* we don't need the bad block table anymore... | 146 | /* we don't need the bad block table anymore... |
154 | * after scrub, there are no bad blocks left! | 147 | * after scrub, there are no bad blocks left! |
155 | */ | 148 | */ |
156 | if (priv_nand->bbt) { | 149 | if (priv_nand->bbt) { |
157 | kfree(priv_nand->bbt); | 150 | kfree(priv_nand->bbt); |
158 | } | 151 | } |
159 | priv_nand->bbt = NULL; | 152 | priv_nand->bbt = NULL; |
160 | } | 153 | } |
161 | 154 | ||
162 | for (; | 155 | for (; |
163 | erase.addr < opts->offset + erase_length; | 156 | erase.addr < opts->offset + erase_length; |
164 | erase.addr += meminfo->erasesize) { | 157 | erase.addr += meminfo->erasesize) { |
165 | 158 | ||
166 | WATCHDOG_RESET (); | 159 | WATCHDOG_RESET (); |
167 | 160 | ||
168 | if (!opts->scrub && bbtest) { | 161 | if (!opts->scrub && bbtest) { |
169 | int ret = meminfo->block_isbad(meminfo, erase.addr); | 162 | int ret = meminfo->block_isbad(meminfo, erase.addr); |
170 | if (ret > 0) { | 163 | if (ret > 0) { |
171 | if (!opts->quiet) | 164 | if (!opts->quiet) |
172 | printf("\rSkipping bad block at " | 165 | printf("\rSkipping bad block at " |
173 | "0x%08x " | 166 | "0x%08x " |
174 | " \n", | 167 | " \n", |
175 | erase.addr); | 168 | erase.addr); |
176 | continue; | 169 | continue; |
177 | 170 | ||
178 | } else if (ret < 0) { | 171 | } else if (ret < 0) { |
179 | printf("\n%s: MTD get bad block failed: %d\n", | 172 | printf("\n%s: MTD get bad block failed: %d\n", |
180 | mtd_device, | 173 | mtd_device, |
181 | ret); | 174 | ret); |
182 | return -1; | 175 | return -1; |
183 | } | 176 | } |
184 | } | 177 | } |
185 | 178 | ||
186 | result = meminfo->erase(meminfo, &erase); | 179 | result = meminfo->erase(meminfo, &erase); |
187 | if (result != 0) { | 180 | if (result != 0) { |
188 | printf("\n%s: MTD Erase failure: %d\n", | 181 | printf("\n%s: MTD Erase failure: %d\n", |
189 | mtd_device, result); | 182 | mtd_device, result); |
190 | continue; | 183 | continue; |
191 | } | 184 | } |
192 | 185 | ||
193 | /* format for JFFS2 ? */ | 186 | /* format for JFFS2 ? */ |
194 | if (opts->jffs2) { | 187 | if (opts->jffs2) { |
195 | 188 | ||
196 | /* write cleanmarker */ | 189 | /* write cleanmarker */ |
197 | if (isNAND) { | 190 | if (isNAND) { |
198 | size_t written; | 191 | size_t written; |
199 | result = meminfo->write_oob(meminfo, | 192 | result = meminfo->write_oob(meminfo, |
200 | erase.addr + clmpos, | 193 | erase.addr + clmpos, |
201 | clmlen, | 194 | clmlen, |
202 | &written, | 195 | &written, |
203 | (unsigned char *) | 196 | (unsigned char *) |
204 | &cleanmarker); | 197 | &cleanmarker); |
205 | if (result != 0) { | 198 | if (result != 0) { |
206 | printf("\n%s: MTD writeoob failure: %d\n", | 199 | printf("\n%s: MTD writeoob failure: %d\n", |
207 | mtd_device, result); | 200 | mtd_device, result); |
208 | continue; | 201 | continue; |
209 | } | 202 | } |
210 | } else { | 203 | } else { |
211 | printf("\n%s: this erase routine only supports" | 204 | printf("\n%s: this erase routine only supports" |
212 | " NAND devices!\n", | 205 | " NAND devices!\n", |
213 | mtd_device); | 206 | mtd_device); |
214 | } | 207 | } |
215 | } | 208 | } |
216 | 209 | ||
217 | if (!opts->quiet) { | 210 | if (!opts->quiet) { |
218 | int percent = (int) | 211 | int percent = (int) |
219 | ((unsigned long long) | 212 | ((unsigned long long) |
220 | (erase.addr+meminfo->erasesize-opts->offset) | 213 | (erase.addr+meminfo->erasesize-opts->offset) |
221 | * 100 / erase_length); | 214 | * 100 / erase_length); |
222 | 215 | ||
223 | /* output progress message only at whole percent | 216 | /* output progress message only at whole percent |
224 | * steps to reduce the number of messages printed | 217 | * steps to reduce the number of messages printed |
225 | * on (slow) serial consoles | 218 | * on (slow) serial consoles |
226 | */ | 219 | */ |
227 | if (percent != percent_complete) { | 220 | if (percent != percent_complete) { |
228 | percent_complete = percent; | 221 | percent_complete = percent; |
229 | 222 | ||
230 | printf("\rErasing at 0x%x -- %3d%% complete.", | 223 | printf("\rErasing at 0x%x -- %3d%% complete.", |
231 | erase.addr, percent); | 224 | erase.addr, percent); |
232 | 225 | ||
233 | if (opts->jffs2 && result == 0) | 226 | if (opts->jffs2 && result == 0) |
234 | printf(" Cleanmarker written at 0x%x.", | 227 | printf(" Cleanmarker written at 0x%x.", |
235 | erase.addr); | 228 | erase.addr); |
236 | } | 229 | } |
237 | } | 230 | } |
238 | } | 231 | } |
239 | if (!opts->quiet) | 232 | if (!opts->quiet) |
240 | printf("\n"); | 233 | printf("\n"); |
241 | 234 | ||
242 | if (nand_block_bad_old) { | 235 | if (nand_block_bad_old) { |
243 | struct nand_chip *priv_nand = meminfo->priv; | 236 | struct nand_chip *priv_nand = meminfo->priv; |
244 | 237 | ||
245 | priv_nand->block_bad = nand_block_bad_old; | 238 | priv_nand->block_bad = nand_block_bad_old; |
246 | priv_nand->scan_bbt(meminfo); | 239 | priv_nand->scan_bbt(meminfo); |
247 | } | 240 | } |
248 | 241 | ||
249 | return 0; | 242 | return 0; |
250 | } | 243 | } |
251 | 244 | ||
252 | #define MAX_PAGE_SIZE 2048 | 245 | #define MAX_PAGE_SIZE 2048 |
253 | #define MAX_OOB_SIZE 64 | 246 | #define MAX_OOB_SIZE 64 |
254 | 247 | ||
255 | /* | 248 | /* |
256 | * buffer array used for writing data | 249 | * buffer array used for writing data |
257 | */ | 250 | */ |
258 | static unsigned char data_buf[MAX_PAGE_SIZE]; | 251 | static unsigned char data_buf[MAX_PAGE_SIZE]; |
259 | static unsigned char oob_buf[MAX_OOB_SIZE]; | 252 | static unsigned char oob_buf[MAX_OOB_SIZE]; |
260 | 253 | ||
261 | /* OOB layouts to pass into the kernel as default */ | 254 | /* OOB layouts to pass into the kernel as default */ |
262 | static struct nand_oobinfo none_oobinfo = { | 255 | static struct nand_oobinfo none_oobinfo = { |
263 | .useecc = MTD_NANDECC_OFF, | 256 | .useecc = MTD_NANDECC_OFF, |
264 | }; | 257 | }; |
265 | 258 | ||
266 | static struct nand_oobinfo jffs2_oobinfo = { | 259 | static struct nand_oobinfo jffs2_oobinfo = { |
267 | .useecc = MTD_NANDECC_PLACE, | 260 | .useecc = MTD_NANDECC_PLACE, |
268 | .eccbytes = 6, | 261 | .eccbytes = 6, |
269 | .eccpos = { 0, 1, 2, 3, 6, 7 } | 262 | .eccpos = { 0, 1, 2, 3, 6, 7 } |
270 | }; | 263 | }; |
271 | 264 | ||
272 | static struct nand_oobinfo yaffs_oobinfo = { | 265 | static struct nand_oobinfo yaffs_oobinfo = { |
273 | .useecc = MTD_NANDECC_PLACE, | 266 | .useecc = MTD_NANDECC_PLACE, |
274 | .eccbytes = 6, | 267 | .eccbytes = 6, |
275 | .eccpos = { 8, 9, 10, 13, 14, 15} | 268 | .eccpos = { 8, 9, 10, 13, 14, 15} |
276 | }; | 269 | }; |
277 | 270 | ||
278 | static struct nand_oobinfo autoplace_oobinfo = { | 271 | static struct nand_oobinfo autoplace_oobinfo = { |
279 | .useecc = MTD_NANDECC_AUTOPLACE | 272 | .useecc = MTD_NANDECC_AUTOPLACE |
280 | }; | 273 | }; |
281 | 274 | ||
282 | /** | 275 | /** |
283 | * nand_write_opts: - write image to NAND flash with support for various options | 276 | * nand_write_opts: - write image to NAND flash with support for various options |
284 | * | 277 | * |
285 | * @param meminfo NAND device to erase | 278 | * @param meminfo NAND device to erase |
286 | * @param opts write options (@see nand_write_options) | 279 | * @param opts write options (@see nand_write_options) |
287 | * @return 0 in case of success | 280 | * @return 0 in case of success |
288 | * | 281 | * |
289 | * This code is ported from nandwrite.c from Linux mtd utils by | 282 | * This code is ported from nandwrite.c from Linux mtd utils by |
290 | * Steven J. Hill and Thomas Gleixner. | 283 | * Steven J. Hill and Thomas Gleixner. |
291 | */ | 284 | */ |
292 | int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) | 285 | int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) |
293 | { | 286 | { |
294 | int imglen = 0; | 287 | int imglen = 0; |
295 | int pagelen; | 288 | int pagelen; |
296 | int baderaseblock; | 289 | int baderaseblock; |
297 | int blockstart = -1; | 290 | int blockstart = -1; |
298 | loff_t offs; | 291 | loff_t offs; |
299 | int readlen; | 292 | int readlen; |
300 | int oobinfochanged = 0; | 293 | int oobinfochanged = 0; |
301 | int percent_complete = -1; | 294 | int percent_complete = -1; |
302 | struct nand_oobinfo old_oobinfo; | 295 | struct nand_oobinfo old_oobinfo; |
303 | ulong mtdoffset = opts->offset; | 296 | ulong mtdoffset = opts->offset; |
304 | ulong erasesize_blockalign; | 297 | ulong erasesize_blockalign; |
305 | u_char *buffer = opts->buffer; | 298 | u_char *buffer = opts->buffer; |
306 | size_t written; | 299 | size_t written; |
307 | int result; | 300 | int result; |
308 | 301 | ||
309 | if (opts->pad && opts->writeoob) { | 302 | if (opts->pad && opts->writeoob) { |
310 | printf("Can't pad when oob data is present.\n"); | 303 | printf("Can't pad when oob data is present.\n"); |
311 | return -1; | 304 | return -1; |
312 | } | 305 | } |
313 | 306 | ||
314 | /* set erasesize to specified number of blocks - to match | 307 | /* set erasesize to specified number of blocks - to match |
315 | * jffs2 (virtual) block size */ | 308 | * jffs2 (virtual) block size */ |
316 | if (opts->blockalign == 0) { | 309 | if (opts->blockalign == 0) { |
317 | erasesize_blockalign = meminfo->erasesize; | 310 | erasesize_blockalign = meminfo->erasesize; |
318 | } else { | 311 | } else { |
319 | erasesize_blockalign = meminfo->erasesize * opts->blockalign; | 312 | erasesize_blockalign = meminfo->erasesize * opts->blockalign; |
320 | } | 313 | } |
321 | 314 | ||
322 | /* make sure device page sizes are valid */ | 315 | /* make sure device page sizes are valid */ |
323 | if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) | 316 | if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) |
324 | && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) | 317 | && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) |
325 | && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { | 318 | && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { |
326 | printf("Unknown flash (not normal NAND)\n"); | 319 | printf("Unknown flash (not normal NAND)\n"); |
327 | return -1; | 320 | return -1; |
328 | } | 321 | } |
329 | 322 | ||
330 | /* read the current oob info */ | 323 | /* read the current oob info */ |
331 | memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); | 324 | memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); |
332 | 325 | ||
333 | /* write without ecc? */ | 326 | /* write without ecc? */ |
334 | if (opts->noecc) { | 327 | if (opts->noecc) { |
335 | memcpy(&meminfo->oobinfo, &none_oobinfo, | 328 | memcpy(&meminfo->oobinfo, &none_oobinfo, |
336 | sizeof(meminfo->oobinfo)); | 329 | sizeof(meminfo->oobinfo)); |
337 | oobinfochanged = 1; | 330 | oobinfochanged = 1; |
338 | } | 331 | } |
339 | 332 | ||
340 | /* autoplace ECC? */ | 333 | /* autoplace ECC? */ |
341 | if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { | 334 | if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { |
342 | 335 | ||
343 | memcpy(&meminfo->oobinfo, &autoplace_oobinfo, | 336 | memcpy(&meminfo->oobinfo, &autoplace_oobinfo, |
344 | sizeof(meminfo->oobinfo)); | 337 | sizeof(meminfo->oobinfo)); |
345 | oobinfochanged = 1; | 338 | oobinfochanged = 1; |
346 | } | 339 | } |
347 | 340 | ||
348 | /* force OOB layout for jffs2 or yaffs? */ | 341 | /* force OOB layout for jffs2 or yaffs? */ |
349 | if (opts->forcejffs2 || opts->forceyaffs) { | 342 | if (opts->forcejffs2 || opts->forceyaffs) { |
350 | struct nand_oobinfo *oobsel = | 343 | struct nand_oobinfo *oobsel = |
351 | opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; | 344 | opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; |
352 | 345 | ||
353 | if (meminfo->oobsize == 8) { | 346 | if (meminfo->oobsize == 8) { |
354 | if (opts->forceyaffs) { | 347 | if (opts->forceyaffs) { |
355 | printf("YAFSS cannot operate on " | 348 | printf("YAFSS cannot operate on " |
356 | "256 Byte page size\n"); | 349 | "256 Byte page size\n"); |
357 | goto restoreoob; | 350 | goto restoreoob; |
358 | } | 351 | } |
359 | /* Adjust number of ecc bytes */ | 352 | /* Adjust number of ecc bytes */ |
360 | jffs2_oobinfo.eccbytes = 3; | 353 | jffs2_oobinfo.eccbytes = 3; |
361 | } | 354 | } |
362 | 355 | ||
363 | memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); | 356 | memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); |
364 | } | 357 | } |
365 | 358 | ||
366 | /* get image length */ | 359 | /* get image length */ |
367 | imglen = opts->length; | 360 | imglen = opts->length; |
368 | pagelen = meminfo->oobblock | 361 | pagelen = meminfo->oobblock |
369 | + ((opts->writeoob != 0) ? meminfo->oobsize : 0); | 362 | + ((opts->writeoob != 0) ? meminfo->oobsize : 0); |
370 | 363 | ||
371 | /* check, if file is pagealigned */ | 364 | /* check, if file is pagealigned */ |
372 | if ((!opts->pad) && ((imglen % pagelen) != 0)) { | 365 | if ((!opts->pad) && ((imglen % pagelen) != 0)) { |
373 | printf("Input block length is not page aligned\n"); | 366 | printf("Input block length is not page aligned\n"); |
374 | goto restoreoob; | 367 | goto restoreoob; |
375 | } | 368 | } |
376 | 369 | ||
377 | /* check, if length fits into device */ | 370 | /* check, if length fits into device */ |
378 | if (((imglen / pagelen) * meminfo->oobblock) | 371 | if (((imglen / pagelen) * meminfo->oobblock) |
379 | > (meminfo->size - opts->offset)) { | 372 | > (meminfo->size - opts->offset)) { |
380 | printf("Image %d bytes, NAND page %d bytes, " | 373 | printf("Image %d bytes, NAND page %d bytes, " |
381 | "OOB area %u bytes, device size %u bytes\n", | 374 | "OOB area %u bytes, device size %u bytes\n", |
382 | imglen, pagelen, meminfo->oobblock, meminfo->size); | 375 | imglen, pagelen, meminfo->oobblock, meminfo->size); |
383 | printf("Input block does not fit into device\n"); | 376 | printf("Input block does not fit into device\n"); |
384 | goto restoreoob; | 377 | goto restoreoob; |
385 | } | 378 | } |
386 | 379 | ||
387 | if (!opts->quiet) | 380 | if (!opts->quiet) |
388 | printf("\n"); | 381 | printf("\n"); |
389 | 382 | ||
390 | /* get data from input and write to the device */ | 383 | /* get data from input and write to the device */ |
391 | while (imglen && (mtdoffset < meminfo->size)) { | 384 | while (imglen && (mtdoffset < meminfo->size)) { |
392 | 385 | ||
393 | WATCHDOG_RESET (); | 386 | WATCHDOG_RESET (); |
394 | 387 | ||
395 | /* | 388 | /* |
396 | * new eraseblock, check for bad block(s). Stay in the | 389 | * new eraseblock, check for bad block(s). Stay in the |
397 | * loop to be sure if the offset changes because of | 390 | * loop to be sure if the offset changes because of |
398 | * a bad block, that the next block that will be | 391 | * a bad block, that the next block that will be |
399 | * written to is also checked. Thus avoiding errors if | 392 | * written to is also checked. Thus avoiding errors if |
400 | * the block(s) after the skipped block(s) is also bad | 393 | * the block(s) after the skipped block(s) is also bad |
401 | * (number of blocks depending on the blockalign | 394 | * (number of blocks depending on the blockalign |
402 | */ | 395 | */ |
403 | while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) { | 396 | while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) { |
404 | blockstart = mtdoffset & (~erasesize_blockalign+1); | 397 | blockstart = mtdoffset & (~erasesize_blockalign+1); |
405 | offs = blockstart; | 398 | offs = blockstart; |
406 | baderaseblock = 0; | 399 | baderaseblock = 0; |
407 | 400 | ||
408 | /* check all the blocks in an erase block for | 401 | /* check all the blocks in an erase block for |
409 | * bad blocks */ | 402 | * bad blocks */ |
410 | do { | 403 | do { |
411 | int ret = meminfo->block_isbad(meminfo, offs); | 404 | int ret = meminfo->block_isbad(meminfo, offs); |
412 | 405 | ||
413 | if (ret < 0) { | 406 | if (ret < 0) { |
414 | printf("Bad block check failed\n"); | 407 | printf("Bad block check failed\n"); |
415 | goto restoreoob; | 408 | goto restoreoob; |
416 | } | 409 | } |
417 | if (ret == 1) { | 410 | if (ret == 1) { |
418 | baderaseblock = 1; | 411 | baderaseblock = 1; |
419 | if (!opts->quiet) | 412 | if (!opts->quiet) |
420 | printf("\rBad block at 0x%lx " | 413 | printf("\rBad block at 0x%lx " |
421 | "in erase block from " | 414 | "in erase block from " |
422 | "0x%x will be skipped\n", | 415 | "0x%x will be skipped\n", |
423 | (long) offs, | 416 | (long) offs, |
424 | blockstart); | 417 | blockstart); |
425 | } | 418 | } |
426 | 419 | ||
427 | if (baderaseblock) { | 420 | if (baderaseblock) { |
428 | mtdoffset = blockstart | 421 | mtdoffset = blockstart |
429 | + erasesize_blockalign; | 422 | + erasesize_blockalign; |
430 | } | 423 | } |
431 | offs += erasesize_blockalign | 424 | offs += erasesize_blockalign |
432 | / opts->blockalign; | 425 | / opts->blockalign; |
433 | } while (offs < blockstart + erasesize_blockalign); | 426 | } while (offs < blockstart + erasesize_blockalign); |
434 | } | 427 | } |
435 | 428 | ||
436 | readlen = meminfo->oobblock; | 429 | readlen = meminfo->oobblock; |
437 | if (opts->pad && (imglen < readlen)) { | 430 | if (opts->pad && (imglen < readlen)) { |
438 | readlen = imglen; | 431 | readlen = imglen; |
439 | memset(data_buf + readlen, 0xff, | 432 | memset(data_buf + readlen, 0xff, |
440 | meminfo->oobblock - readlen); | 433 | meminfo->oobblock - readlen); |
441 | } | 434 | } |
442 | 435 | ||
443 | /* read page data from input memory buffer */ | 436 | /* read page data from input memory buffer */ |
444 | memcpy(data_buf, buffer, readlen); | 437 | memcpy(data_buf, buffer, readlen); |
445 | buffer += readlen; | 438 | buffer += readlen; |
446 | 439 | ||
447 | if (opts->writeoob) { | 440 | if (opts->writeoob) { |
448 | /* read OOB data from input memory block, exit | 441 | /* read OOB data from input memory block, exit |
449 | * on failure */ | 442 | * on failure */ |
450 | memcpy(oob_buf, buffer, meminfo->oobsize); | 443 | memcpy(oob_buf, buffer, meminfo->oobsize); |
451 | buffer += meminfo->oobsize; | 444 | buffer += meminfo->oobsize; |
452 | 445 | ||
453 | /* write OOB data first, as ecc will be placed | 446 | /* write OOB data first, as ecc will be placed |
454 | * in there*/ | 447 | * in there*/ |
455 | result = meminfo->write_oob(meminfo, | 448 | result = meminfo->write_oob(meminfo, |
456 | mtdoffset, | 449 | mtdoffset, |
457 | meminfo->oobsize, | 450 | meminfo->oobsize, |
458 | &written, | 451 | &written, |
459 | (unsigned char *) | 452 | (unsigned char *) |
460 | &oob_buf); | 453 | &oob_buf); |
461 | 454 | ||
462 | if (result != 0) { | 455 | if (result != 0) { |
463 | printf("\nMTD writeoob failure: %d\n", | 456 | printf("\nMTD writeoob failure: %d\n", |
464 | result); | 457 | result); |
465 | goto restoreoob; | 458 | goto restoreoob; |
466 | } | 459 | } |
467 | imglen -= meminfo->oobsize; | 460 | imglen -= meminfo->oobsize; |
468 | } | 461 | } |
469 | 462 | ||
470 | /* write out the page data */ | 463 | /* write out the page data */ |
471 | result = meminfo->write(meminfo, | 464 | result = meminfo->write(meminfo, |
472 | mtdoffset, | 465 | mtdoffset, |
473 | meminfo->oobblock, | 466 | meminfo->oobblock, |
474 | &written, | 467 | &written, |
475 | (unsigned char *) &data_buf); | 468 | (unsigned char *) &data_buf); |
476 | 469 | ||
477 | if (result != 0) { | 470 | if (result != 0) { |
478 | printf("writing NAND page at offset 0x%lx failed\n", | 471 | printf("writing NAND page at offset 0x%lx failed\n", |
479 | mtdoffset); | 472 | mtdoffset); |
480 | goto restoreoob; | 473 | goto restoreoob; |
481 | } | 474 | } |
482 | imglen -= readlen; | 475 | imglen -= readlen; |
483 | 476 | ||
484 | if (!opts->quiet) { | 477 | if (!opts->quiet) { |
485 | int percent = (int) | 478 | int percent = (int) |
486 | ((unsigned long long) | 479 | ((unsigned long long) |
487 | (opts->length-imglen) * 100 | 480 | (opts->length-imglen) * 100 |
488 | / opts->length); | 481 | / opts->length); |
489 | /* output progress message only at whole percent | 482 | /* output progress message only at whole percent |
490 | * steps to reduce the number of messages printed | 483 | * steps to reduce the number of messages printed |
491 | * on (slow) serial consoles | 484 | * on (slow) serial consoles |
492 | */ | 485 | */ |
493 | if (percent != percent_complete) { | 486 | if (percent != percent_complete) { |
494 | printf("\rWriting data at 0x%x " | 487 | printf("\rWriting data at 0x%x " |
495 | "-- %3d%% complete.", | 488 | "-- %3d%% complete.", |
496 | mtdoffset, percent); | 489 | mtdoffset, percent); |
497 | percent_complete = percent; | 490 | percent_complete = percent; |
498 | } | 491 | } |
499 | } | 492 | } |
500 | 493 | ||
501 | mtdoffset += meminfo->oobblock; | 494 | mtdoffset += meminfo->oobblock; |
502 | } | 495 | } |
503 | 496 | ||
504 | if (!opts->quiet) | 497 | if (!opts->quiet) |
505 | printf("\n"); | 498 | printf("\n"); |
506 | 499 | ||
507 | restoreoob: | 500 | restoreoob: |
508 | if (oobinfochanged) { | 501 | if (oobinfochanged) { |
509 | memcpy(&meminfo->oobinfo, &old_oobinfo, | 502 | memcpy(&meminfo->oobinfo, &old_oobinfo, |
510 | sizeof(meminfo->oobinfo)); | 503 | sizeof(meminfo->oobinfo)); |
511 | } | 504 | } |
512 | 505 | ||
513 | if (imglen > 0) { | 506 | if (imglen > 0) { |
514 | printf("Data did not fit into device, due to bad blocks\n"); | 507 | printf("Data did not fit into device, due to bad blocks\n"); |
515 | return -1; | 508 | return -1; |
516 | } | 509 | } |
517 | 510 | ||
518 | /* return happy */ | 511 | /* return happy */ |
519 | return 0; | 512 | return 0; |
520 | } | 513 | } |
521 | 514 | ||
522 | /** | 515 | /** |
523 | * nand_read_opts: - read image from NAND flash with support for various options | 516 | * nand_read_opts: - read image from NAND flash with support for various options |
524 | * | 517 | * |
525 | * @param meminfo NAND device to erase | 518 | * @param meminfo NAND device to erase |
526 | * @param opts read options (@see struct nand_read_options) | 519 | * @param opts read options (@see struct nand_read_options) |
527 | * @return 0 in case of success | 520 | * @return 0 in case of success |
528 | * | 521 | * |
529 | */ | 522 | */ |
530 | int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) | 523 | int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) |
531 | { | 524 | { |
532 | int imglen = opts->length; | 525 | int imglen = opts->length; |
533 | int pagelen; | 526 | int pagelen; |
534 | int baderaseblock; | 527 | int baderaseblock; |
535 | int blockstart = -1; | 528 | int blockstart = -1; |
536 | int percent_complete = -1; | 529 | int percent_complete = -1; |
537 | loff_t offs; | 530 | loff_t offs; |
538 | size_t readlen; | 531 | size_t readlen; |
539 | ulong mtdoffset = opts->offset; | 532 | ulong mtdoffset = opts->offset; |
540 | u_char *buffer = opts->buffer; | 533 | u_char *buffer = opts->buffer; |
541 | int result; | 534 | int result; |
542 | 535 | ||
543 | /* make sure device page sizes are valid */ | 536 | /* make sure device page sizes are valid */ |
544 | if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) | 537 | if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) |
545 | && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) | 538 | && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) |
546 | && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { | 539 | && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { |
547 | printf("Unknown flash (not normal NAND)\n"); | 540 | printf("Unknown flash (not normal NAND)\n"); |
548 | return -1; | 541 | return -1; |
549 | } | 542 | } |
550 | 543 | ||
551 | pagelen = meminfo->oobblock | 544 | pagelen = meminfo->oobblock |
552 | + ((opts->readoob != 0) ? meminfo->oobsize : 0); | 545 | + ((opts->readoob != 0) ? meminfo->oobsize : 0); |
553 | 546 | ||
554 | /* check, if length is not larger than device */ | 547 | /* check, if length is not larger than device */ |
555 | if (((imglen / pagelen) * meminfo->oobblock) | 548 | if (((imglen / pagelen) * meminfo->oobblock) |
556 | > (meminfo->size - opts->offset)) { | 549 | > (meminfo->size - opts->offset)) { |
557 | printf("Image %d bytes, NAND page %d bytes, " | 550 | printf("Image %d bytes, NAND page %d bytes, " |
558 | "OOB area %u bytes, device size %u bytes\n", | 551 | "OOB area %u bytes, device size %u bytes\n", |
559 | imglen, pagelen, meminfo->oobblock, meminfo->size); | 552 | imglen, pagelen, meminfo->oobblock, meminfo->size); |
560 | printf("Input block is larger than device\n"); | 553 | printf("Input block is larger than device\n"); |
561 | return -1; | 554 | return -1; |
562 | } | 555 | } |
563 | 556 | ||
564 | if (!opts->quiet) | 557 | if (!opts->quiet) |
565 | printf("\n"); | 558 | printf("\n"); |
566 | 559 | ||
567 | /* get data from input and write to the device */ | 560 | /* get data from input and write to the device */ |
568 | while (imglen && (mtdoffset < meminfo->size)) { | 561 | while (imglen && (mtdoffset < meminfo->size)) { |
569 | 562 | ||
570 | WATCHDOG_RESET (); | 563 | WATCHDOG_RESET (); |
571 | 564 | ||
572 | /* | 565 | /* |
573 | * new eraseblock, check for bad block(s). Stay in the | 566 | * new eraseblock, check for bad block(s). Stay in the |
574 | * loop to be sure if the offset changes because of | 567 | * loop to be sure if the offset changes because of |
575 | * a bad block, that the next block that will be | 568 | * a bad block, that the next block that will be |
576 | * written to is also checked. Thus avoiding errors if | 569 | * written to is also checked. Thus avoiding errors if |
577 | * the block(s) after the skipped block(s) is also bad | 570 | * the block(s) after the skipped block(s) is also bad |
578 | * (number of blocks depending on the blockalign | 571 | * (number of blocks depending on the blockalign |
579 | */ | 572 | */ |
580 | while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) { | 573 | while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) { |
581 | blockstart = mtdoffset & (~meminfo->erasesize+1); | 574 | blockstart = mtdoffset & (~meminfo->erasesize+1); |
582 | offs = blockstart; | 575 | offs = blockstart; |
583 | baderaseblock = 0; | 576 | baderaseblock = 0; |
584 | 577 | ||
585 | /* check all the blocks in an erase block for | 578 | /* check all the blocks in an erase block for |
586 | * bad blocks */ | 579 | * bad blocks */ |
587 | do { | 580 | do { |
588 | int ret = meminfo->block_isbad(meminfo, offs); | 581 | int ret = meminfo->block_isbad(meminfo, offs); |
589 | 582 | ||
590 | if (ret < 0) { | 583 | if (ret < 0) { |
591 | printf("Bad block check failed\n"); | 584 | printf("Bad block check failed\n"); |
592 | return -1; | 585 | return -1; |
593 | } | 586 | } |
594 | if (ret == 1) { | 587 | if (ret == 1) { |
595 | baderaseblock = 1; | 588 | baderaseblock = 1; |
596 | if (!opts->quiet) | 589 | if (!opts->quiet) |
597 | printf("\rBad block at 0x%lx " | 590 | printf("\rBad block at 0x%lx " |
598 | "in erase block from " | 591 | "in erase block from " |
599 | "0x%x will be skipped\n", | 592 | "0x%x will be skipped\n", |
600 | (long) offs, | 593 | (long) offs, |
601 | blockstart); | 594 | blockstart); |
602 | } | 595 | } |
603 | 596 | ||
604 | if (baderaseblock) { | 597 | if (baderaseblock) { |
605 | mtdoffset = blockstart | 598 | mtdoffset = blockstart |
606 | + meminfo->erasesize; | 599 | + meminfo->erasesize; |
607 | } | 600 | } |
608 | offs += meminfo->erasesize; | 601 | offs += meminfo->erasesize; |
609 | 602 | ||
610 | } while (offs < blockstart + meminfo->erasesize); | 603 | } while (offs < blockstart + meminfo->erasesize); |
611 | } | 604 | } |
612 | 605 | ||
613 | 606 | ||
614 | /* read page data to memory buffer */ | 607 | /* read page data to memory buffer */ |
615 | result = meminfo->read(meminfo, | 608 | result = meminfo->read(meminfo, |
616 | mtdoffset, | 609 | mtdoffset, |
617 | meminfo->oobblock, | 610 | meminfo->oobblock, |
618 | &readlen, | 611 | &readlen, |
619 | (unsigned char *) &data_buf); | 612 | (unsigned char *) &data_buf); |
620 | 613 | ||
621 | if (result != 0) { | 614 | if (result != 0) { |
622 | printf("reading NAND page at offset 0x%lx failed\n", | 615 | printf("reading NAND page at offset 0x%lx failed\n", |
623 | mtdoffset); | 616 | mtdoffset); |
624 | return -1; | 617 | return -1; |
625 | } | 618 | } |
626 | 619 | ||
627 | if (imglen < readlen) { | 620 | if (imglen < readlen) { |
628 | readlen = imglen; | 621 | readlen = imglen; |
629 | } | 622 | } |
630 | 623 | ||
631 | memcpy(buffer, data_buf, readlen); | 624 | memcpy(buffer, data_buf, readlen); |
632 | buffer += readlen; | 625 | buffer += readlen; |
633 | imglen -= readlen; | 626 | imglen -= readlen; |
634 | 627 | ||
635 | if (opts->readoob) { | 628 | if (opts->readoob) { |
636 | result = meminfo->read_oob(meminfo, | 629 | result = meminfo->read_oob(meminfo, |
637 | mtdoffset, | 630 | mtdoffset, |
638 | meminfo->oobsize, | 631 | meminfo->oobsize, |
639 | &readlen, | 632 | &readlen, |
640 | (unsigned char *) | 633 | (unsigned char *) |
641 | &oob_buf); | 634 | &oob_buf); |
642 | 635 | ||
643 | if (result != 0) { | 636 | if (result != 0) { |
644 | printf("\nMTD readoob failure: %d\n", | 637 | printf("\nMTD readoob failure: %d\n", |
645 | result); | 638 | result); |
646 | return -1; | 639 | return -1; |
647 | } | 640 | } |
648 | 641 | ||
649 | 642 | ||
650 | if (imglen < readlen) { | 643 | if (imglen < readlen) { |
651 | readlen = imglen; | 644 | readlen = imglen; |
652 | } | 645 | } |
653 | 646 | ||
654 | memcpy(buffer, oob_buf, readlen); | 647 | memcpy(buffer, oob_buf, readlen); |
655 | 648 | ||
656 | buffer += readlen; | 649 | buffer += readlen; |
657 | imglen -= readlen; | 650 | imglen -= readlen; |
658 | } | 651 | } |
659 | 652 | ||
660 | if (!opts->quiet) { | 653 | if (!opts->quiet) { |
661 | int percent = (int) | 654 | int percent = (int) |
662 | ((unsigned long long) | 655 | ((unsigned long long) |
663 | (opts->length-imglen) * 100 | 656 | (opts->length-imglen) * 100 |
664 | / opts->length); | 657 | / opts->length); |
665 | /* output progress message only at whole percent | 658 | /* output progress message only at whole percent |
666 | * steps to reduce the number of messages printed | 659 | * steps to reduce the number of messages printed |
667 | * on (slow) serial consoles | 660 | * on (slow) serial consoles |
668 | */ | 661 | */ |
669 | if (percent != percent_complete) { | 662 | if (percent != percent_complete) { |
670 | if (!opts->quiet) | 663 | if (!opts->quiet) |
671 | printf("\rReading data from 0x%x " | 664 | printf("\rReading data from 0x%x " |
672 | "-- %3d%% complete.", | 665 | "-- %3d%% complete.", |
673 | mtdoffset, percent); | 666 | mtdoffset, percent); |
674 | percent_complete = percent; | 667 | percent_complete = percent; |
675 | } | 668 | } |
676 | } | 669 | } |
677 | 670 | ||
678 | mtdoffset += meminfo->oobblock; | 671 | mtdoffset += meminfo->oobblock; |
679 | } | 672 | } |
680 | 673 | ||
681 | if (!opts->quiet) | 674 | if (!opts->quiet) |
682 | printf("\n"); | 675 | printf("\n"); |
683 | 676 | ||
684 | if (imglen > 0) { | 677 | if (imglen > 0) { |
685 | printf("Could not read entire image due to bad blocks\n"); | 678 | printf("Could not read entire image due to bad blocks\n"); |
686 | return -1; | 679 | return -1; |
687 | } | 680 | } |
688 | 681 | ||
689 | /* return happy */ | 682 | /* return happy */ |
690 | return 0; | 683 | return 0; |
691 | } | 684 | } |
692 | 685 | ||
693 | /****************************************************************************** | 686 | /****************************************************************************** |
694 | * Support for locking / unlocking operations of some NAND devices | 687 | * Support for locking / unlocking operations of some NAND devices |
695 | *****************************************************************************/ | 688 | *****************************************************************************/ |
696 | 689 | ||
697 | #define NAND_CMD_LOCK 0x2a | 690 | #define NAND_CMD_LOCK 0x2a |
698 | #define NAND_CMD_LOCK_TIGHT 0x2c | 691 | #define NAND_CMD_LOCK_TIGHT 0x2c |
699 | #define NAND_CMD_UNLOCK1 0x23 | 692 | #define NAND_CMD_UNLOCK1 0x23 |
700 | #define NAND_CMD_UNLOCK2 0x24 | 693 | #define NAND_CMD_UNLOCK2 0x24 |
701 | #define NAND_CMD_LOCK_STATUS 0x7a | 694 | #define NAND_CMD_LOCK_STATUS 0x7a |
702 | 695 | ||
703 | /** | 696 | /** |
704 | * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT | 697 | * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT |
705 | * state | 698 | * state |
706 | * | 699 | * |
707 | * @param meminfo nand mtd instance | 700 | * @param meminfo nand mtd instance |
708 | * @param tight bring device in lock tight mode | 701 | * @param tight bring device in lock tight mode |
709 | * | 702 | * |
710 | * @return 0 on success, -1 in case of error | 703 | * @return 0 on success, -1 in case of error |
711 | * | 704 | * |
712 | * The lock / lock-tight command only applies to the whole chip. To get some | 705 | * The lock / lock-tight command only applies to the whole chip. To get some |
713 | * parts of the chip lock and others unlocked use the following sequence: | 706 | * parts of the chip lock and others unlocked use the following sequence: |
714 | * | 707 | * |
715 | * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) | 708 | * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) |
716 | * - Call nand_unlock() once for each consecutive area to be unlocked | 709 | * - Call nand_unlock() once for each consecutive area to be unlocked |
717 | * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) | 710 | * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) |
718 | * | 711 | * |
719 | * If the device is in lock-tight state software can't change the | 712 | * If the device is in lock-tight state software can't change the |
720 | * current active lock/unlock state of all pages. nand_lock() / nand_unlock() | 713 | * current active lock/unlock state of all pages. nand_lock() / nand_unlock() |
721 | * calls will fail. It is only posible to leave lock-tight state by | 714 | * calls will fail. It is only posible to leave lock-tight state by |
722 | * an hardware signal (low pulse on _WP pin) or by power down. | 715 | * an hardware signal (low pulse on _WP pin) or by power down. |
723 | */ | 716 | */ |
724 | int nand_lock(nand_info_t *meminfo, int tight) | 717 | int nand_lock(nand_info_t *meminfo, int tight) |
725 | { | 718 | { |
726 | int ret = 0; | 719 | int ret = 0; |
727 | int status; | 720 | int status; |
728 | struct nand_chip *this = meminfo->priv; | 721 | struct nand_chip *this = meminfo->priv; |
729 | 722 | ||
730 | /* select the NAND device */ | 723 | /* select the NAND device */ |
731 | this->select_chip(meminfo, 0); | 724 | this->select_chip(meminfo, 0); |
732 | 725 | ||
733 | this->cmdfunc(meminfo, | 726 | this->cmdfunc(meminfo, |
734 | (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), | 727 | (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), |
735 | -1, -1); | 728 | -1, -1); |
736 | 729 | ||
737 | /* call wait ready function */ | 730 | /* call wait ready function */ |
738 | status = this->waitfunc(meminfo, this, FL_WRITING); | 731 | status = this->waitfunc(meminfo, this, FL_WRITING); |
739 | 732 | ||
740 | /* see if device thinks it succeeded */ | 733 | /* see if device thinks it succeeded */ |
741 | if (status & 0x01) { | 734 | if (status & 0x01) { |
742 | ret = -1; | 735 | ret = -1; |
743 | } | 736 | } |
744 | 737 | ||
745 | /* de-select the NAND device */ | 738 | /* de-select the NAND device */ |
746 | this->select_chip(meminfo, -1); | 739 | this->select_chip(meminfo, -1); |
747 | return ret; | 740 | return ret; |
748 | } | 741 | } |
749 | 742 | ||
750 | /** | 743 | /** |
751 | * nand_get_lock_status: - query current lock state from one page of NAND | 744 | * nand_get_lock_status: - query current lock state from one page of NAND |
752 | * flash | 745 | * flash |
753 | * | 746 | * |
754 | * @param meminfo nand mtd instance | 747 | * @param meminfo nand mtd instance |
755 | * @param offset page address to query (muss be page aligned!) | 748 | * @param offset page address to query (muss be page aligned!) |
756 | * | 749 | * |
757 | * @return -1 in case of error | 750 | * @return -1 in case of error |
758 | * >0 lock status: | 751 | * >0 lock status: |
759 | * bitfield with the following combinations: | 752 | * bitfield with the following combinations: |
760 | * NAND_LOCK_STATUS_TIGHT: page in tight state | 753 | * NAND_LOCK_STATUS_TIGHT: page in tight state |
761 | * NAND_LOCK_STATUS_LOCK: page locked | 754 | * NAND_LOCK_STATUS_LOCK: page locked |
762 | * NAND_LOCK_STATUS_UNLOCK: page unlocked | 755 | * NAND_LOCK_STATUS_UNLOCK: page unlocked |
763 | * | 756 | * |
764 | */ | 757 | */ |
765 | int nand_get_lock_status(nand_info_t *meminfo, ulong offset) | 758 | int nand_get_lock_status(nand_info_t *meminfo, ulong offset) |
766 | { | 759 | { |
767 | int ret = 0; | 760 | int ret = 0; |
768 | int chipnr; | 761 | int chipnr; |
769 | int page; | 762 | int page; |
770 | struct nand_chip *this = meminfo->priv; | 763 | struct nand_chip *this = meminfo->priv; |
771 | 764 | ||
772 | /* select the NAND device */ | 765 | /* select the NAND device */ |
773 | chipnr = (int)(offset >> this->chip_shift); | 766 | chipnr = (int)(offset >> this->chip_shift); |
774 | this->select_chip(meminfo, chipnr); | 767 | this->select_chip(meminfo, chipnr); |
775 | 768 | ||
776 | 769 | ||
777 | if ((offset & (meminfo->oobblock - 1)) != 0) { | 770 | if ((offset & (meminfo->oobblock - 1)) != 0) { |
778 | printf ("nand_get_lock_status: " | 771 | printf ("nand_get_lock_status: " |
779 | "Start address must be beginning of " | 772 | "Start address must be beginning of " |
780 | "nand page!\n"); | 773 | "nand page!\n"); |
781 | ret = -1; | 774 | ret = -1; |
782 | goto out; | 775 | goto out; |
783 | } | 776 | } |
784 | 777 | ||
785 | /* check the Lock Status */ | 778 | /* check the Lock Status */ |
786 | page = (int)(offset >> this->page_shift); | 779 | page = (int)(offset >> this->page_shift); |
787 | this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask); | 780 | this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask); |
788 | 781 | ||
789 | ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT | 782 | ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT |
790 | | NAND_LOCK_STATUS_LOCK | 783 | | NAND_LOCK_STATUS_LOCK |
791 | | NAND_LOCK_STATUS_UNLOCK); | 784 | | NAND_LOCK_STATUS_UNLOCK); |
792 | 785 | ||
793 | out: | 786 | out: |
794 | /* de-select the NAND device */ | 787 | /* de-select the NAND device */ |
795 | this->select_chip(meminfo, -1); | 788 | this->select_chip(meminfo, -1); |
796 | return ret; | 789 | return ret; |
797 | } | 790 | } |
798 | 791 | ||
799 | /** | 792 | /** |
800 | * nand_unlock: - Unlock area of NAND pages | 793 | * nand_unlock: - Unlock area of NAND pages |
801 | * only one consecutive area can be unlocked at one time! | 794 | * only one consecutive area can be unlocked at one time! |
802 | * | 795 | * |
803 | * @param meminfo nand mtd instance | 796 | * @param meminfo nand mtd instance |
804 | * @param start start byte address | 797 | * @param start start byte address |
805 | * @param length number of bytes to unlock (must be a multiple of | 798 | * @param length number of bytes to unlock (must be a multiple of |
806 | * page size nand->oobblock) | 799 | * page size nand->oobblock) |
807 | * | 800 | * |
808 | * @return 0 on success, -1 in case of error | 801 | * @return 0 on success, -1 in case of error |
809 | */ | 802 | */ |
810 | int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) | 803 | int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) |
811 | { | 804 | { |
812 | int ret = 0; | 805 | int ret = 0; |
813 | int chipnr; | 806 | int chipnr; |
814 | int status; | 807 | int status; |
815 | int page; | 808 | int page; |
816 | struct nand_chip *this = meminfo->priv; | 809 | struct nand_chip *this = meminfo->priv; |
817 | printf ("nand_unlock: start: %08x, length: %d!\n", | 810 | printf ("nand_unlock: start: %08x, length: %d!\n", |
818 | (int)start, (int)length); | 811 | (int)start, (int)length); |
819 | 812 | ||
820 | /* select the NAND device */ | 813 | /* select the NAND device */ |
821 | chipnr = (int)(start >> this->chip_shift); | 814 | chipnr = (int)(start >> this->chip_shift); |
822 | this->select_chip(meminfo, chipnr); | 815 | this->select_chip(meminfo, chipnr); |
823 | 816 | ||
824 | /* check the WP bit */ | 817 | /* check the WP bit */ |
825 | this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1); | 818 | this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1); |
826 | if ((this->read_byte(meminfo) & 0x80) == 0) { | 819 | if ((this->read_byte(meminfo) & 0x80) == 0) { |
827 | printf ("nand_unlock: Device is write protected!\n"); | 820 | printf ("nand_unlock: Device is write protected!\n"); |
828 | ret = -1; | 821 | ret = -1; |
829 | goto out; | 822 | goto out; |
830 | } | 823 | } |
831 | 824 | ||
832 | if ((start & (meminfo->oobblock - 1)) != 0) { | 825 | if ((start & (meminfo->oobblock - 1)) != 0) { |
833 | printf ("nand_unlock: Start address must be beginning of " | 826 | printf ("nand_unlock: Start address must be beginning of " |
834 | "nand page!\n"); | 827 | "nand page!\n"); |
835 | ret = -1; | 828 | ret = -1; |
836 | goto out; | 829 | goto out; |
837 | } | 830 | } |
838 | 831 | ||
839 | if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { | 832 | if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { |
840 | printf ("nand_unlock: Length must be a multiple of nand page " | 833 | printf ("nand_unlock: Length must be a multiple of nand page " |
841 | "size!\n"); | 834 | "size!\n"); |
842 | ret = -1; | 835 | ret = -1; |
843 | goto out; | 836 | goto out; |
844 | } | 837 | } |
845 | 838 | ||
846 | /* submit address of first page to unlock */ | 839 | /* submit address of first page to unlock */ |
847 | page = (int)(start >> this->page_shift); | 840 | page = (int)(start >> this->page_shift); |
848 | this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask); | 841 | this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask); |
849 | 842 | ||
850 | /* submit ADDRESS of LAST page to unlock */ | 843 | /* submit ADDRESS of LAST page to unlock */ |
851 | page += (int)(length >> this->page_shift) - 1; | 844 | page += (int)(length >> this->page_shift) - 1; |
852 | this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask); | 845 | this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask); |
853 | 846 | ||
854 | /* call wait ready function */ | 847 | /* call wait ready function */ |
855 | status = this->waitfunc(meminfo, this, FL_WRITING); | 848 | status = this->waitfunc(meminfo, this, FL_WRITING); |
856 | /* see if device thinks it succeeded */ | 849 | /* see if device thinks it succeeded */ |
857 | if (status & 0x01) { | 850 | if (status & 0x01) { |
858 | /* there was an error */ | 851 | /* there was an error */ |
859 | ret = -1; | 852 | ret = -1; |
860 | goto out; | 853 | goto out; |
861 | } | 854 | } |
862 | 855 | ||
863 | out: | 856 | out: |
864 | /* de-select the NAND device */ | 857 | /* de-select the NAND device */ |
865 | this->select_chip(meminfo, -1); | 858 | this->select_chip(meminfo, -1); |
866 | return ret; | 859 | return ret; |
867 | } | 860 | } |
868 | 861 | ||
869 | #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */ | 862 | #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */ |
870 | 863 |
include/arm925t.h
1 | /************************************************ | 1 | /************************************************ |
2 | * NAME : arm925t.h | 2 | * NAME : arm925t.h |
3 | * Version : 23 June 2003 * | 3 | * Version : 23 June 2003 * |
4 | ************************************************/ | 4 | ************************************************/ |
5 | 5 | ||
6 | #ifndef __ARM925T_H__ | 6 | #ifndef __ARM925T_H__ |
7 | #define __ARM925T_H__ | 7 | #define __ARM925T_H__ |
8 | 8 | ||
9 | void gpioreserve(ushort mask); | ||
10 | void gpiosetdir(ushort mask, ushort in); | ||
11 | void gpiosetout(ushort mask, ushort out); | ||
12 | void gpioinit(void); | ||
13 | void archflashwp(void *archdata, int wp); | 9 | void archflashwp(void *archdata, int wp); |
14 | 10 | ||
15 | #endif /*__ARM925T_H__*/ | 11 | #endif /*__ARM925T_H__*/ |
16 | 12 |