From fbf9d876334ba5ef50704fe33543a48a9e6d0440 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 22 May 2013 09:53:39 +0200 Subject: [PATCH 1/6] cfi_flash: add shift option for some cfi memory chips Many cfi chips support 16 and 8 bit modes. Most important difference is use of so called "Q15/A-1" pin. In 16bit mode this pin is used for data IO. In 8bit mode, it is an address input which add one more least significant bit (LSB). In this case we should shift all adresses by one: For example 0xaa << 1 = 0x154 Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 23 +++++++++++++++++++---- drivers/mtd/nor/cfi_flash.h | 4 +++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 0cfac2d03..4b4e29d4a 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -240,10 +240,10 @@ static void flash_read_cfi (struct flash_info *info, void *buf, p[i] = flash_read_uchar(info, start + i); } -static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) +static int flash_detect_width (struct flash_info *info, struct cfi_qry *qry) { int cfi_offset; - debug ("flash detect cfi\n"); + for (info->portwidth = CFG_FLASH_CFI_WIDTH; info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) { @@ -264,8 +264,8 @@ static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) info->cfi_offset=flash_offset_cfi[cfi_offset]; debug ("device interface is %d\n", info->interface); - debug ("found port %d chip %d ", - info->portwidth, info->chipwidth); + debug ("found port %d chip %d chip_lsb %d ", + info->portwidth, info->chipwidth, info->chip_lsb); debug ("port %d bits chip %d bits\n", info->portwidth << CFI_FLASH_SHIFT_WIDTH, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); @@ -278,6 +278,21 @@ static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) return 0; } +static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) +{ + int ret; + + debug ("flash detect cfi\n"); + + info->chip_lsb = 0; + ret = flash_detect_width (info, qry); + if (!ret) { + info->chip_lsb = 1; + ret = flash_detect_width (info, qry); + } + return ret; +} + /* * The following code cannot be run from FLASH! */ diff --git a/drivers/mtd/nor/cfi_flash.h b/drivers/mtd/nor/cfi_flash.h index bcf5c40c7..2a2454c86 100644 --- a/drivers/mtd/nor/cfi_flash.h +++ b/drivers/mtd/nor/cfi_flash.h @@ -57,6 +57,8 @@ struct flash_info { uchar portwidth; /* the width of the port */ uchar chipwidth; /* the width of the chip */ + uchar chip_lsb; /* extra Least Significant Bit in the */ + /* address of chip. */ ushort buffer_size; /* # of bytes in write buffer */ ulong erase_blk_tout; /* maximum block erase timeout */ ulong write_tout; /* maximum write timeout */ @@ -298,7 +300,7 @@ static inline u64 flash_read64(void *addr) */ static inline uchar *flash_make_addr (struct flash_info *info, flash_sect_t sect, uint offset) { - return ((uchar *) (info->start[sect] + (offset * info->portwidth))); + return ((uchar *) (info->start[sect] + ((offset * info->portwidth) << info->chip_lsb))); } uchar flash_read_uchar (struct flash_info *info, uint offset); From 24b9ea6e751686ff3b031a6f5e1437eaddc7aa46 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 22 May 2013 09:53:40 +0200 Subject: [PATCH 2/6] cfi_flash: size_ratio should not be 0 We will get size = 0 if size_ratio = 0 Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 4b4e29d4a..85e96ceed 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -371,7 +371,8 @@ static ulong flash_get_size (struct flash_info *info) size_ratio = info->portwidth / info->chipwidth; /* if the chip is x8/x16 reduce the ratio by half */ if ((info->interface == FLASH_CFI_X8X16) - && (info->chipwidth == FLASH_CFI_BY8)) { + && (info->chipwidth == FLASH_CFI_BY8) + && (size_ratio != 1)) { size_ratio >>= 1; } debug ("size_ratio %d port %d bits chip %d bits\n", From 6d7cd20442f409c3f1b3a9197ac3ee34f692bcd7 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Sun, 26 May 2013 22:17:29 +0200 Subject: [PATCH 3/6] cfi: remove unused field from struct flash_info Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nor/cfi_flash.h b/drivers/mtd/nor/cfi_flash.h index 2a2454c86..5c31d5105 100644 --- a/drivers/mtd/nor/cfi_flash.h +++ b/drivers/mtd/nor/cfi_flash.h @@ -48,7 +48,6 @@ struct cfi_cmd_set; */ struct flash_info { - struct driver_d driver; ulong size; /* total bank size in bytes */ ushort sector_count; /* number of erase units */ ulong flash_id; /* combined device & manufacturer code */ From 736f7119175f5878f18bd6faffb883bd920f0b55 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Sun, 26 May 2013 22:24:07 +0200 Subject: [PATCH 4/6] cfi: Add dev * to flash_info and switch to dev_* Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 55 +++++++++++++++++++------------------ drivers/mtd/nor/cfi_flash.h | 1 + 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 85e96ceed..aaff1839c 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -187,10 +187,10 @@ static ulong flash_read_long (struct flash_info *info, flash_sect_t sect, uint o addr = flash_make_addr (info, sect, offset); #ifdef DEBUG - debug ("long addr is at %p info->portwidth = %d\n", addr, + dev_dbg(info->dev, "long addr is at %p info->portwidth = %d\n", addr, info->portwidth); for (x = 0; x < 4 * info->portwidth; x++) { - debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x)); + dev_dbg(info->dev, "addr[%x] = 0x%x\n", x, flash_read8(addr + x)); } #endif #if defined __LITTLE_ENDIAN @@ -262,11 +262,11 @@ static int flash_detect_width (struct flash_info *info, struct cfi_qry *qry) info->interface = le16_to_cpu(qry->interface_desc); info->cfi_offset=flash_offset_cfi[cfi_offset]; - debug ("device interface is %d\n", + dev_dbg(info->dev, "device interface is %d\n", info->interface); - debug ("found port %d chip %d chip_lsb %d ", + dev_dbg(info->dev, "found port %d chip %d chip_lsb %d ", info->portwidth, info->chipwidth, info->chip_lsb); - debug ("port %d bits chip %d bits\n", + dev_dbg(info->dev, "port %d bits chip %d bits\n", info->portwidth << CFI_FLASH_SHIFT_WIDTH, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); return 1; @@ -274,7 +274,7 @@ static int flash_detect_width (struct flash_info *info, struct cfi_qry *qry) } } } - debug ("not found\n"); + dev_dbg(info->dev, "not found\n"); return 0; } @@ -282,7 +282,7 @@ static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) { int ret; - debug ("flash detect cfi\n"); + dev_dbg(info->dev, "flash detect cfi\n"); info->chip_lsb = 0; ret = flash_detect_width (info, qry); @@ -354,7 +354,7 @@ static ulong flash_get_size (struct flash_info *info) break; #endif default: - printf("unsupported vendor\n"); + dev_err(info->dev, "unsupported vendor\n"); return 0; } info->cfi_cmd_set->flash_read_jedec_ids (info); @@ -362,11 +362,11 @@ static ulong flash_get_size (struct flash_info *info) info->cfi_cmd_set->flash_fixup (info, &qry); - debug ("manufacturer is %d\n", info->vendor); - debug ("manufacturer id is 0x%x\n", info->manufacturer_id); - 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); + dev_dbg(info->dev, "manufacturer is %d\n", info->vendor); + dev_dbg(info->dev, "manufacturer id is 0x%x\n", info->manufacturer_id); + dev_dbg(info->dev, "device id is 0x%x\n", info->device_id); + dev_dbg(info->dev, "device id2 is 0x%x\n", info->device_id2); + dev_dbg(info->dev, "cfi version is 0x%04x\n", info->cfi_version); size_ratio = info->portwidth / info->chipwidth; /* if the chip is x8/x16 reduce the ratio by half */ @@ -375,10 +375,10 @@ static ulong flash_get_size (struct flash_info *info) && (size_ratio != 1)) { size_ratio >>= 1; } - debug ("size_ratio %d port %d bits chip %d bits\n", + dev_dbg(info->dev, "size_ratio %d port %d bits chip %d bits\n", size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - debug ("found %d erase regions\n", num_erase_regions); + dev_dbg(info->dev, "found %d erase regions\n", num_erase_regions); info->eraseregions = xzalloc(sizeof(*(info->eraseregions)) * num_erase_regions); info->numeraseregions = num_erase_regions; sect_cnt = 0; @@ -388,19 +388,19 @@ static ulong flash_get_size (struct flash_info *info) struct mtd_erase_region_info *region = &info->eraseregions[i]; if (i > NUM_ERASE_REGIONS) { - printf ("%d erase regions found, only %d used\n", + dev_info(info->dev, "%d erase regions found, only %d used\n", num_erase_regions, NUM_ERASE_REGIONS); break; } tmp = le32_to_cpu(qry.erase_region_info[i]); - debug("erase region %u: 0x%08lx\n", i, tmp); + dev_dbg(info->dev, "erase region %u: 0x%08lx\n", i, tmp); erase_region_count = (tmp & 0xffff) + 1; tmp >>= 16; erase_region_size = (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; - debug ("erase_region_count = %d erase_region_size = %d\n", + dev_dbg(info->dev, "erase_region_count = %d erase_region_size = %d\n", erase_region_count, erase_region_size); region->offset = cur_offset; @@ -475,7 +475,7 @@ static int cfi_erase(struct flash_info *finfo, size_t count, loff_t offset) unsigned long start, end; int i, ret = 0; - debug("%s: erase 0x%08llx (size %zu)\n", __func__, offset, count); + dev_dbg(finfo->dev, "%s: erase 0x%08llx (size %zu)\n", __func__, offset, count); start = find_sector(finfo, (unsigned long)finfo->base + offset); end = find_sector(finfo, (unsigned long)finfo->base + offset + @@ -802,7 +802,7 @@ int flash_generic_status_check (struct flash_info *info, flash_sect_t sector, start = get_time_ns(); while (info->cfi_cmd_set->flash_is_busy (info, sector)) { if (is_timeout(start, tout)) { - printf ("Flash %s timeout at address %lx data %lx\n", + dev_err(info->dev, "Flash %s timeout at address %lx data %lx\n", prompt, info->start[sector], flash_read_long (info, sector, 0)); flash_write_cmd (info, sector, 0, info->cmd_reset); @@ -839,7 +839,7 @@ void flash_write_cmd(struct flash_info *info, flash_sect_t sect, addr = flash_make_addr (info, sect, offset); flash_make_cmd (info, cmd, &cword); - debug("%s: %p %lX %X => %p " CFI_WORD_FMT "\n", __func__, + dev_dbg(info->dev, "%s: %p %lX %X => %p " CFI_WORD_FMT "\n", __func__, info, sect, offset, addr, cword); flash_write_word(info, cword, addr); @@ -855,15 +855,15 @@ int flash_isequal(struct flash_info *info, flash_sect_t sect, addr = flash_make_addr (info, sect, offset); flash_make_cmd (info, cmd, &cword); - debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr); + dev_dbg(info->dev, "is= cmd %x(%c) addr %p ", cmd, cmd, addr); if (bankwidth_is_1(info)) { - debug ("is= %x %x\n", flash_read8(addr), (u8)cword); + dev_dbg(info->dev, "is= %x %x\n", flash_read8(addr), (u8)cword); retval = (flash_read8(addr) == cword); } else if (bankwidth_is_2(info)) { - debug ("is= %4.4x %4.4x\n", flash_read16(addr), (u16)cword); + dev_dbg(info->dev, "is= %4.4x %4.4x\n", flash_read16(addr), (u16)cword); retval = (flash_read16(addr) == cword); } else if (bankwidth_is_4(info)) { - debug ("is= %8.8x %8.8x\n", flash_read32(addr), (u32)cword); + dev_dbg(info->dev, "is= %8.8x %8.8x\n", flash_read32(addr), (u32)cword); retval = (flash_read32(addr) == cword); } else if (bankwidth_is_8(info)) { #ifdef DEBUG @@ -873,7 +873,7 @@ int flash_isequal(struct flash_info *info, flash_sect_t sect, print_longlong (str1, flash_read32(addr)); print_longlong (str2, cword); - debug ("is= %s %s\n", str1, str2); + dev_dbg(info->dev, "is= %s %s\n", str1, str2); } #endif retval = (flash_read64(addr) == cword); @@ -978,9 +978,10 @@ static int cfi_probe (struct device_d *dev) info->cmd_reset = FLASH_CMD_RESET; info->base = dev_request_mem_region(dev, 0); info->size = flash_get_size(info); + info->dev = dev; if (info->flash_id == FLASH_UNKNOWN) { - printf ("## Unknown FLASH on Bank at 0x%08x - Size = 0x%08lx = %ld MB\n", + dev_warn(dev, "## Unknown FLASH on Bank at 0x%08x - Size = 0x%08lx = %ld MB\n", dev->resource[0].start, info->size, info->size << 20); return -ENODEV; } diff --git a/drivers/mtd/nor/cfi_flash.h b/drivers/mtd/nor/cfi_flash.h index 5c31d5105..9aad5c41f 100644 --- a/drivers/mtd/nor/cfi_flash.h +++ b/drivers/mtd/nor/cfi_flash.h @@ -48,6 +48,7 @@ struct cfi_cmd_set; */ struct flash_info { + struct device_d *dev; ulong size; /* total bank size in bytes */ ushort sector_count; /* number of erase units */ ulong flash_id; /* combined device & manufacturer code */ From ecceaac8e76440565f0ee22060e16826042a1660 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Sun, 26 May 2013 22:26:20 +0200 Subject: [PATCH 5/6] cfi: make hardware device parent of mtd device Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index aaff1839c..7a8ffb638 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -963,6 +963,7 @@ static void cfi_init_mtd(struct flash_info *info) mtd->numeraseregions = info->numeraseregions; mtd->flags = MTD_CAP_NORFLASH; mtd->type = MTD_NORFLASH; + mtd->parent = info->dev; add_mtd_device(mtd, "nor"); } From 0d7ac7c3817e006cc4e258522a989642f1be1538 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Sat, 25 May 2013 00:16:31 +0200 Subject: [PATCH 6/6] mtd: call mtd_erase with complete area if possible If a device does not have bad blocks loop over the eraseblocks in the driver instead of the core. This allows the mtd_dataflash driver to erase blocks instead of pages to gain more speed during erasing. Also the mtd_dataflash driver modifies the erase_info struct which causes the outer loop in the core to never end. Signed-off-by: Sascha Hauer --- drivers/mtd/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index 61744b69d..f3580981f 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -107,6 +107,12 @@ static int mtd_op_erase(struct cdev *cdev, size_t count, loff_t offset) memset(&erase, 0, sizeof(erase)); erase.mtd = mtd; erase.addr = offset; + + if (!mtd->block_isbad) { + erase.len = count; + return mtd_erase(mtd, &erase); + } + erase.len = mtd->erasesize; while (count > 0) {