2 * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
4 * See file CREDITS for list of people who contributed to this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
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
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <linux/stat.h>
31 #include <linux/mtd/mtd-abi.h>
41 struct mtd_info_user info
;
52 static ssize_t
nand_bb_read(struct cdev
*cdev
, void *buf
, size_t count
,
53 unsigned long offset
, ulong flags
)
55 struct nand_bb
*bb
= cdev
->priv
;
56 int ret
, bytes
= 0, now
;
58 debug("%s %d %d\n", __func__
, offset
, count
);
61 ret
= ioctl(bb
->fd
, MEMGETBADBLOCK
, (void *)bb
->offset
);
66 printf("skipping bad block at 0x%08x\n", bb
->offset
);
67 bb
->offset
+= bb
->info
.erasesize
;
71 now
= min(count
, (size_t)(bb
->info
.erasesize
-
72 (bb
->offset
% bb
->info
.erasesize
)));
73 lseek(bb
->fd
, bb
->offset
, SEEK_SET
);
74 ret
= read(bb
->fd
, buf
, now
);
86 /* Must be a multiple of the largest NAND page size */
87 #define BB_WRITEBUF_SIZE 4096
89 static int nand_bb_write_buf(struct nand_bb
*bb
, size_t count
)
92 void *buf
= bb
->writebuf
;
93 int cur_ofs
= bb
->offset
& ~(BB_WRITEBUF_SIZE
- 1);
96 ret
= ioctl(bb
->fd
, MEMGETBADBLOCK
, (void *)cur_ofs
);
101 debug("skipping bad block at 0x%08x\n", cur_ofs
);
102 bb
->offset
+= bb
->info
.erasesize
;
103 cur_ofs
+= bb
->info
.erasesize
;
107 now
= min(count
, (size_t)(bb
->info
.erasesize
));
108 lseek(bb
->fd
, cur_ofs
, SEEK_SET
);
109 ret
= write(bb
->fd
, buf
, now
);
120 static ssize_t
nand_bb_write(struct cdev
*cdev
, const void *buf
, size_t count
,
121 unsigned long offset
, ulong flags
)
123 struct nand_bb
*bb
= cdev
->priv
;
124 int bytes
= count
, now
, wroffs
, ret
;
126 debug("%s offset: 0x%08x count: 0x%08x\n", __func__
, offset
, count
);
129 wroffs
= bb
->offset
% BB_WRITEBUF_SIZE
;
130 now
= min((int)count
, BB_WRITEBUF_SIZE
- wroffs
);
131 memcpy(bb
->writebuf
+ wroffs
, buf
, now
);
133 if (wroffs
+ now
== BB_WRITEBUF_SIZE
) {
135 ret
= nand_bb_write_buf(bb
, BB_WRITEBUF_SIZE
);
150 static int nand_bb_erase(struct cdev
*cdev
, size_t count
, unsigned long offset
)
152 struct nand_bb
*bb
= cdev
->priv
;
155 printf("can only erase from beginning of device\n");
159 return erase(bb
->fd
, bb
->raw_size
, 0);
162 static int nand_bb_open(struct cdev
*cdev
, struct filep
*f
)
164 struct nand_bb
*bb
= cdev
->priv
;
172 bb
->writebuf
= xmalloc(BB_WRITEBUF_SIZE
);
177 static int nand_bb_close(struct cdev
*cdev
, struct filep
*f
)
179 struct nand_bb
*bb
= cdev
->priv
;
182 nand_bb_write_buf(bb
, bb
->offset
% BB_WRITEBUF_SIZE
);
190 static int nand_bb_calc_size(struct nand_bb
*bb
)
195 while (pos
< bb
->raw_size
) {
196 ret
= ioctl(bb
->fd
, MEMGETBADBLOCK
, (void *)pos
);
200 bb
->cdev
.size
+= bb
->info
.erasesize
;
202 pos
+= bb
->info
.erasesize
;
208 static struct file_operations nand_bb_ops
= {
209 .open
= nand_bb_open
,
210 .close
= nand_bb_close
,
211 .read
= nand_bb_read
,
212 .write
= nand_bb_write
,
213 .erase
= nand_bb_erase
,
217 * Add a bad block aware device ontop of another (NAND) device
218 * @param[in] dev The device to add a partition on
219 * @param[in] name Partition name (can be obtained with devinfo command)
220 * @return The device representing the new partition.
222 int dev_add_bb_dev(char *path
, const char *name
)
228 bb
= xzalloc(sizeof(*bb
));
229 bb
->devname
= asprintf("/dev/%s", basename(path
));
231 bb
->cdev
.name
= strdup(name
);
233 bb
->cdev
.name
= asprintf("%s.bb", basename(path
));
235 ret
= stat(bb
->devname
, &s
);
239 bb
->raw_size
= s
.st_size
;
241 bb
->fd
= open(bb
->devname
, O_RDWR
);
247 ret
= ioctl(bb
->fd
, MEMGETINFO
, &bb
->info
);
251 nand_bb_calc_size(bb
);
252 bb
->cdev
.ops
= &nand_bb_ops
;
255 devfs_create(&bb
->cdev
);
264 #define NAND_ADD (1 << 0)
265 #define NAND_DEL (1 << 1)
266 #define NAND_MARKBAD (1 << 2)
268 static int do_nand(cmd_tbl_t
*cmdtp
, int argc
, char *argv
[])
272 int command
= 0, badblock
= 0;
274 while((opt
= getopt(argc
, argv
, "adb:")) > 0) {
276 printf("only one command may be given\n");
288 command
= NAND_MARKBAD
;
289 badblock
= simple_strtoul(optarg
, NULL
, 0);
293 if (command
& NAND_ADD
) {
294 while (optind
< argc
) {
295 if (dev_add_bb_dev(argv
[optind
], NULL
))
302 if (command
& NAND_DEL
) {
303 while (optind
< argc
) {
306 cdev
= cdev_by_name(basename(argv
[optind
]));
308 printf("no such device: %s\n", argv
[optind
]);
319 if (command
& NAND_MARKBAD
) {
323 printf("marking block at 0x%08x on %s as bad\n", badblock
, argv
[optind
]);
325 fd
= open(argv
[optind
], O_RDWR
);
331 ret
= ioctl(fd
, MEMSETBADBLOCK
, (void *)badblock
);
343 static const __maybe_unused
char cmd_nand_help
[] =
344 "Usage: nand [OPTION]...\n"
345 "nand related commands\n"
346 " -a <dev> register a bad block aware device ontop of a normal nand device\n"
347 " -d <dev> deregister a bad block aware device\n"
348 " -b <ofs> <dev> mark block at offset ofs as bad\n";
350 U_BOOT_CMD_START(nand
)
353 U_BOOT_CMD_HELP(cmd_nand_help
)