summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/cfi_flash.c101
-rw-r--r--drivers/mtd/mtdpart.c14
-rw-r--r--drivers/mtd/nand/mxc_nand.c2
-rw-r--r--drivers/mtd/nand/nand_util.c68
-rw-r--r--drivers/mtd/ubi/build.c8
-rw-r--r--drivers/mtd/ubi/ubi.h4
-rw-r--r--drivers/mtd/ubi/wl.c1
7 files changed, 155 insertions, 43 deletions
diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c
index 60dbb78..cf10b0d 100644
--- a/drivers/mtd/cfi_flash.c
+++ b/drivers/mtd/cfi_flash.c
@@ -210,9 +210,11 @@ unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
static inline void *
flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
{
- unsigned int byte_offset = offset * info->portwidth;
+ unsigned int byte_offset = offset * info->portwidth / info->chipwidth;
+ unsigned int addr = (info->start[sect] + byte_offset);
+ unsigned int mask = 0xffffffff << (info->portwidth - 1);
- return (void *)(info->start[sect] + byte_offset);
+ return (void *)(uintptr_t)(addr & mask);
}
static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
@@ -398,6 +400,8 @@ void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
#endif
flash_write64(cword.ll, addr);
break;
+ default:
+ printf("fwc: Unknown port width %d\n", info->portwidth);
}
/* Ensure all the instructions are fully finished */
@@ -585,7 +589,6 @@ static int flash_status_check (flash_info_t * info, flash_sect_t sector,
prompt, info->start[sector],
flash_read_long (info, sector, 0));
flash_write_cmd (info, sector, 0, info->cmd_reset);
- udelay(1);
return ERR_TIMOUT;
}
udelay (1); /* also triggers watchdog */
@@ -753,12 +756,8 @@ static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
static flash_sect_t find_sector (flash_info_t * info, ulong addr)
{
static flash_sect_t saved_sector; /* previously found sector */
- static flash_info_t *saved_info; /* previously used flash bank */
flash_sect_t sector = saved_sector;
- if ((info != saved_info) || (sector >= info->sector_count))
- sector = 0;
-
while ((info->start[sector] < addr)
&& (sector < info->sector_count - 1))
sector++;
@@ -770,7 +769,6 @@ static flash_sect_t find_sector (flash_info_t * info, ulong addr)
sector--;
saved_sector = sector;
- saved_info = info;
return sector;
}
@@ -787,12 +785,15 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest,
/* Check if Flash is (sufficiently) erased */
switch (info->portwidth) {
case FLASH_CFI_8BIT:
+ debug("%s: 8-bit 0x%02x\n", __func__, cword.c);
flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
break;
case FLASH_CFI_16BIT:
+ debug("%s: 16-bit 0x%04x\n", __func__, cword.w);
flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
break;
case FLASH_CFI_32BIT:
+ debug("%s: 32-bit 0x%08lx\n", __func__, cword.l);
flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
break;
case FLASH_CFI_64BIT:
@@ -1053,6 +1054,8 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
flash_sect_t sect;
int st;
+ debug("%s: erasing sectors %d to %d\n", __func__, s_first, s_last);
+
if (info->flash_id != FLASH_MAN_CFI) {
puts ("Can't erase unknown flash type - aborted\n");
return 1;
@@ -1162,6 +1165,9 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
rcode = 1;
else if (flash_verbose)
putc ('.');
+ } else {
+ debug("\nSector %d is protected.\n",
+ info->protect[sect]);
}
}
@@ -1857,7 +1863,7 @@ static void flash_read_cfi (flash_info_t *info, void *buf,
unsigned int i;
for (i = 0; i < len; i++)
- p[i] = flash_read_uchar(info, start + i);
+ p[i] = flash_read_uchar(info, start + (i * 2));
}
static void __flash_cmd_reset(flash_info_t *info)
@@ -1878,21 +1884,40 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
{
int cfi_offset;
- /* Issue FLASH reset command */
- flash_cmd_reset(info);
-
for (cfi_offset=0;
cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint);
cfi_offset++) {
+ /* Issue FLASH reset command */
+ flash_cmd_reset(info);
flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
FLASH_CMD_CFI);
- if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
- && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
- && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
+ if (flash_isequal(info, 0, FLASH_OFFSET_CFI_RESP, 'Q') &&
+ flash_isequal(info, 0,
+ FLASH_OFFSET_CFI_RESP + 2, 'R') &&
+ flash_isequal(info, 0,
+ FLASH_OFFSET_CFI_RESP + 4, 'Y')) {
flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
sizeof(struct cfi_qry));
+#ifdef CONFIG_SYS_FLASH_INTERFACE_WIDTH
+ info->interface = CONFIG_SYS_FLASH_INTERFACE_WIDTH;
+#else
info->interface = le16_to_cpu(qry->interface_desc);
-
+ /* Some flash chips can support multiple bus widths.
+ * In this case, override the interface width and
+ * limit it to the port width.
+ */
+ if ((info->interface == FLASH_CFI_X8X16) &&
+ (info->portwidth == FLASH_CFI_8BIT)) {
+ debug("Overriding 16-bit interface"
+ " width to 8-bit port width.\n");
+ info->interface = FLASH_CFI_X8;
+ } else if ((info->interface == FLASH_CFI_X16X32) &&
+ (info->portwidth == FLASH_CFI_16BIT)) {
+ debug("Overriding 16-bit interface"
+ " width to 16-bit port width.\n");
+ info->interface = FLASH_CFI_X16;
+ }
+#endif
info->cfi_offset = flash_offset_cfi[cfi_offset];
debug ("device interface is %d\n",
info->interface);
@@ -1903,8 +1928,8 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
/* calculate command offsets as in the Linux driver */
- info->addr_unlock1 = 0x555;
- info->addr_unlock2 = 0x2aa;
+ info->addr_unlock1 = 0xaaa;
+ info->addr_unlock2 = 0x555;
/*
* modify the unlock address if we are
@@ -1938,8 +1963,12 @@ static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
for (info->chipwidth = FLASH_CFI_BY8;
info->chipwidth <= info->portwidth;
info->chipwidth <<= 1)
- if (__flash_detect_cfi(info, qry))
+ if (__flash_detect_cfi(info, qry)) {
+ debug("Found CFI flash, portwidth %d,"
+ " chipwidth %d\n",
+ info->portwidth, info->chipwidth);
return 1;
+ }
}
debug ("not found\n");
return 0;
@@ -1958,7 +1987,7 @@ static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry)
/* CFI < 1.1, try to guess from device id */
if ((info->device_id & 0x80) != 0)
cfi_reverse_geometry(qry);
- } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
+ } else if (flash_read_uchar(info, info->ext_addr + 0x1e) == 3) {
/* CFI >= 1.1, deduct from top/bottom flag */
/* note: ext_addr is valid since cfi_version > 0 */
cfi_reverse_geometry(qry);
@@ -2024,6 +2053,26 @@ static void flash_fixup_sst(flash_info_t *info, struct cfi_qry *qry)
}
}
+static void flash_fixup_num(flash_info_t *info, struct cfi_qry *qry)
+{
+ /*
+ * The M29EW devices seem to report the CFI information wrong
+ * when it's in 8 bit mode.
+ * There's an app note from Numonyx on this issue.
+ * So adjust the buffer size for M29EW while operating in 8-bit mode
+ */
+ if (((qry->max_buf_write_size) > 0x8) &&
+ (info->device_id == 0x7E) &&
+ (info->device_id2 == 0x2201 ||
+ info->device_id2 == 0x2301 ||
+ info->device_id2 == 0x2801 ||
+ info->device_id2 == 0x4801)) {
+ debug("Adjusted buffer size on Numonyx flash"
+ " M29EW family in 8 bit mode\n");
+ qry->max_buf_write_size = 0x8;
+ }
+}
+
/*
* The following code cannot be run from FLASH!
*
@@ -2054,14 +2103,15 @@ ulong flash_get_size (phys_addr_t base, int banknum)
if (flash_detect_cfi (info, &qry)) {
info->vendor = le16_to_cpu(qry.p_id);
- info->ext_addr = le16_to_cpu(qry.p_adr);
+ info->ext_addr = le16_to_cpu(qry.p_adr) * 2;
+ debug("extended address is 0x%x\n", info->ext_addr);
num_erase_regions = qry.num_erase_regions;
if (info->ext_addr) {
info->cfi_version = (ushort) flash_read_uchar (info,
- info->ext_addr + 3) << 8;
+ info->ext_addr + 6) << 8;
info->cfi_version |= (ushort) flash_read_uchar (info,
- info->ext_addr + 4);
+ info->ext_addr + 8);
}
#ifdef DEBUG
@@ -2105,6 +2155,9 @@ ulong flash_get_size (phys_addr_t base, int banknum)
case 0x00bf: /* SST */
flash_fixup_sst(info, &qry);
break;
+ case 0x0089: /* Numonyx */
+ flash_fixup_num(info, &qry);
+ break;
}
debug ("manufacturer is %d\n", info->vendor);
@@ -2112,6 +2165,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)
debug ("device id is 0x%x\n", info->device_id);
debug ("device id2 is 0x%x\n", info->device_id2);
debug ("cfi version is 0x%04x\n", info->cfi_version);
+ debug("port width: %d, chipwidth: %d, interface: %d\n",
+ info->portwidth, info->chipwidth, info->interface);
size_ratio = info->portwidth / info->chipwidth;
/* if the chip is x8/x16 reduce the ratio by half */
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 96dcda2..cbfc679 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -347,16 +347,18 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
if (mtd_mod_by_eb(cur_offset, master) != 0) {
/* Round up to next erasesize */
slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
- printk(KERN_NOTICE "Moving partition %d: "
- "0x%012llx -> 0x%012llx\n", partno,
- (unsigned long long)cur_offset, (unsigned long long)slave->offset);
+ debug("Moving partition %d: 0x%012llx -> 0x%012llx\n",
+ partno, (unsigned long long)cur_offset,
+ (unsigned long long)slave->offset);
}
}
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = master->size - slave->offset;
- printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
- (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
+ debug("0x%012llx-0x%012llx : \"%s\"\n",
+ (unsigned long long)slave->offset,
+ (unsigned long long)(slave->offset + slave->mtd.size),
+ slave->mtd.name);
/* let's do some sanity checks */
if (slave->offset >= master->size) {
@@ -463,7 +465,7 @@ int add_mtd_partitions(struct mtd_info *master,
if (mtd_partitions.next == NULL)
INIT_LIST_HEAD(&mtd_partitions);
- printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
+ debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = add_one_partition(master, parts + i, i, cur_offset);
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 507bbc2..eeba521 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -30,8 +30,6 @@
#define DRIVER_NAME "mxc_nand"
-typedef enum {false, true} bool;
-
struct mxc_nand_host {
struct mtd_info mtd;
struct nand_chip *nand;
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index ff2d348..4727f9c 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -416,11 +416,13 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
* @param nand NAND device
* @param offset offset in flash
* @param length image length
+ * @param used length of flash needed for the requested length
* @return 0 if the image fits and there are no bad blocks
* 1 if the image fits, but there are bad blocks
* -1 if the image does not fit
*/
-static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
+static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length,
+ size_t *used)
{
size_t len_excl_bad = 0;
int ret = 0;
@@ -442,8 +444,13 @@ static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
ret = 1;
offset += block_len;
+ *used += block_len;
}
+ /* If the length is not a multiple of block_len, adjust. */
+ if (len_excl_bad > length)
+ *used -= (len_excl_bad - length);
+
return ret;
}
@@ -476,23 +483,36 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
* block instead as long as the image is short enough to fit even after
- * skipping the bad blocks.
+ * skipping the bad blocks. Due to bad blocks we may not be able to
+ * perform the requested write. In the case where the write would
+ * extend beyond the end of the NAND device, both length and actual (if
+ * not NULL) are set to 0. In the case where the write would extend
+ * beyond the limit we are passed, length is set to 0 and actual is set
+ * to the required length.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length
+ * @param actual set to size required to write length worth of
+ * buffer or 0 on error, if not NULL
+ * @param lim maximum size that actual may be in order to not
+ * exceed the buffer
* @param buffer buffer to read from
* @param flags flags modifying the behaviour of the write to NAND
* @return 0 in case of success
*/
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer, int flags)
+ size_t *actual, loff_t lim, u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
+ size_t used_for_write = 0;
u_char *p_buffer = buffer;
int need_skip;
+ if (actual)
+ *actual = 0;
+
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)
@@ -529,13 +549,23 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
return -EINVAL;
}
- need_skip = check_skip_len(nand, offset, *length);
+ need_skip = check_skip_len(nand, offset, *length, &used_for_write);
+
+ if (actual)
+ *actual = used_for_write;
+
if (need_skip < 0) {
printf("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
+ if (used_for_write > lim) {
+ puts("Size of write exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
if (!need_skip && !(flags & WITH_DROP_FFS)) {
rval = nand_write(nand, offset, length, buffer);
if (rval == 0)
@@ -626,36 +656,58 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
*
* Read image from NAND flash.
* Blocks that are marked bad are skipped and the next block is read
- * instead as long as the image is short enough to fit even after skipping the
- * bad blocks.
+ * instead as long as the image is short enough to fit even after
+ * skipping the bad blocks. Due to bad blocks we may not be able to
+ * perform the requested read. In the case where the read would extend
+ * beyond the end of the NAND device, both length and actual (if not
+ * NULL) are set to 0. In the case where the read would extend beyond
+ * the limit we are passed, length is set to 0 and actual is set to the
+ * required length.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length, on return holds number of read bytes
+ * @param actual set to size required to read length worth of buffer or 0
+ * on error, if not NULL
+ * @param lim maximum size that actual may be in order to not exceed the
+ * buffer
* @param buffer buffer to write to
* @return 0 in case of success
*/
int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer)
+ size_t *actual, loff_t lim, u_char *buffer)
{
int rval;
size_t left_to_read = *length;
+ size_t used_for_read = 0;
u_char *p_buffer = buffer;
int need_skip;
if ((offset & (nand->writesize - 1)) != 0) {
printf("Attempt to read non page-aligned data\n");
*length = 0;
+ if (actual)
+ *actual = 0;
return -EINVAL;
}
- need_skip = check_skip_len(nand, offset, *length);
+ need_skip = check_skip_len(nand, offset, *length, &used_for_read);
+
+ if (actual)
+ *actual = used_for_read;
+
if (need_skip < 0) {
printf("Attempt to read outside the flash area\n");
*length = 0;
return -EINVAL;
}
+ if (used_for_read > lim) {
+ puts("Size of read exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
if (!need_skip) {
rval = nand_read(nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index d144ac2..a708162 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -478,19 +478,19 @@ static int attach_by_scanning(struct ubi_device *ubi)
err = ubi_eba_init_scan(ubi, si);
if (err)
- goto out_wl;
+ goto out_vtbl;
err = ubi_wl_init_scan(ubi, si);
if (err)
- goto out_vtbl;
+ goto out_eba;
ubi_scan_destroy_si(si);
return 0;
+out_eba:
+ ubi_eba_close(ubi);
out_vtbl:
vfree(ubi->vtbl);
-out_wl:
- ubi_wl_close(ubi);
out_si:
ubi_scan_destroy_si(si);
return err;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 14c3a5f..044e849 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -59,7 +59,11 @@
#define UBI_NAME_STR "ubi"
/* Normal UBI messages */
+#ifdef CONFIG_UBI_SILENCE_MSG
+#define ubi_msg(fmt, ...)
+#else
#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__)
+#endif
/* UBI warning messages */
#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \
__func__, ##__VA_ARGS__)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 88b867a..d1ba722 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1538,6 +1538,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
if (ubi->avail_pebs < WL_RESERVED_PEBS) {
ubi_err("no enough physical eraseblocks (%d, need %d)",
ubi->avail_pebs, WL_RESERVED_PEBS);
+ err = -ENOSPC;
goto out_free;
}
ubi->avail_pebs -= WL_RESERVED_PEBS;