From 1099bd178ec14380650ffbaf365ca90865e8bc8d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 09:35:47 +0200 Subject: [PATCH 01/13] mtd: cfi-flash: We can print longlongs printf supports printing longlongs, so drop print_longlong. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 0fa41bf67..6d8276413 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -124,19 +124,6 @@ static int flash_write_cfiword (struct flash_info *info, ulong dest, } #ifdef DEBUG -/* - * Debug support - */ -void print_longlong (char *str, unsigned long long data) -{ - int i; - char *cp; - - cp = (unsigned char *) &data; - for (i = 0; i < 8; i++) - sprintf (&str[i * 2], "%2.2x", *cp++); -} - static void flash_printqry (struct cfi_qry *qry) { u8 *p = (u8 *)qry; @@ -867,16 +854,7 @@ int flash_isequal(struct flash_info *info, flash_sect_t sect, 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 - { - char str1[20]; - char str2[20]; - - print_longlong (str1, flash_read32(addr)); - print_longlong (str2, cword); - dev_dbg(info->dev, "is= %s %s\n", str1, str2); - } -#endif + dev_dbg(info->dev, "is= %16.16llx %16.16llx\n", flash_read64(addr), (u64)cword); retval = (flash_read64(addr) == cword); } else retval = 0; From bbc89b57543dcf4d1e42e7aa704cdb642f5a33bd Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 09:39:08 +0200 Subject: [PATCH 02/13] mtd: cfi-flash: save indention level Bail out early in flash_get_size() and save an indention level. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 235 ++++++++++++++++++------------------ 1 file changed, 118 insertions(+), 117 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 6d8276413..01d23d54f 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -312,133 +312,134 @@ static ulong flash_get_size (struct flash_info *info) info->start[0] = base; info->protect = 0; - if (flash_detect_cfi (info, &qry)) { - info->vendor = le16_to_cpu(qry.p_id); - info->ext_addr = le16_to_cpu(qry.p_adr); - num_erase_regions = qry.num_erase_regions; + if (!flash_detect_cfi (info, &qry)) + return 0; - if (info->ext_addr) { - info->cfi_version = (ushort) flash_read_uchar (info, - info->ext_addr + 3) << 8; - info->cfi_version |= (ushort) flash_read_uchar (info, - info->ext_addr + 4); - } + info->vendor = le16_to_cpu(qry.p_id); + info->ext_addr = le16_to_cpu(qry.p_adr); + 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->cfi_version |= (ushort) flash_read_uchar (info, + info->ext_addr + 4); + } #ifdef DEBUG - flash_printqry (&qry); + flash_printqry (&qry); #endif - switch (info->vendor) { + switch (info->vendor) { #ifdef CONFIG_DRIVER_CFI_INTEL - case CFI_CMDSET_INTEL_EXTENDED: - case CFI_CMDSET_INTEL_STANDARD: - info->cfi_cmd_set = &cfi_cmd_set_intel; - break; + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + info->cfi_cmd_set = &cfi_cmd_set_intel; + break; #endif #ifdef CONFIG_DRIVER_CFI_AMD - case CFI_CMDSET_AMD_STANDARD: - case CFI_CMDSET_AMD_EXTENDED: - info->cfi_cmd_set = &cfi_cmd_set_amd; - break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + info->cfi_cmd_set = &cfi_cmd_set_amd; + break; #endif - default: - dev_err(info->dev, "unsupported vendor\n"); - return 0; - } - info->cfi_cmd_set->flash_read_jedec_ids (info); - flash_write_cmd (info, 0, info->cfi_offset, FLASH_CMD_CFI); - - info->cfi_cmd_set->flash_fixup (info, &qry); - - 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 */ - if ((info->interface == FLASH_CFI_X8X16) - && (info->chipwidth == FLASH_CFI_BY8) - && (size_ratio != 1)) { - size_ratio >>= 1; - } - 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); - 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; - sector = base; - - for (i = 0; i < num_erase_regions; i++) { - struct mtd_erase_region_info *region = &info->eraseregions[i]; - - if (i > NUM_ERASE_REGIONS) { - 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]); - 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; - dev_dbg(info->dev, "erase_region_count = %d erase_region_size = %d\n", - erase_region_count, erase_region_size); - - region->offset = cur_offset; - region->erasesize = erase_region_size * size_ratio; - region->numblocks = erase_region_count; - cur_offset += erase_region_size * size_ratio * erase_region_count; - - /* increase the space malloced for the sector start addresses */ - info->start = xrealloc(info->start, sizeof(ulong) * (erase_region_count + sect_cnt)); - info->protect = xrealloc(info->protect, sizeof(uchar) * (erase_region_count + sect_cnt)); - - for (j = 0; j < erase_region_count; j++) { - info->start[sect_cnt] = sector; - sector += (erase_region_size * size_ratio); - - /* - * Only read protection status from supported devices (intel...) - */ - switch (info->vendor) { - case CFI_CMDSET_INTEL_EXTENDED: - case CFI_CMDSET_INTEL_STANDARD: - info->protect[sect_cnt] = - flash_isset (info, sect_cnt, - FLASH_OFFSET_PROTECT, - FLASH_STATUS_PROTECT); - break; - default: - info->protect[sect_cnt] = 0; /* default: not protected */ - } - - sect_cnt++; - } - } - - info->sector_count = sect_cnt; - /* multiply the size by the number of chips */ - info->size = (1 << qry.dev_size) * size_ratio; - info->buffer_size = (1 << le16_to_cpu(qry.max_buf_write_size)); - info->erase_blk_tout = 1 << (qry.block_erase_timeout_typ + - qry.block_erase_timeout_max); - info->buffer_write_tout = 1 << (qry.buf_write_timeout_typ + - qry.buf_write_timeout_max); - info->write_tout = 1 << (qry.word_write_timeout_typ + - qry.word_write_timeout_max); - info->flash_id = FLASH_MAN_CFI; - if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) { - info->portwidth >>= 1; /* XXX - Need to test on x8/x16 in parallel. */ - } - flash_write_cmd (info, 0, 0, info->cmd_reset); + default: + dev_err(info->dev, "unsupported vendor\n"); + return 0; } + info->cfi_cmd_set->flash_read_jedec_ids (info); + flash_write_cmd (info, 0, info->cfi_offset, FLASH_CMD_CFI); + + info->cfi_cmd_set->flash_fixup (info, &qry); + + 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 */ + if ((info->interface == FLASH_CFI_X8X16) + && (info->chipwidth == FLASH_CFI_BY8) + && (size_ratio != 1)) { + size_ratio >>= 1; + } + 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); + 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; + sector = base; + + for (i = 0; i < num_erase_regions; i++) { + struct mtd_erase_region_info *region = &info->eraseregions[i]; + + if (i > NUM_ERASE_REGIONS) { + 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]); + 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; + dev_dbg(info->dev, "erase_region_count = %d erase_region_size = %d\n", + erase_region_count, erase_region_size); + + region->offset = cur_offset; + region->erasesize = erase_region_size * size_ratio; + region->numblocks = erase_region_count; + cur_offset += erase_region_size * size_ratio * erase_region_count; + + /* increase the space malloced for the sector start addresses */ + info->start = xrealloc(info->start, sizeof(ulong) * (erase_region_count + sect_cnt)); + info->protect = xrealloc(info->protect, sizeof(uchar) * (erase_region_count + sect_cnt)); + + for (j = 0; j < erase_region_count; j++) { + info->start[sect_cnt] = sector; + sector += (erase_region_size * size_ratio); + + /* + * Only read protection status from supported devices (intel...) + */ + switch (info->vendor) { + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + info->protect[sect_cnt] = + flash_isset (info, sect_cnt, + FLASH_OFFSET_PROTECT, + FLASH_STATUS_PROTECT); + break; + default: + info->protect[sect_cnt] = 0; /* default: not protected */ + } + + sect_cnt++; + } + } + + info->sector_count = sect_cnt; + /* multiply the size by the number of chips */ + info->size = (1 << qry.dev_size) * size_ratio; + info->buffer_size = (1 << le16_to_cpu(qry.max_buf_write_size)); + info->erase_blk_tout = 1 << (qry.block_erase_timeout_typ + + qry.block_erase_timeout_max); + info->buffer_write_tout = 1 << (qry.buf_write_timeout_typ + + qry.buf_write_timeout_max); + info->write_tout = 1 << (qry.word_write_timeout_typ + + qry.word_write_timeout_max); + info->flash_id = FLASH_MAN_CFI; + if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) + info->portwidth >>= 1; /* XXX - Need to test on x8/x16 in parallel. */ + + flash_write_cmd (info, 0, 0, info->cmd_reset); return info->size; } From 2ec3921248b2eb5031107b1680ef748e0bef64f5 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 09:40:50 +0200 Subject: [PATCH 03/13] mtd: cfi-flash: statically initialize instead of memset Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 01d23d54f..caa93b689 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -295,11 +295,9 @@ static ulong flash_get_size (struct flash_info *info) int erase_region_size; int erase_region_count; int cur_offset = 0; - struct cfi_qry qry; + struct cfi_qry qry = {}; unsigned long base = (unsigned long)info->base; - memset(&qry, 0, sizeof(qry)); - info->ext_addr = 0; info->cfi_version = 0; #ifdef CFG_FLASH_PROTECTION From 25b42d1fe7cd9d5847294dc320536d591b5f34ef Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 09:42:45 +0200 Subject: [PATCH 04/13] mtd: cfi-flash: replace ifdef with IS_ENABLED TO get rid of some ifdefs. While at it add the vendor code of the unsupported vendor to the error message. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index caa93b689..98ce7914e 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -329,22 +329,23 @@ static ulong flash_get_size (struct flash_info *info) #endif switch (info->vendor) { -#ifdef CONFIG_DRIVER_CFI_INTEL case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: - info->cfi_cmd_set = &cfi_cmd_set_intel; + if (IS_ENABLED(CONFIG_DRIVER_CFI_INTEL)) + info->cfi_cmd_set = &cfi_cmd_set_intel; break; -#endif -#ifdef CONFIG_DRIVER_CFI_AMD case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: - info->cfi_cmd_set = &cfi_cmd_set_amd; + if (IS_ENABLED(CONFIG_DRIVER_CFI_AMD)) + info->cfi_cmd_set = &cfi_cmd_set_amd; break; -#endif - default: - dev_err(info->dev, "unsupported vendor\n"); + } + + if (!info->cfi_cmd_set) { + dev_err(info->dev, "unsupported vendor 0x%04x\n", info->vendor); return 0; } + info->cfi_cmd_set->flash_read_jedec_ids (info); flash_write_cmd (info, 0, info->cfi_offset, FLASH_CMD_CFI); From 20a096120abf8bfcbaf5d5975ca9369b840b9c85 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 09:44:35 +0200 Subject: [PATCH 05/13] mtd: cfi-flash: remove unnecessary ifdefs Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 98ce7914e..762a6c6de 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -168,19 +168,15 @@ static ulong flash_read_long (struct flash_info *info, flash_sect_t sect, uint o { uchar *addr; ulong retval; - -#ifdef DEBUG int x; -#endif + addr = flash_make_addr (info, sect, offset); -#ifdef DEBUG dev_dbg(info->dev, "long addr is at %p info->portwidth = %d\n", addr, info->portwidth); - for (x = 0; x < 4 * info->portwidth; x++) { + for (x = 0; x < 4 * info->portwidth; x++) dev_dbg(info->dev, "addr[%x] = 0x%x\n", x, flash_read8(addr + x)); - } -#endif + #if defined __LITTLE_ENDIAN retval = ((flash_read8(addr) << 16) | (flash_read8(addr + info->portwidth) << 24) | From 0203b33d14b704e5ac9cd7735c4e8be304cdd961 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 10:00:24 +0200 Subject: [PATCH 06/13] mtd: cfi-flash: make flash_detect_width more readable Use variables instead of long defines to get the loops into a single line. Also use goto to move the deeply indented code more to the left. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 61 +++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 762a6c6de..a369b46e9 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -224,42 +224,49 @@ static void flash_read_cfi (struct flash_info *info, void *buf, p[i] = flash_read_uchar(info, start + i); } -static int flash_detect_width (struct flash_info *info, struct cfi_qry *qry) +static int flash_detect_width(struct flash_info *info, struct cfi_qry *qry) { int cfi_offset; + int pw, cw; + for (pw = CFG_FLASH_CFI_WIDTH; pw <= FLASH_CFI_64BIT; pw <<= 1) { + for (cw = FLASH_CFI_BY8; cw <= pw; cw <<= 1) { + info->chipwidth = cw; + info->portwidth = pw; - for (info->portwidth = CFG_FLASH_CFI_WIDTH; - info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) { - for (info->chipwidth = FLASH_CFI_BY8; - info->chipwidth <= info->portwidth; - info->chipwidth <<= 1) { - flash_write_cmd (info, 0, 0, AMD_CMD_RESET); - flash_write_cmd (info, 0, 0, FLASH_CMD_RESET); - for (cfi_offset=0; cfi_offset < sizeof(flash_offset_cfi)/sizeof(uint); cfi_offset++) { - 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')) { - flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP, - sizeof(struct cfi_qry)); - info->interface = le16_to_cpu(qry->interface_desc); + flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); - info->cfi_offset=flash_offset_cfi[cfi_offset]; - dev_dbg(info->dev, "device interface is %d\n", - info->interface); - dev_dbg(info->dev, "found port %d chip %d chip_lsb %d ", - info->portwidth, info->chipwidth, info->chip_lsb); - 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; - } + for (cfi_offset = 0; cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint); + cfi_offset++) { + + 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')) + goto found; } } } - dev_dbg(info->dev, "not found\n"); + + dev_dbg(info->dev, "no flash found\n"); + return 0; + +found: + flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP, + sizeof(struct cfi_qry)); + info->interface = le16_to_cpu(qry->interface_desc); + info->cfi_offset=flash_offset_cfi[cfi_offset]; + dev_dbg(info->dev, "device interface is %d\n", info->interface); + dev_dbg(info->dev, "found port %d chip %d chip_lsb %d ", + info->portwidth, info->chipwidth, info->chip_lsb); + 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; } static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) From 81d8fa83b48d1abfe405b3e4927c35d7974362eb Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 10:07:26 +0200 Subject: [PATCH 07/13] mtd: cfi-flash: return 0 for success Use zero as success return code and negative standard error codes consistently in the driver. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 55 +++++++++++++++---------------- drivers/mtd/nor/cfi_flash.h | 13 -------- drivers/mtd/nor/cfi_flash_intel.c | 14 ++++---- 3 files changed, 33 insertions(+), 49 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index a369b46e9..4165c88df 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -110,10 +110,10 @@ static int flash_write_cfiword (struct flash_info *info, ulong dest, else if (bankwidth_is_8(info)) flag = ((flash_read64(dstaddr) & cword) == cword); else - return 2; + return -EIO; if (!flag) - return 2; + return -EIO; info->cfi_cmd_set->flash_prepare_write(info); @@ -252,7 +252,7 @@ static int flash_detect_width(struct flash_info *info, struct cfi_qry *qry) dev_dbg(info->dev, "no flash found\n"); - return 0; + return -ENODEV; found: flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP, @@ -266,10 +266,10 @@ found: info->portwidth << CFI_FLASH_SHIFT_WIDTH, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - return 1; + return 0; } -static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) +static int flash_detect_cfi(struct flash_info *info, struct cfi_qry *qry) { int ret; @@ -277,19 +277,19 @@ static int flash_detect_cfi (struct flash_info *info, struct cfi_qry *qry) info->chip_lsb = 0; ret = flash_detect_width (info, qry); - if (!ret) { - info->chip_lsb = 1; - ret = flash_detect_width (info, qry); - } - return ret; + if (!ret) + return 0; + + info->chip_lsb = 1; + return flash_detect_width (info, qry); } /* * The following code cannot be run from FLASH! */ -static ulong flash_get_size (struct flash_info *info) +static int flash_detect_size(struct flash_info *info) { - int i, j; + int i, j, ret; flash_sect_t sect_cnt; unsigned long sector; unsigned long tmp; @@ -313,8 +313,9 @@ static ulong flash_get_size (struct flash_info *info) info->start[0] = base; info->protect = 0; - if (!flash_detect_cfi (info, &qry)) - return 0; + ret = flash_detect_cfi(info, &qry); + if (ret) + return ret; info->vendor = le16_to_cpu(qry.p_id); info->ext_addr = le16_to_cpu(qry.p_adr); @@ -346,7 +347,7 @@ static ulong flash_get_size (struct flash_info *info) if (!info->cfi_cmd_set) { dev_err(info->dev, "unsupported vendor 0x%04x\n", info->vendor); - return 0; + return -ENOSYS; } info->cfi_cmd_set->flash_read_jedec_ids (info); @@ -443,7 +444,7 @@ static ulong flash_get_size (struct flash_info *info) flash_write_cmd (info, 0, 0, info->cmd_reset); - return info->size; + return 0; } /* loop through the sectors from the highest address @@ -486,13 +487,7 @@ out: return ret; } -/* - * Copy memory to flash, returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - */ -static int write_buff (struct flash_info *info, const uchar * src, ulong addr, ulong cnt) +static int write_buff(struct flash_info *info, const uchar * src, ulong addr, ulong cnt) { ulong wp; uchar *p; @@ -548,8 +543,11 @@ static int write_buff (struct flash_info *info, const uchar * src, ulong addr, u i = buffered_size - (wp % buffered_size); if (i > cnt) i = cnt; - if ((rc = info->cfi_cmd_set->flash_write_cfibuffer (info, wp, src, i)) != ERR_OK) + + rc = info->cfi_cmd_set->flash_write_cfibuffer(info, wp, src, i); + if (rc) return rc; + i -= i & (info->portwidth - 1); wp += i; src += i; @@ -797,11 +795,11 @@ int flash_generic_status_check (struct flash_info *info, flash_sect_t sector, prompt, info->start[sector], flash_read_long (info, sector, 0)); flash_write_cmd (info, sector, 0, info->cmd_reset); - return ERR_TIMOUT; + return -ETIMEDOUT; } udelay (1); /* also triggers watchdog */ } - return ERR_OK; + return 0; } /* @@ -962,6 +960,7 @@ static void cfi_init_mtd(struct flash_info *info) static int cfi_probe (struct device_d *dev) { struct flash_info *info = xzalloc(sizeof(*info)); + int ret; dev->priv = (void *)info; @@ -973,9 +972,9 @@ static int cfi_probe (struct device_d *dev) return PTR_ERR(info->base); info->dev = dev; - info->size = flash_get_size(info); - if (info->flash_id == FLASH_UNKNOWN) { + ret = flash_detect_size(info); + if (ret) { 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 aeaf751ab..8f33bdfae 100644 --- a/drivers/mtd/nor/cfi_flash.h +++ b/drivers/mtd/nor/cfi_flash.h @@ -363,19 +363,6 @@ extern void flash_read_user_serial(struct flash_info *info, void * buffer, int o extern void flash_read_factory_serial(struct flash_info *info, void * buffer, int offset, int len); #endif /* CFG_FLASH_PROTECTION */ -/*----------------------------------------------------------------------- - * return codes from flash_write(): - */ -#define ERR_OK 0 -#define ERR_TIMOUT 1 -#define ERR_NOT_ERASED 2 -#define ERR_PROTECTED 4 -#define ERR_INVAL 8 -#define ERR_ALIGN 16 -#define ERR_UNKNOWN_FLASH_VENDOR 32 -#define ERR_UNKNOWN_FLASH_TYPE 64 -#define ERR_PROG_ERROR 128 - /*----------------------------------------------------------------------- * Protection Flags for flash_protect(): */ diff --git a/drivers/mtd/nor/cfi_flash_intel.c b/drivers/mtd/nor/cfi_flash_intel.c index 32e581a39..dcf8d9751 100644 --- a/drivers/mtd/nor/cfi_flash_intel.c +++ b/drivers/mtd/nor/cfi_flash_intel.c @@ -67,7 +67,7 @@ static int intel_flash_write_cfibuffer (struct flash_info *info, ulong dest, con retcode = flash_generic_status_check (info, sector, info->buffer_write_tout, "write to buffer"); - if (retcode != ERR_OK) + if (retcode) return retcode; /* reduce the number of loops by the width of the port */ @@ -110,24 +110,22 @@ static int intel_flash_status_check (struct flash_info *info, flash_sect_t secto { int retcode; - retcode = flash_generic_status_check (info, sector, tout, prompt); - - if ((retcode == ERR_OK) - && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) { - retcode = ERR_INVAL; + retcode = flash_generic_status_check(info, sector, tout, prompt); + if (!retcode && !flash_isequal(info, sector, 0, FLASH_STATUS_DONE)) { + retcode = -EINVAL; printf ("Flash %s error at address %lx\n", prompt, info->start[sector]); if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) { puts ("Command Sequence Error.\n"); } else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) { puts ("Block Erase Error.\n"); - retcode = ERR_NOT_ERASED; + retcode = -EIO; } else if (flash_isset (info, sector, 0, FLASH_STATUS_PSLBS)) { puts ("Locking Error\n"); } if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) { puts ("Block locked.\n"); - retcode = ERR_PROTECTED; + retcode = -EROFS; } if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS)) puts ("Vpp Low Error.\n"); From 9c48ac000b795e0d5fe3ca000d0786b9552b167e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 10:17:28 +0200 Subject: [PATCH 08/13] mtd: cfi-flash: turn some messages into vdbg Some messages produce a lot of log spam. Turn them into dev_vdbg to make some more important messages more visible. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 12 ++++++------ drivers/mtd/nor/cfi_flash.h | 12 ++++-------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 4165c88df..eb167493c 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -828,7 +828,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); - dev_dbg(info->dev, "%s: %p %lX %X => %p " CFI_WORD_FMT "\n", __func__, + dev_vdbg(info->dev, "%s: %p %lX %X => %p " CFI_WORD_FMT "\n", __func__, info, sect, offset, addr, cword); flash_write_word(info, cword, addr); @@ -844,18 +844,18 @@ int flash_isequal(struct flash_info *info, flash_sect_t sect, addr = flash_make_addr (info, sect, offset); flash_make_cmd (info, cmd, &cword); - dev_dbg(info->dev, "is= cmd %x(%c) addr %p ", cmd, cmd, addr); + dev_vdbg(info->dev, "is= cmd %x(%c) addr %p ", cmd, cmd, addr); if (bankwidth_is_1(info)) { - dev_dbg(info->dev, "is= %x %x\n", flash_read8(addr), (u8)cword); + dev_vdbg(info->dev, "is= %x %x\n", flash_read8(addr), (u8)cword); retval = (flash_read8(addr) == cword); } else if (bankwidth_is_2(info)) { - dev_dbg(info->dev, "is= %4.4x %4.4x\n", flash_read16(addr), (u16)cword); + dev_vdbg(info->dev, "is= %4.4x %4.4x\n", flash_read16(addr), (u16)cword); retval = (flash_read16(addr) == cword); } else if (bankwidth_is_4(info)) { - dev_dbg(info->dev, "is= %8.8x %8.8x\n", flash_read32(addr), (u32)cword); + dev_vdbg(info->dev, "is= %8.8x %8.8x\n", flash_read32(addr), (u32)cword); retval = (flash_read32(addr) == cword); } else if (bankwidth_is_8(info)) { - dev_dbg(info->dev, "is= %16.16llx %16.16llx\n", flash_read64(addr), (u64)cword); + dev_vdbg(info->dev, "is= %16.16llx %16.16llx\n", flash_read64(addr), (u64)cword); retval = (flash_read64(addr) == cword); } else retval = 0; diff --git a/drivers/mtd/nor/cfi_flash.h b/drivers/mtd/nor/cfi_flash.h index 8f33bdfae..25d2e5887 100644 --- a/drivers/mtd/nor/cfi_flash.h +++ b/drivers/mtd/nor/cfi_flash.h @@ -332,18 +332,14 @@ u32 jedec_read_mfr(struct flash_info *info); static inline void flash_write_word(struct flash_info *info, cfiword_t datum, void *addr) { - if (bankwidth_is_1(info)) { - debug("fw addr %p val %02x\n", addr, (u8)datum); + if (bankwidth_is_1(info)) flash_write8(datum, addr); - } else if (bankwidth_is_2(info)) { - debug("fw addr %p val %04x\n", addr, (u16)datum); + else if (bankwidth_is_2(info)) flash_write16(datum, addr); - } else if (bankwidth_is_4(info)) { - debug("fw addr %p val %08x\n", addr, (u32)datum); + else if (bankwidth_is_4(info)) flash_write32(datum, addr); - } else if (bankwidth_is_8(info)) { + else if (bankwidth_is_8(info)) flash_write64(datum, addr); - } } extern void flash_print_info (struct flash_info *); From 6be93f2fcee7282c7c484e89e6cafd09e0fa515c Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 11:14:42 +0200 Subject: [PATCH 09/13] mtd: cfi-flash: use unaligned accessor functions Some members in struct cfi_qry are unaligned. Use get_unaligned_* to access them. Fixes unaligned aborts on busses which don't support unaligned accesses. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index eb167493c..021f3b9f9 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "cfi_flash.h" /* @@ -257,8 +258,8 @@ static int flash_detect_width(struct flash_info *info, struct cfi_qry *qry) found: flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP, sizeof(struct cfi_qry)); - info->interface = le16_to_cpu(qry->interface_desc); - info->cfi_offset=flash_offset_cfi[cfi_offset]; + info->interface = get_unaligned_le16(&qry->interface_desc); + info->cfi_offset = flash_offset_cfi[cfi_offset]; dev_dbg(info->dev, "device interface is %d\n", info->interface); dev_dbg(info->dev, "found port %d chip %d chip_lsb %d ", info->portwidth, info->chipwidth, info->chip_lsb); @@ -317,8 +318,8 @@ static int flash_detect_size(struct flash_info *info) if (ret) return ret; - info->vendor = le16_to_cpu(qry.p_id); - info->ext_addr = le16_to_cpu(qry.p_adr); + info->vendor = get_unaligned_le16(&qry.p_id); + info->ext_addr = get_unaligned_le16(&qry.p_adr); num_erase_regions = qry.num_erase_regions; if (info->ext_addr) { @@ -386,7 +387,7 @@ static int flash_detect_size(struct flash_info *info) break; } - tmp = le32_to_cpu(qry.erase_region_info[i]); + tmp = get_unaligned_le32(&qry.erase_region_info[i]); dev_dbg(info->dev, "erase region %u: 0x%08lx\n", i, tmp); erase_region_count = (tmp & 0xffff) + 1; @@ -431,7 +432,7 @@ static int flash_detect_size(struct flash_info *info) info->sector_count = sect_cnt; /* multiply the size by the number of chips */ info->size = (1 << qry.dev_size) * size_ratio; - info->buffer_size = (1 << le16_to_cpu(qry.max_buf_write_size)); + info->buffer_size = (1 << get_unaligned_le16(&qry.max_buf_write_size)); info->erase_blk_tout = 1 << (qry.block_erase_timeout_typ + qry.block_erase_timeout_max); info->buffer_write_tout = 1 << (qry.buf_write_timeout_typ + From 9746f02caed759f9ec0fcf4c9e8417d86af5a796 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 22 Jun 2015 10:08:27 +0200 Subject: [PATCH 10/13] mtd: cfi-flash: Coding style cleanup - use consistent variable types (drop uchar, ushort and friends) - remove whitespace between functions and opening brace - Add some blank lines to rectify code - drop 'rc' and 'retcode' and use 'ret' consistently - Do not put variable assignment into if() - drop unncessary braces Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 278 +++++++++++++++++------------- drivers/mtd/nor/cfi_flash.h | 109 ++++++------ drivers/mtd/nor/cfi_flash_amd.c | 41 ++--- drivers/mtd/nor/cfi_flash_intel.c | 111 ++++++------ 4 files changed, 287 insertions(+), 252 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index 021f3b9f9..b93c663b8 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -59,7 +59,9 @@ * */ -static uint flash_offset_cfi[2]={FLASH_OFFSET_CFI,FLASH_OFFSET_CFI_ALT}; +static unsigned int flash_offset_cfi[2] = { + FLASH_OFFSET_CFI,FLASH_OFFSET_CFI_ALT +}; /* * Check if chip width is defined. If not, start detecting with 8bit. @@ -73,7 +75,7 @@ static uint flash_offset_cfi[2]={FLASH_OFFSET_CFI,FLASH_OFFSET_CFI_ALT}; * Functions */ -static void flash_add_byte (struct flash_info *info, cfiword_t * cword, uchar c) +static void flash_add_byte(struct flash_info *info, cfiword_t *cword, u8 c) { if (bankwidth_is_1(info)) { *cword = c; @@ -95,7 +97,7 @@ static void flash_add_byte (struct flash_info *info, cfiword_t * cword, uchar c) #endif } -static int flash_write_cfiword (struct flash_info *info, ulong dest, +static int flash_write_cfiword(struct flash_info *info, unsigned long dest, cfiword_t cword) { void *dstaddr = (void *)dest; @@ -120,12 +122,12 @@ static int flash_write_cfiword (struct flash_info *info, ulong dest, flash_write_word(info, cword, (void *)dest); - return flash_status_check (info, find_sector (info, dest), + return flash_status_check(info, find_sector (info, dest), info->write_tout, "write"); } #ifdef DEBUG -static void flash_printqry (struct cfi_qry *qry) +static void flash_printqry(struct cfi_qry *qry) { u8 *p = (u8 *)qry; int x, y; @@ -151,9 +153,9 @@ static void flash_printqry (struct cfi_qry *qry) /* * read a character at a port width address */ -uchar flash_read_uchar (struct flash_info *info, uint offset) +u8 flash_read_uchar(struct flash_info *info, unsigned int offset) { - uchar *cp = flash_make_addr(info, 0, offset); + u8 *cp = flash_make_addr(info, 0, offset); #if defined __LITTLE_ENDIAN return flash_read8(cp); #else @@ -165,10 +167,11 @@ uchar flash_read_uchar (struct flash_info *info, uint offset) * read a long word by picking the least significant byte of each maximum * port size word. Swap for ppc format. */ -static ulong flash_read_long (struct flash_info *info, flash_sect_t sect, uint offset) +static unsigned long flash_read_long(struct flash_info *info, flash_sect_t sect, + unsigned int offset) { - uchar *addr; - ulong retval; + u8 *addr; + unsigned long retval; int x; addr = flash_make_addr (info, sect, offset); @@ -200,14 +203,14 @@ static ulong flash_read_long (struct flash_info *info, flash_sect_t sect, uint o u32 jedec_read_mfr(struct flash_info *info) { int bank = 0; - uchar mfr; + u8 mfr; /* According to JEDEC "Standard Manufacturer's Identification Code" * (http://www.jedec.org/download/search/jep106W.pdf) * several first banks can contain 0x7f instead of actual ID */ do { - mfr = flash_read_uchar (info, + mfr = flash_read_uchar(info, (bank << 8) | FLASH_OFFSET_MANUFACTURER_ID); bank++; } while (mfr == FLASH_ID_CONTINUATION); @@ -215,7 +218,7 @@ u32 jedec_read_mfr(struct flash_info *info) return mfr; } -static void flash_read_cfi (struct flash_info *info, void *buf, +static void flash_read_cfi(struct flash_info *info, void *buf, unsigned int start, size_t len) { u8 *p = buf; @@ -238,8 +241,9 @@ static int flash_detect_width(struct flash_info *info, struct cfi_qry *qry) flash_write_cmd(info, 0, 0, AMD_CMD_RESET); flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); - for (cfi_offset = 0; cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint); - cfi_offset++) { + for (cfi_offset = 0; + cfi_offset < sizeof(flash_offset_cfi) / sizeof(unsigned int); + cfi_offset++) { flash_write_cmd(info, 0, flash_offset_cfi[cfi_offset], FLASH_CMD_CFI); @@ -295,7 +299,7 @@ static int flash_detect_size(struct flash_info *info) unsigned long sector; unsigned long tmp; int size_ratio; - uchar num_erase_regions; + int num_erase_regions; int erase_region_size; int erase_region_count; int cur_offset = 0; @@ -309,7 +313,7 @@ static int flash_detect_size(struct flash_info *info) #endif /* first only malloc space for the first sector */ - info->start = xmalloc(sizeof(ulong)); + info->start = xmalloc(sizeof(*info->eraseregions)); info->start[0] = base; info->protect = 0; @@ -323,14 +327,14 @@ static int flash_detect_size(struct flash_info *info) num_erase_regions = qry.num_erase_regions; if (info->ext_addr) { - info->cfi_version = (ushort) flash_read_uchar (info, + info->cfi_version = (u16)flash_read_uchar (info, info->ext_addr + 3) << 8; - info->cfi_version |= (ushort) flash_read_uchar (info, + info->cfi_version |= (u16)flash_read_uchar (info, info->ext_addr + 4); } #ifdef DEBUG - flash_printqry (&qry); + flash_printqry(&qry); #endif switch (info->vendor) { @@ -352,7 +356,7 @@ static int flash_detect_size(struct flash_info *info) } info->cfi_cmd_set->flash_read_jedec_ids (info); - flash_write_cmd (info, 0, info->cfi_offset, FLASH_CMD_CFI); + flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI); info->cfi_cmd_set->flash_fixup (info, &qry); @@ -363,17 +367,20 @@ static int flash_detect_size(struct flash_info *info) 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 */ if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8) && (size_ratio != 1)) { size_ratio >>= 1; } + 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); dev_dbg(info->dev, "found %d erase regions\n", num_erase_regions); - info->eraseregions = xzalloc(sizeof(*(info->eraseregions)) * num_erase_regions); + + info->eraseregions = xzalloc(sizeof(*info->eraseregions) * num_erase_regions); info->numeraseregions = num_erase_regions; sect_cnt = 0; sector = base; @@ -403,8 +410,10 @@ static int flash_detect_size(struct flash_info *info) cur_offset += erase_region_size * size_ratio * erase_region_count; /* increase the space malloced for the sector start addresses */ - info->start = xrealloc(info->start, sizeof(ulong) * (erase_region_count + sect_cnt)); - info->protect = xrealloc(info->protect, sizeof(uchar) * (erase_region_count + sect_cnt)); + info->start = xrealloc(info->start, sizeof(*info->eraseregions) * + (erase_region_count + sect_cnt)); + info->protect = xrealloc(info->protect, sizeof(*info->eraseregions) * + (erase_region_count + sect_cnt)); for (j = 0; j < erase_region_count; j++) { info->start[sect_cnt] = sector; @@ -440,10 +449,11 @@ static int flash_detect_size(struct flash_info *info) info->write_tout = 1 << (qry.word_write_timeout_typ + qry.word_write_timeout_max); info->flash_id = FLASH_MAN_CFI; + if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) info->portwidth >>= 1; /* XXX - Need to test on x8/x16 in parallel. */ - flash_write_cmd (info, 0, 0, info->cmd_reset); + flash_write_cmd(info, 0, 0, info->cmd_reset); return 0; } @@ -452,7 +462,7 @@ static int flash_detect_size(struct flash_info *info) * when the passed address is greater or equal to the sector address * we have a match */ -flash_sect_t find_sector (struct flash_info *info, ulong addr) +flash_sect_t find_sector(struct flash_info *info, unsigned long addr) { flash_sect_t sector; @@ -460,6 +470,7 @@ flash_sect_t find_sector (struct flash_info *info, ulong addr) if (addr >= info->start[sector]) break; } + return sector; } @@ -488,37 +499,40 @@ out: return ret; } -static int write_buff(struct flash_info *info, const uchar * src, ulong addr, ulong cnt) +static int write_buff(struct flash_info *info, const u8 *src, + unsigned long addr, unsigned long cnt) { - ulong wp; - uchar *p; + unsigned long wp; + u8 *p; int aln; cfiword_t cword; - int i, rc; + int i, ret; #ifdef CONFIG_CFI_BUFFER_WRITE int buffered_size; #endif /* get lower aligned address */ - wp = (addr & ~(info->portwidth - 1)); + wp = addr & ~(info->portwidth - 1); /* handle unaligned start */ - if ((aln = addr - wp) != 0) { + aln = addr - wp; + if (aln) { cword = 0; - p = (uchar*)wp; + p = (u8 *)wp; for (i = 0; i < aln; ++i) - flash_add_byte (info, &cword, flash_read8(p + i)); + flash_add_byte(info, &cword, flash_read8(p + i)); for (; (i < info->portwidth) && (cnt > 0); i++) { - flash_add_byte (info, &cword, *src++); + flash_add_byte(info, &cword, *src++); cnt--; } - for (; (cnt == 0) && (i < info->portwidth); ++i) - flash_add_byte (info, &cword, flash_read8(p + i)); - rc = flash_write_cfiword (info, wp, cword); - if (rc != 0) - return rc; + for (; (cnt == 0) && (i < info->portwidth); ++i) + flash_add_byte(info, &cword, flash_read8(p + i)); + + ret = flash_write_cfiword(info, wp, cword); + if (ret) + return ret; wp += i; } @@ -531,12 +545,17 @@ static int write_buff(struct flash_info *info, const uchar * src, ulong addr, ul /* prohibit buffer write when buffer_size is 1 */ if (info->buffer_size == 1) { cword = 0; + for (i = 0; i < info->portwidth; i++) - flash_add_byte (info, &cword, *src++); - if ((rc = flash_write_cfiword (info, wp, cword)) != 0) - return rc; + flash_add_byte(info, &cword, *src++); + + ret = flash_write_cfiword(info, wp, cword); + if (ret) + return ret; + wp += info->portwidth; cnt -= info->portwidth; + continue; } @@ -545,9 +564,9 @@ static int write_buff(struct flash_info *info, const uchar * src, ulong addr, ul if (i > cnt) i = cnt; - rc = info->cfi_cmd_set->flash_write_cfibuffer(info, wp, src, i); - if (rc) - return rc; + ret = info->cfi_cmd_set->flash_write_cfibuffer(info, wp, src, i); + if (ret) + return ret; i -= i & (info->portwidth - 1); wp += i; @@ -557,64 +576,67 @@ static int write_buff(struct flash_info *info, const uchar * src, ulong addr, ul #else while (cnt >= info->portwidth) { cword = 0; - for (i = 0; i < info->portwidth; i++) { - flash_add_byte (info, &cword, *src++); - } - if ((rc = flash_write_cfiword (info, wp, cword)) != 0) - return rc; + + for (i = 0; i < info->portwidth; i++) + flash_add_byte(info, &cword, *src++); + + ret = flash_write_cfiword(info, wp, cword); + if (ret) + return ret; + wp += info->portwidth; cnt -= info->portwidth; } #endif /* CONFIG_CFI_BUFFER_WRITE */ - if (cnt == 0) { + if (cnt == 0) return 0; - } /* * handle unaligned tail bytes */ cword = 0; - p = (uchar*)wp; + p = (u8 *)wp; + for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) { - flash_add_byte (info, &cword, *src++); + flash_add_byte(info, &cword, *src++); --cnt; } - for (; i < info->portwidth; ++i) { - flash_add_byte (info, &cword, flash_read8(p + i)); - } - return flash_write_cfiword (info, wp, cword); + for (; i < info->portwidth; ++i) + flash_add_byte(info, &cword, flash_read8(p + i)); + + return flash_write_cfiword(info, wp, cword); } -static int flash_real_protect (struct flash_info *info, long sector, int prot) +static int flash_real_protect(struct flash_info *info, long sector, int prot) { - int retcode = 0; + int ret; - retcode = info->cfi_cmd_set->flash_real_protect(info, sector, prot); + ret = info->cfi_cmd_set->flash_real_protect(info, sector, prot); + if (ret) + return ret; - if (retcode) - return retcode; + ret = flash_status_check(info, sector, info->erase_blk_tout, + prot ? "protect" : "unprotect"); + if (ret) + return ret; - if ((retcode = - flash_status_check (info, sector, info->erase_blk_tout, - prot ? "protect" : "unprotect")) == 0) { + info->protect[sector] = prot; - info->protect[sector] = prot; + /* + * On some of Intel's flash chips (marked via legacy_unlock) + * unprotect unprotects all locking. + */ + if (prot == 0 && info->legacy_unlock) { + flash_sect_t i; - /* - * On some of Intel's flash chips (marked via legacy_unlock) - * unprotect unprotects all locking. - */ - if ((prot == 0) && (info->legacy_unlock)) { - flash_sect_t i; - - for (i = 0; i < info->sector_count; i++) { - if (info->protect[i]) - flash_real_protect (info, i, 1); - } + for (i = 0; i < info->sector_count; i++) { + if (info->protect[i]) + flash_real_protect (info, i, 1); } } - return retcode; + + return 0; } static int cfi_mtd_protect(struct flash_info *finfo, loff_t offset, size_t len, int prot) @@ -626,12 +648,12 @@ static int cfi_mtd_protect(struct flash_info *finfo, loff_t offset, size_t len, end = find_sector(finfo, (unsigned long)finfo->base + offset + len - 1); for (i = start; i <= end; i++) { - ret = flash_real_protect (finfo, i, prot); + ret = flash_real_protect(finfo, i, prot); if (ret) - goto out; + return ret; } -out: - return ret; + + return 0; } static int cfi_mtd_lock(struct mtd_info *mtd, loff_t offset, size_t len) @@ -654,50 +676,53 @@ static void cfi_info (struct device_d* dev) int i; if (info->flash_id != FLASH_MAN_CFI) { - puts ("missing or unknown FLASH type\n"); + printf("missing or unknown FLASH type\n"); return; } - printf ("CFI conformant FLASH (%d x %d)", + printf("CFI conformant FLASH (%d x %d)", (info->portwidth << 3), (info->chipwidth << 3)); - printf (" Size: %ld MB in %d Sectors\n", + printf(" Size: %ld MB in %d Sectors\n ", info->size >> 20, info->sector_count); - printf (" "); + switch (info->vendor) { case CFI_CMDSET_INTEL_STANDARD: - printf ("Intel Standard"); + printf("Intel Standard"); break; case CFI_CMDSET_INTEL_EXTENDED: - printf ("Intel Extended"); + printf("Intel Extended"); break; case CFI_CMDSET_AMD_STANDARD: - printf ("AMD Standard"); + printf("AMD Standard"); break; case CFI_CMDSET_AMD_EXTENDED: - printf ("AMD Extended"); + printf("AMD Extended"); break; default: - printf ("Unknown (%d)", info->vendor); + printf("Unknown (%d)", info->vendor); break; } - printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x%02X", + printf(" command set, Manufacturer ID: 0x%02X, Device ID: 0x%02X", info->manufacturer_id, info->device_id); - if (info->device_id == 0x7E) { + + if (info->device_id == 0x7E) printf("%04X", info->device_id2); - } - printf ("\n Erase timeout: %ld ms, write timeout: %ld us\n", + + printf("\n Erase timeout: %ld ms, write timeout: %ld us\n", info->erase_blk_tout, info->write_tout); + if (info->buffer_size > 1) { - printf (" Buffer write timeout: %ld us, buffer size: %d bytes\n", + printf(" Buffer write timeout: %ld us, buffer size: %d bytes\n", info->buffer_write_tout, info->buffer_size); } - puts ("\n Sector Start Addresses:"); + printf("\n Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { if ((i % 5) == 0) - printf ("\n"); + printf("\n"); #ifdef CFG_FLASH_EMPTY_INFO { int k; @@ -713,7 +738,7 @@ static void cfi_info (struct device_d* dev) else size = info->start[0] + info->size - info->start[i]; erased = 1; - flash = (volatile unsigned long *) info->start[i]; + flash = (volatile unsigned long *)info->start[i]; size = size >> 2; /* divide by 4 for longword access */ for (k = 0; k < size; k++) { if (*flash++ != 0xffffffff) { @@ -723,13 +748,13 @@ static void cfi_info (struct device_d* dev) } /* print empty and read-only info */ - printf (" %08lX %c %s ", + printf(" %08lX %c %s ", info->start[i], erased ? 'E' : ' ', info->protect[i] ? "RO" : " "); } #else /* ! CFG_FLASH_EMPTY_INFO */ - printf (" %08lX %s ", + printf(" %08lX %s ", info->start[i], info->protect[i] ? "RO" : " "); #endif @@ -771,8 +796,8 @@ static void flash_read_factory_serial (struct flash_info *info, void *buffer, in #endif -int flash_status_check (struct flash_info *info, flash_sect_t sector, - uint64_t tout, char *prompt) +int flash_status_check(struct flash_info *info, flash_sect_t sector, + u64 tout, char *prompt) { return info->cfi_cmd_set->flash_status_check(info, sector, tout, prompt); } @@ -781,10 +806,10 @@ int flash_status_check (struct flash_info *info, flash_sect_t sector, * wait for XSR.7 to be set. Time out with an error if it does not. * This routine does not set the flash to read-array mode. */ -int flash_generic_status_check (struct flash_info *info, flash_sect_t sector, - uint64_t tout, char *prompt) +int flash_generic_status_check(struct flash_info *info, flash_sect_t sector, + u64 tout, char *prompt) { - uint64_t start; + u64 start; tout *= 1000000; @@ -795,11 +820,14 @@ int flash_generic_status_check (struct flash_info *info, flash_sect_t sector, 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); + + flash_write_cmd(info, sector, 0, info->cmd_reset); + return -ETIMEDOUT; } - udelay (1); /* also triggers watchdog */ + udelay(1); /* also triggers watchdog */ } + return 0; } @@ -820,10 +848,9 @@ void flash_make_cmd(struct flash_info *info, u32 cmd, cfiword_t *cmdbuf) * Write a proper sized command to the correct address */ void flash_write_cmd(struct flash_info *info, flash_sect_t sect, - uint offset, u32 cmd) + unsigned int offset, u32 cmd) { - - uchar *addr; + u8 *addr; cfiword_t cword; addr = flash_make_addr (info, sect, offset); @@ -836,7 +863,7 @@ void flash_write_cmd(struct flash_info *info, flash_sect_t sect, } int flash_isequal(struct flash_info *info, flash_sect_t sect, - uint offset, u32 cmd) + unsigned int offset, u32 cmd) { void *addr; cfiword_t cword; @@ -846,6 +873,7 @@ int flash_isequal(struct flash_info *info, flash_sect_t sect, flash_make_cmd (info, cmd, &cword); dev_vdbg(info->dev, "is= cmd %x(%c) addr %p ", cmd, cmd, addr); + if (bankwidth_is_1(info)) { dev_vdbg(info->dev, "is= %x %x\n", flash_read8(addr), (u8)cword); retval = (flash_read8(addr) == cword); @@ -858,14 +886,15 @@ int flash_isequal(struct flash_info *info, flash_sect_t sect, } else if (bankwidth_is_8(info)) { dev_vdbg(info->dev, "is= %16.16llx %16.16llx\n", flash_read64(addr), (u64)cword); retval = (flash_read64(addr) == cword); - } else + } else { retval = 0; + } return retval; } int flash_isset(struct flash_info *info, flash_sect_t sect, - uint offset, u32 cmd) + unsigned int offset, u32 cmd) { void *addr = flash_make_addr (info, sect, offset); cfiword_t cword; @@ -880,14 +909,15 @@ int flash_isset(struct flash_info *info, flash_sect_t sect, retval = ((flash_read32(addr) & cword) == cword); } else if (bankwidth_is_8(info)) { retval = ((flash_read64(addr) & cword) == cword); - } else + } else { retval = 0; + } return retval; } static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) + size_t *retlen, u8 *buf) { struct flash_info *info = container_of(mtd, struct flash_info, mtd); @@ -898,7 +928,7 @@ static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, } static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) + size_t *retlen, const u8 *buf) { struct flash_info *info = container_of(mtd, struct flash_info, mtd); int ret; @@ -915,7 +945,6 @@ static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) int ret; ret = cfi_erase(info, instr->len, instr->addr); - if (ret) { instr->state = MTD_ERASE_FAILED; return -EIO; @@ -930,7 +959,7 @@ static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) static void cfi_init_mtd(struct flash_info *info) { struct mtd_info *mtd = &info->mtd; - u_int32_t erasesize; + u32 erasesize; int i; mtd->read = cfi_mtd_read; @@ -941,10 +970,11 @@ static void cfi_init_mtd(struct flash_info *info) mtd->size = info->size; erasesize = 0; - for (i=0; i < info->numeraseregions; i++) { + for (i = 0; i < info->numeraseregions; i++) { if (erasesize < info->eraseregions[i].erasesize) erasesize = info->eraseregions[i].erasesize; } + mtd->erasesize = erasesize; mtd->writesize = 1; @@ -958,12 +988,12 @@ static void cfi_init_mtd(struct flash_info *info) add_mtd_device(mtd, "nor", DEVICE_ID_DYNAMIC); } -static int cfi_probe (struct device_d *dev) +static int cfi_probe(struct device_d *dev) { struct flash_info *info = xzalloc(sizeof(*info)); int ret; - dev->priv = (void *)info; + dev->priv = info; /* Init: no FLASHes known */ info->flash_id = FLASH_UNKNOWN; diff --git a/drivers/mtd/nor/cfi_flash.h b/drivers/mtd/nor/cfi_flash.h index 25d2e5887..6d975b94e 100644 --- a/drivers/mtd/nor/cfi_flash.h +++ b/drivers/mtd/nor/cfi_flash.h @@ -49,32 +49,32 @@ 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 */ - ulong *start; /* physical sector start addresses */ - uchar *protect; /* sector protection status */ + unsigned long size; /* total bank size in bytes */ + unsigned int sector_count; /* number of erase units */ + unsigned long flash_id; /* combined device & manufacturer code */ + unsigned long *start; /* physical sector start addresses */ + unsigned char *protect; /* sector protection status */ - uchar portwidth; /* the width of the port */ - uchar chipwidth; /* the width of the chip */ - uchar chip_lsb; /* extra Least Significant Bit in the */ + unsigned int portwidth; /* the width of the port */ + unsigned int chipwidth; /* the width of the chip */ + unsigned int 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 */ - ulong buffer_write_tout; /* maximum buffer write timeout */ - ushort vendor; /* the primary vendor id */ - ushort cmd_reset; /* vendor specific reset command */ - ushort interface; /* used for x8/x16 adjustments */ - ushort legacy_unlock; /* support Intel legacy (un)locking */ - uchar manufacturer_id; /* manufacturer id */ - ushort device_id; /* device id */ - ushort device_id2; /* extended device id */ - ushort ext_addr; /* extended query table address */ - ushort cfi_version; /* cfi version */ - ushort cfi_offset; /* offset for cfi query */ - ulong addr_unlock1; /* unlock address 1 for AMD flash roms */ - ulong addr_unlock2; /* unlock address 2 for AMD flash roms */ + unsigned int buffer_size; /* # of bytes in write buffer */ + unsigned long erase_blk_tout; /* maximum block erase timeout */ + unsigned long write_tout; /* maximum write timeout */ + unsigned long buffer_write_tout;/* maximum buffer write timeout */ + unsigned int vendor; /* the primary vendor id */ + unsigned int cmd_reset; /* vendor specific reset command */ + unsigned int interface; /* used for x8/x16 adjustments */ + unsigned int legacy_unlock; /* support Intel legacy (un)locking */ + unsigned int manufacturer_id; /* manufacturer id */ + unsigned int device_id; /* device id */ + unsigned int device_id2; /* extended device id */ + unsigned int ext_addr; /* extended query table address */ + unsigned int cfi_version; /* cfi version */ + unsigned int cfi_offset; /* offset for cfi query */ + unsigned long addr_unlock1; /* unlock address 1 for AMD flash roms */ + unsigned long addr_unlock2; /* unlock address 2 for AMD flash roms */ struct cfi_cmd_set *cfi_cmd_set; struct mtd_info mtd; int numeraseregions; @@ -118,14 +118,16 @@ struct cfi_pri_hdr { struct cfi_cmd_set { - int (*flash_write_cfibuffer) (struct flash_info *info, ulong dest, const uchar * cp, int len); - int (*flash_erase_one) (struct flash_info *info, long sect); - int (*flash_is_busy) (struct flash_info *info, flash_sect_t sect); - void (*flash_read_jedec_ids) (struct flash_info *info); - void (*flash_prepare_write) (struct flash_info *info); - int (*flash_status_check) (struct flash_info *info, flash_sect_t sector, uint64_t tout, char *prompt); - int (*flash_real_protect) (struct flash_info *info, long sector, int prot); - void (*flash_fixup) (struct flash_info *info, struct cfi_qry *qry); + int (*flash_write_cfibuffer)(struct flash_info *info, unsigned long dest, + const u8 *cp, int len); + int (*flash_erase_one)(struct flash_info *info, long sect); + int (*flash_is_busy)(struct flash_info *info, flash_sect_t sect); + void (*flash_read_jedec_ids)(struct flash_info *info); + void (*flash_prepare_write)(struct flash_info *info); + int (*flash_status_check)(struct flash_info *info, flash_sect_t sector, + u64 tout, char *prompt); + int (*flash_real_protect)(struct flash_info *info, long sector, int prot); + void (*flash_fixup)(struct flash_info *info, struct cfi_qry *qry); }; extern struct cfi_cmd_set cfi_cmd_set_intel; @@ -241,17 +243,17 @@ extern struct cfi_cmd_set cfi_cmd_set_amd; /* Prototypes */ int flash_isset(struct flash_info *info, flash_sect_t sect, - uint offset, u32 cmd); + unsigned int offset, u32 cmd); void flash_write_cmd(struct flash_info *info, flash_sect_t sect, - uint offset, u32 cmd); -flash_sect_t find_sector (struct flash_info *info, ulong addr); -int flash_status_check (struct flash_info *info, flash_sect_t sector, - uint64_t tout, char *prompt); -int flash_generic_status_check (struct flash_info *info, flash_sect_t sector, - uint64_t tout, char *prompt); + unsigned int offset, u32 cmd); +flash_sect_t find_sector(struct flash_info *info, unsigned long addr); +int flash_status_check(struct flash_info *info, flash_sect_t sector, + u64 tout, char *prompt); +int flash_generic_status_check(struct flash_info *info, flash_sect_t sector, + u64 tout, char *prompt); int flash_isequal(struct flash_info *info, flash_sect_t sect, - uint offset, u32 cmd); + unsigned int offset, u32 cmd); void flash_make_cmd(struct flash_info *info, u32 cmd, cfiword_t *cmdbuf); static inline void flash_write8(u8 value, void *addr) @@ -298,12 +300,13 @@ static inline u64 flash_read64(void *addr) /* * create an address based on the offset and the port width */ -static inline uchar *flash_make_addr (struct flash_info *info, flash_sect_t sect, uint offset) +static inline u8 *flash_make_addr(struct flash_info *info, flash_sect_t sect, + unsigned int offset) { - return ((uchar *) (info->start[sect] + ((offset * info->portwidth) << info->chip_lsb))); + return ((u8 *)(info->start[sect] + ((offset * info->portwidth) << info->chip_lsb))); } -uchar flash_read_uchar (struct flash_info *info, uint offset); +u8 flash_read_uchar(struct flash_info *info, unsigned int offset); u32 jedec_read_mfr(struct flash_info *info); #ifdef CONFIG_DRIVER_CFI_BANK_WIDTH_1 @@ -342,21 +345,20 @@ static inline void flash_write_word(struct flash_info *info, cfiword_t datum, vo flash_write64(datum, addr); } -extern void flash_print_info (struct flash_info *); -extern int flash_sect_erase (ulong addr_first, ulong addr_last); -extern int flash_sect_protect (int flag, ulong addr_first, ulong addr_last); +extern void flash_print_info(struct flash_info *); +extern int flash_sect_erase(unsigned long addr_first, unsigned long addr_last); +extern int flash_sect_protect(int flag, unsigned long addr_first, + unsigned long addr_last); /* common/flash.c */ -extern void flash_protect (int flag, ulong from, ulong to, struct flash_info *info); -extern int flash_write (char *, ulong, ulong); -extern struct flash_info *addr2info (ulong); -//extern int write_buff (flash_info_t *info, const uchar *src, ulong addr, ulong cnt); +extern void flash_protect(int flag, unsigned long from, unsigned long to, + struct flash_info *info); +extern int flash_write(char *, unsigned long, unsigned long); -/* board/?/flash.c */ #if defined(CFG_FLASH_PROTECTION) extern int flash_real_protect(struct flash_info *info, long sector, int prot); -extern void flash_read_user_serial(struct flash_info *info, void * buffer, int offset, int len); -extern void flash_read_factory_serial(struct flash_info *info, void * buffer, int offset, int len); +extern void flash_read_user_serial(struct flash_info *info, void *buffer, int offset, int len); +extern void flash_read_factory_serial(struct flash_info *info, void *buffer, int offset, int len); #endif /* CFG_FLASH_PROTECTION */ /*----------------------------------------------------------------------- @@ -712,4 +714,3 @@ extern void flash_read_factory_serial(struct flash_info *info, void * buffer, in #define FLASH_WRITE_TIMEOUT 500 /* timeout for writes in ms */ #endif /* __CFI_FLASH_H */ - diff --git a/drivers/mtd/nor/cfi_flash_amd.c b/drivers/mtd/nor/cfi_flash_amd.c index 45c59b9d0..9c44561d4 100644 --- a/drivers/mtd/nor/cfi_flash_amd.c +++ b/drivers/mtd/nor/cfi_flash_amd.c @@ -19,7 +19,7 @@ static void cfi_reverse_geometry(struct cfi_qry *qry) } } -static void flash_unlock_seq (struct flash_info *info) +static void flash_unlock_seq(struct flash_info *info) { flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_UNLOCK_START); flash_write_cmd (info, 0, info->addr_unlock2, AMD_CMD_UNLOCK_ACK); @@ -31,7 +31,7 @@ static void flash_unlock_seq (struct flash_info *info) * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct * */ -static void amd_read_jedec_ids (struct flash_info *info) +static void amd_read_jedec_ids(struct flash_info *info) { info->cmd_reset = AMD_CMD_RESET; info->manufacturer_id = 0; @@ -62,20 +62,21 @@ static void amd_read_jedec_ids (struct flash_info *info) udelay(1000); /* some flash are slow to respond */ info->manufacturer_id = jedec_read_mfr(info); - info->device_id = flash_read_uchar (info, + info->device_id = flash_read_uchar(info, FLASH_OFFSET_DEVICE_ID); if (info->device_id == 0x7E) { /* AMD 3-byte (expanded) device ids */ - info->device_id2 = flash_read_uchar (info, + info->device_id2 = flash_read_uchar(info, FLASH_OFFSET_DEVICE_ID2); info->device_id2 <<= 8; - info->device_id2 |= flash_read_uchar (info, + info->device_id2 |= flash_read_uchar(info, FLASH_OFFSET_DEVICE_ID3); } flash_write_cmd(info, 0, 0, info->cmd_reset); } -static int flash_toggle (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd) +static int flash_toggle(struct flash_info *info, flash_sect_t sect, + unsigned int offset, u8 cmd) { void *addr; cfiword_t cword; @@ -83,6 +84,7 @@ static int flash_toggle (struct flash_info *info, flash_sect_t sect, uint offset addr = flash_make_addr (info, sect, offset); flash_make_cmd (info, cmd, &cword); + if (bankwidth_is_1(info)) { retval = flash_read8(addr) != flash_read8(addr); } else if (bankwidth_is_2(info)) { @@ -92,8 +94,9 @@ static int flash_toggle (struct flash_info *info, flash_sect_t sect, uint offset } else if (bankwidth_is_8(info)) { retval = ( (flash_read32( addr ) != flash_read32( addr )) || (flash_read32(addr+4) != flash_read32(addr+4)) ); - } else + } else { retval = 0; + } return retval; } @@ -102,12 +105,12 @@ static int flash_toggle (struct flash_info *info, flash_sect_t sect, uint offset * flash_is_busy - check to see if the flash is busy * This routine checks the status of the chip and returns true if the chip is busy */ -static int amd_flash_is_busy (struct flash_info *info, flash_sect_t sect) +static int amd_flash_is_busy(struct flash_info *info, flash_sect_t sect) { return flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); } -static int amd_flash_erase_one (struct flash_info *info, long sect) +static int amd_flash_erase_one(struct flash_info *info, long sect) { flash_unlock_seq(info); flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_ERASE_START); @@ -124,13 +127,12 @@ static void amd_flash_prepare_write(struct flash_info *info) } #ifdef CONFIG_CFI_BUFFER_WRITE -static int amd_flash_write_cfibuffer (struct flash_info *info, ulong dest, const uchar * cp, - int len) +static int amd_flash_write_cfibuffer(struct flash_info *info, unsigned long dest, + const u8 *cp, int len) { flash_sect_t sector; int cnt; - int retcode; - void *src = (void*)cp; + void *src = (void *)cp; void *dst = (void *)dest; cfiword_t cword; @@ -170,18 +172,18 @@ static int amd_flash_write_cfibuffer (struct flash_info *info, ulong dest, const } } - flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM); - retcode = flash_status_check (info, sector, info->buffer_write_tout, + flash_write_cmd(info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM); + + return flash_status_check(info, sector, info->buffer_write_tout, "buffer write"); - return retcode; } #else #define amd_flash_write_cfibuffer NULL #endif /* CONFIG_CFI_BUFFER_WRITE */ -static int amd_flash_real_protect (struct flash_info *info, long sector, int prot) +static int amd_flash_real_protect(struct flash_info *info, long sector, int prot) { - if (info->manufacturer_id != (uchar)ATM_MANUFACT) + if (info->manufacturer_id != (u8)ATM_MANUFACT) return 0; if (prot) { @@ -205,7 +207,7 @@ static int amd_flash_real_protect (struct flash_info *info, long sector, int pro * Manufacturer-specific quirks. Add workarounds for geometry * reversal, etc. here. */ -static void flash_fixup_amd (struct flash_info *info, struct cfi_qry *qry) +static void flash_fixup_amd(struct flash_info *info, struct cfi_qry *qry) { /* check if flash geometry needs reversal */ if (qry->num_erase_regions > 1) { @@ -265,4 +267,3 @@ struct cfi_cmd_set cfi_cmd_set_amd = { .flash_real_protect = amd_flash_real_protect, .flash_fixup = amd_flash_fixup, }; - diff --git a/drivers/mtd/nor/cfi_flash_intel.c b/drivers/mtd/nor/cfi_flash_intel.c index dcf8d9751..835734df3 100644 --- a/drivers/mtd/nor/cfi_flash_intel.c +++ b/drivers/mtd/nor/cfi_flash_intel.c @@ -6,8 +6,8 @@ * * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct * -*/ -static void intel_read_jedec_ids (struct flash_info *info) + */ +static void intel_read_jedec_ids(struct flash_info *info) { info->cmd_reset = FLASH_CMD_RESET; info->manufacturer_id = 0; @@ -19,7 +19,7 @@ static void intel_read_jedec_ids (struct flash_info *info) udelay(1000); /* some flash are slow to respond */ info->manufacturer_id = jedec_read_mfr(info); - info->device_id = flash_read_uchar (info, + info->device_id = flash_read_uchar(info, FLASH_OFFSET_DEVICE_ID); flash_write_cmd(info, 0, 0, info->cmd_reset); } @@ -28,16 +28,16 @@ static void intel_read_jedec_ids (struct flash_info *info) * flash_is_busy - check to see if the flash is busy * This routine checks the status of the chip and returns true if the chip is busy */ -static int intel_flash_is_busy (struct flash_info *info, flash_sect_t sect) +static int intel_flash_is_busy(struct flash_info *info, flash_sect_t sect) { return !flash_isset (info, sect, 0, FLASH_STATUS_DONE); } -static int intel_flash_erase_one (struct flash_info *info, long sect) +static int intel_flash_erase_one(struct flash_info *info, long sect) { - flash_write_cmd (info, sect, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, sect, 0, FLASH_CMD_BLOCK_ERASE); - flash_write_cmd (info, sect, 0, FLASH_CMD_ERASE_CONFIRM); + flash_write_cmd(info, sect, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd(info, sect, 0, FLASH_CMD_BLOCK_ERASE); + flash_write_cmd(info, sect, 0, FLASH_CMD_ERASE_CONFIRM); return flash_status_check(info, sect, info->erase_blk_tout, "erase"); } @@ -49,26 +49,26 @@ static void intel_flash_prepare_write(struct flash_info *info) } #ifdef CONFIG_CFI_BUFFER_WRITE -static int intel_flash_write_cfibuffer (struct flash_info *info, ulong dest, const uchar * cp, - int len) +static int intel_flash_write_cfibuffer(struct flash_info *info, + unsigned long dest, const u8 *cp, int len) { flash_sect_t sector; int cnt; - int retcode; + int ret; void *src = (void*)cp; void *dst = (void *)dest; /* reduce width due to possible alignment problems */ const unsigned long ptr = (unsigned long)dest | (unsigned long)cp | info->portwidth; const int width = ptr & -ptr; - sector = find_sector (info, dest); - flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER); + sector = find_sector(info, dest); + flash_write_cmd(info, sector, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd(info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER); - retcode = flash_generic_status_check (info, sector, info->buffer_write_tout, + ret = flash_generic_status_check(info, sector, info->buffer_write_tout, "write to buffer"); - if (retcode) - return retcode; + if (ret) + return ret; /* reduce the number of loops by the width of the port */ cnt = len / width; @@ -95,66 +95,70 @@ static int intel_flash_write_cfibuffer (struct flash_info *info, ulong dest, con } } - flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_BUFFER_CONFIRM); - retcode = flash_status_check (info, sector, + flash_write_cmd(info, sector, 0, FLASH_CMD_WRITE_BUFFER_CONFIRM); + ret = flash_status_check(info, sector, info->buffer_write_tout, "buffer write"); - return retcode; + return ret; } #else #define intel_flash_write_cfibuffer NULL #endif /* CONFIG_CFI_BUFFER_WRITE */ -static int intel_flash_status_check (struct flash_info *info, flash_sect_t sector, - uint64_t tout, char *prompt) +static int intel_flash_status_check(struct flash_info *info, flash_sect_t sector, + u64 tout, char *prompt) { - int retcode; + int ret; - retcode = flash_generic_status_check(info, sector, tout, prompt); - if (!retcode && !flash_isequal(info, sector, 0, FLASH_STATUS_DONE)) { - retcode = -EINVAL; - printf ("Flash %s error at address %lx\n", prompt, + ret = flash_generic_status_check(info, sector, tout, prompt); + if (!ret && !flash_isequal(info, sector, 0, FLASH_STATUS_DONE)) { + ret = -EINVAL; + printf("Flash %s error at address %lx\n", prompt, info->start[sector]); - if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) { - puts ("Command Sequence Error.\n"); - } else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) { - puts ("Block Erase Error.\n"); - retcode = -EIO; - } else if (flash_isset (info, sector, 0, FLASH_STATUS_PSLBS)) { - puts ("Locking Error\n"); - } - if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) { - puts ("Block locked.\n"); - retcode = -EROFS; - } - if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS)) - puts ("Vpp Low Error.\n"); - } - flash_write_cmd (info, sector, 0, info->cmd_reset); - return retcode; + if (flash_isset(info, sector, 0, FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) { + printf("Command Sequence Error.\n"); + } else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) { + printf("Block Erase Error.\n"); + ret = -EIO; + } else if (flash_isset(info, sector, 0, FLASH_STATUS_PSLBS)) { + printf("Locking Error\n"); + } + + if (flash_isset(info, sector, 0, FLASH_STATUS_DPS)) { + printf("Block locked.\n"); + ret = -EROFS; + } + + if (flash_isset(info, sector, 0, FLASH_STATUS_VPENS)) + printf("Vpp Low Error.\n"); + } + + flash_write_cmd(info, sector, 0, info->cmd_reset); + + return ret; } -static int intel_flash_real_protect (struct flash_info *info, long sector, int prot) +static int intel_flash_real_protect(struct flash_info *info, long sector, int prot) { - flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT); + flash_write_cmd(info, sector, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd(info, sector, 0, FLASH_CMD_PROTECT); + if (prot) - flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET); + flash_write_cmd(info, sector, 0, FLASH_CMD_PROTECT_SET); else - flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR); + flash_write_cmd(info, sector, 0, FLASH_CMD_PROTECT_CLEAR); return 0; } -static void intel_flash_fixup (struct flash_info *info, struct cfi_qry *qry) +static void intel_flash_fixup(struct flash_info *info, struct cfi_qry *qry) { #ifdef CFG_FLASH_PROTECTION /* read legacy lock/unlock bit from intel flash */ - if (info->ext_addr) { - info->legacy_unlock = flash_read_uchar (info, + if (info->ext_addr) + info->legacy_unlock = flash_read_uchar(info, info->ext_addr + 5) & 0x08; - } #endif } @@ -168,4 +172,3 @@ struct cfi_cmd_set cfi_cmd_set_intel = { .flash_real_protect = intel_flash_real_protect, .flash_fixup = intel_flash_fixup, }; - From 713285ca19f39e9a48598416ccab222105acf708 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 22 Jun 2015 10:37:39 +0200 Subject: [PATCH 11/13] mtd: cfi-flash: remove dead code CFG_FLASH_PROTECTION is never defined, remove the code. Also remove code inside #if 0. Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 36 ------------------------------- drivers/mtd/nor/cfi_flash.h | 6 ------ drivers/mtd/nor/cfi_flash_intel.c | 6 ------ 3 files changed, 48 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index b93c663b8..f9aecadb5 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -308,9 +308,6 @@ static int flash_detect_size(struct flash_info *info) info->ext_addr = 0; info->cfi_version = 0; -#ifdef CFG_FLASH_PROTECTION - info->legacy_unlock = 0; -#endif /* first only malloc space for the first sector */ info->start = xmalloc(sizeof(*info->eraseregions)); @@ -763,39 +760,6 @@ static void cfi_info (struct device_d* dev) return; } -#if 0 -/* - * flash_read_user_serial - read the OneTimeProgramming cells - */ -static void flash_read_user_serial (struct flash_info *info, void *buffer, int offset, - int len) -{ - uchar *src; - uchar *dst; - - dst = buffer; - src = flash_make_addr (info, 0, FLASH_OFFSET_USER_PROTECTION); - flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); - memcpy (dst, src + offset, len); - flash_write_cmd (info, 0, 0, info->cmd_reset); -} - -/* - * flash_read_factory_serial - read the device Id from the protection area - */ -static void flash_read_factory_serial (struct flash_info *info, void *buffer, int offset, - int len) -{ - uchar *src; - - src = flash_make_addr (info, 0, FLASH_OFFSET_INTEL_PROTECTION); - flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); - memcpy (buffer, src + offset, len); - flash_write_cmd (info, 0, 0, info->cmd_reset); -} - -#endif - int flash_status_check(struct flash_info *info, flash_sect_t sector, u64 tout, char *prompt) { diff --git a/drivers/mtd/nor/cfi_flash.h b/drivers/mtd/nor/cfi_flash.h index 6d975b94e..e82eb28ab 100644 --- a/drivers/mtd/nor/cfi_flash.h +++ b/drivers/mtd/nor/cfi_flash.h @@ -355,12 +355,6 @@ extern void flash_protect(int flag, unsigned long from, unsigned long to, struct flash_info *info); extern int flash_write(char *, unsigned long, unsigned long); -#if defined(CFG_FLASH_PROTECTION) -extern int flash_real_protect(struct flash_info *info, long sector, int prot); -extern void flash_read_user_serial(struct flash_info *info, void *buffer, int offset, int len); -extern void flash_read_factory_serial(struct flash_info *info, void *buffer, int offset, int len); -#endif /* CFG_FLASH_PROTECTION */ - /*----------------------------------------------------------------------- * Protection Flags for flash_protect(): */ diff --git a/drivers/mtd/nor/cfi_flash_intel.c b/drivers/mtd/nor/cfi_flash_intel.c index 835734df3..6108d7cc6 100644 --- a/drivers/mtd/nor/cfi_flash_intel.c +++ b/drivers/mtd/nor/cfi_flash_intel.c @@ -154,12 +154,6 @@ static int intel_flash_real_protect(struct flash_info *info, long sector, int pr static void intel_flash_fixup(struct flash_info *info, struct cfi_qry *qry) { -#ifdef CFG_FLASH_PROTECTION - /* read legacy lock/unlock bit from intel flash */ - if (info->ext_addr) - info->legacy_unlock = flash_read_uchar(info, - info->ext_addr + 5) & 0x08; -#endif } struct cfi_cmd_set cfi_cmd_set_intel = { From ce72d46f426b8dd512fcd9abbd3fec1be319fe29 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 12:03:55 +0200 Subject: [PATCH 12/13] mtd: Add mtd concat support The mtd concat layer supports concatenating several MTD devices into a single one. This is nearly as-is from the corresponding Kernel code. Signed-off-by: Sascha Hauer --- drivers/mtd/Kconfig | 10 + drivers/mtd/Makefile | 1 + drivers/mtd/mtdconcat.c | 765 +++++++++++++++++++++++++++++++++++++ include/linux/mtd/concat.h | 34 ++ 4 files changed, 810 insertions(+) create mode 100644 drivers/mtd/mtdconcat.c create mode 100644 include/linux/mtd/concat.h diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 49ea88cac..ea1be556b 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -20,6 +20,16 @@ config MTD_RAW_DEVICE default n prompt "mtdraw device to read/write both data+oob" +config MTD_CONCAT + bool + prompt "MTD concatenating support" + help + Support for concatenating several MTD devices into a single + (virtual) one. This allows you to have -for example- a JFFS(2) + file system spanning multiple physical flash chips. This option + needs driver support, currently only the cfi-flash driver supports + concatenating MTD devices. + source "drivers/mtd/devices/Kconfig" source "drivers/mtd/nor/Kconfig" source "drivers/mtd/nand/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 148ec6ca2..d3ae7fca3 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -6,3 +6,4 @@ obj-y += devices/ obj-$(CONFIG_MTD) += core.o partition.o obj-$(CONFIG_MTD_OOB_DEVICE) += mtdoob.o obj-$(CONFIG_MTD_RAW_DEVICE) += mtdraw.o +obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c new file mode 100644 index 000000000..10d3dc4c3 --- /dev/null +++ b/drivers/mtd/mtdconcat.c @@ -0,0 +1,765 @@ +/* + * MTD device concatenation layer + * + * Copyright © 2002 Robert Kaiser + * Copyright © 2002-2010 David Woodhouse + * + * NAND support by Christian Gan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include +#include +#include +#include + +#include + +/* + * Our storage structure: + * Subdev points to an array of pointers to struct mtd_info objects + * which is allocated along with this structure + * + */ +struct mtd_concat { + struct mtd_info mtd; + int num_subdev; + struct mtd_info **subdev; +}; + +/* + * how to calculate the size required for the above structure, + * including the pointer array subdev points to: + */ +#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + +/* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ +#define CONCAT(x) ((struct mtd_concat *)(x)) + +/* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +static int +concat_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int ret = 0, err; + int i; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; + from -= subdev->size; + continue; + } + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; + else + /* Entire transaction goes into this subdev */ + size = len; + + err = mtd_read(subdev, from, size, &retsize, buf); + + /* Save information about bitflips! */ + if (unlikely(err)) { + if (mtd_is_eccerr(err)) { + mtd->ecc_stats.failed++; + ret = err; + } else if (mtd_is_bitflip(err)) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; + } + + *retlen += retsize; + len -= size; + if (len == 0) + return ret; + + buf += size; + from = 0; + } + return -EINVAL; +} + +static int +concat_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) { + size = 0; + to -= subdev->size; + continue; + } + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + err = mtd_write(subdev, to, size, &retsize, buf); + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + return err; +} + +static int +concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_oob_ops devops = *ops; + int i, err, ret = 0; + + ops->retlen = ops->oobretlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (from >= subdev->size) { + from -= subdev->size; + continue; + } + + /* partial read ? */ + if (from + devops.len > subdev->size) + devops.len = subdev->size - from; + + err = mtd_read_oob(subdev, from, &devops); + ops->retlen += devops.retlen; + ops->oobretlen += devops.oobretlen; + + /* Save information about bitflips! */ + if (unlikely(err)) { + if (mtd_is_eccerr(err)) { + mtd->ecc_stats.failed++; + ret = err; + } else if (mtd_is_bitflip(err)) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; + } + + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return ret; + devops.datbuf += devops.retlen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return ret; + devops.oobbuf += ops->oobretlen; + } + + from = 0; + } + return -EINVAL; +} + +static int +concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_oob_ops devops = *ops; + int i, err; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + ops->retlen = ops->oobretlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (to >= subdev->size) { + to -= subdev->size; + continue; + } + + /* partial write ? */ + if (to + devops.len > subdev->size) + devops.len = subdev->size - to; + + err = mtd_write_oob(subdev, to, &devops); + ops->retlen += devops.retlen; + ops->oobretlen += devops.oobretlen; + if (err) + return err; + + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; + devops.datbuf += devops.retlen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return 0; + devops.oobbuf += devops.oobretlen; + } + to = 0; + } + return -EINVAL; +} + +static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + int err; + wait_queue_head_t waitq; + + erase->mtd = mtd; + erase->priv = (unsigned long) &waitq; + + err = mtd_erase(mtd, erase); + + return err; +} + +static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; + int i, err; + uint64_t length, offset = 0; + struct erase_info *erase; + + /* + * Check for proper erase block alignment of the to-be-erased area. + * It is easier to do this based on the super device's erase + * region info rather than looking at each particular sub-device + * in turn. + */ + if (!concat->mtd.numeraseregions) { + /* the easy case: device has uniform erase block size */ + if (instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; + if (instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; + } else { + /* device has variable erase size */ + struct mtd_erase_region_info *erase_regions = + concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ + for (i = 0; i < concat->mtd.numeraseregions && + instr->addr >= erase_regions[i].offset; i++) ; + --i; + + /* + * Now erase_regions[i] is the region in which the + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ + if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ + for (; i < concat->mtd.numeraseregions && + (instr->addr + instr->len) >= erase_regions[i].offset; + ++i) ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ + if (i < 0 || ((instr->addr + instr->len) & + (erase_regions[i].erasesize - 1))) + return -EINVAL; + } + + /* make a local copy of instr to avoid modifying the caller's struct */ + erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); + + if (!erase) + return -ENOMEM; + + *erase = *instr; + length = instr->len; + + /* + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ + for (i = 0; i < concat->num_subdev; i++) { + subdev = concat->subdev[i]; + if (subdev->size <= erase->addr) { + erase->addr -= subdev->size; + offset += subdev->size; + } else { + break; + } + } + + /* must never happen since size limit has been verified above */ + BUG_ON(i >= concat->num_subdev); + + /* now do the erase: */ + err = 0; + for (; length > 0; i++) { + /* loop for all subdevices affected by this request */ + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ + if (erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + + length -= erase->len; + if ((err = concat_dev_erase(subdev, erase))) { + /* sanity check: should never happen since + * block alignment has been checked above */ + BUG_ON(err == -EINVAL); + if (erase->fail_addr != 0xffffffff) + instr->fail_addr = erase->fail_addr + offset; + break; + } + /* + * erase->addr specifies the offset of the area to be + * erased *within the current subdevice*. It can be + * non-zero only the first time through this loop, i.e. + * for the first subdevice where blocks need to be erased. + * All the following erases must begin at the start of the + * current subdevice, i.e. at offset zero. + */ + erase->addr = 0; + offset += subdev->size; + } + instr->state = erase->state; + kfree(erase); + if (err) + return err; + + if (instr->callback) + instr->callback(instr); + return 0; +} + +static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + uint64_t size; + + if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; + continue; + } + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = mtd_lock(subdev, ofs, size); + if (err) + break; + + len -= size; + if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + + return err; +} + +static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + uint64_t size; + + if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; + continue; + } + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = mtd_unlock(subdev, ofs, size); + if (err) + break; + + len -= size; + if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + + return err; +} + +static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, res = 0; + + if (!mtd_can_have_bb(concat->subdev[0])) + return res; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (ofs >= subdev->size) { + ofs -= subdev->size; + continue; + } + + res = mtd_block_isbad(subdev, ofs); + break; + } + + return res; +} + +static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (ofs >= subdev->size) { + ofs -= subdev->size; + continue; + } + + err = mtd_block_markbad(subdev, ofs); + if (!err) + mtd->ecc_stats.badblocks++; + break; + } + + return err; +} + +/* + * This function constructs a virtual MTD device by concatenating + * num_devs MTD devices. A pointer to the new device object is + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + const char *name) +{ /* name for the new device */ + int i; + size_t size; + struct mtd_concat *concat; + uint32_t max_erasesize, curr_erasesize; + int num_erase_region; + int max_writebufsize = 0; + + printk(KERN_NOTICE "Concatenating MTD devices:\n"); + for (i = 0; i < num_devs; i++) + printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); + printk(KERN_NOTICE "into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); + concat = kzalloc(size, GFP_KERNEL); + if (!concat) { + printk + ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + concat->subdev = (struct mtd_info **) (concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for + * incompatibilities between the subdevices. + */ + concat->mtd.type = subdev[0]->type; + concat->mtd.flags = subdev[0]->flags; + concat->mtd.size = subdev[0]->size; + concat->mtd.erasesize = subdev[0]->erasesize; + concat->mtd.writesize = subdev[0]->writesize; + + for (i = 0; i < num_devs; i++) + if (max_writebufsize < subdev[i]->writebufsize) + max_writebufsize = subdev[i]->writebufsize; + concat->mtd.writebufsize = max_writebufsize; + + concat->mtd.subpage_sft = subdev[0]->subpage_sft; + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.oobavail = subdev[0]->oobavail; + if (subdev[0]->read_oob) + concat->mtd.read_oob = concat_read_oob; + if (subdev[0]->write_oob) + concat->mtd.write_oob = concat_write_oob; + if (subdev[0]->block_isbad) + concat->mtd.block_isbad = concat_block_isbad; + if (subdev[0]->block_markbad) + concat->mtd.block_markbad = concat_block_markbad; + + concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; + + concat->subdev[0] = subdev[0]; + + for (i = 1; i < num_devs; i++) { + if (concat->mtd.type != subdev[i]->type) { + kfree(concat); + printk("Incompatible device type on \"%s\"\n", + subdev[i]->name); + return NULL; + } + if (concat->mtd.flags != subdev[i]->flags) { + /* + * Expect all flags except MTD_WRITEABLE to be + * equal on all subdevices. + */ + if ((concat->mtd.flags ^ subdev[i]-> + flags) & ~MTD_WRITEABLE) { + kfree(concat); + printk("Incompatible device flags on \"%s\"\n", + subdev[i]->name); + return NULL; + } else + /* if writeable attribute differs, + make super device writeable */ + concat->mtd.flags |= + subdev[i]->flags & MTD_WRITEABLE; + } + + concat->mtd.size += subdev[i]->size; + concat->mtd.ecc_stats.badblocks += + subdev[i]->ecc_stats.badblocks; + if (concat->mtd.writesize != subdev[i]->writesize || + concat->mtd.subpage_sft != subdev[i]->subpage_sft || + concat->mtd.oobsize != subdev[i]->oobsize || + !concat->mtd.read_oob != !subdev[i]->read_oob || + !concat->mtd.write_oob != !subdev[i]->write_oob) { + kfree(concat); + printk("Incompatible OOB or ECC data on \"%s\"\n", + subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; + + } + + concat->mtd.ecclayout = subdev[0]->ecclayout; + + concat->num_subdev = num_devs; + concat->mtd.name = xstrdup(name); + + concat->mtd.erase = concat_erase; + concat->mtd.read = concat_read; + concat->mtd.write = concat_write; + concat->mtd.lock = concat_lock; + concat->mtd.unlock = concat_unlock; + + /* + * Combine the erase block size info of the subdevices: + * + * first, walk the map of the new device and see how + * many changes in erase size we have + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; + if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } else { + /* current subdevice has variable erase size */ + int j; + for (j = 0; j < subdev[i]->numeraseregions; j++) { + + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j].erasesize != + curr_erasesize) { + ++num_erase_region; + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + + if (num_erase_region == 1) { + /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; + } else { + uint64_t tmp64; + + /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ + struct mtd_erase_region_info *erase_region_p; + uint64_t begin, position; + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; + concat->mtd.eraseregions = erase_region_p = + kmalloc(num_erase_region * + sizeof (struct mtd_erase_region_info), GFP_KERNEL); + if (!erase_region_p) { + kfree(concat); + printk + ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } + + /* + * walk the map of the new device once more and fill in + * in erase region info: + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; + } else { + /* current subdevice has variable erase size */ + int j; + for (j = 0; j < subdev[i]->numeraseregions; j++) { + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j]. + erasesize != curr_erasesize) { + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + begin = position; + + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + ++erase_region_p; + } + position += + subdev[i]->eraseregions[j]. + numblocks * (uint64_t)curr_erasesize; + } + } + } + /* Now write the final entry */ + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + } + + return &concat->mtd; +} + +/* + * This function destroys an MTD object obtained from concat_mtd_devs() + */ + +void mtd_concat_destroy(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + if (concat->mtd.numeraseregions) + kfree(concat->mtd.eraseregions); + kfree(concat); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Kaiser "); +MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); diff --git a/include/linux/mtd/concat.h b/include/linux/mtd/concat.h new file mode 100644 index 000000000..ccdbe93a9 --- /dev/null +++ b/include/linux/mtd/concat.h @@ -0,0 +1,34 @@ +/* + * MTD device concatenation layer definitions + * + * Copyright © 2002 Robert Kaiser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef MTD_CONCAT_H +#define MTD_CONCAT_H + + +struct mtd_info *mtd_concat_create( + struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + const char *name); /* name for the new device */ + +void mtd_concat_destroy(struct mtd_info *mtd); + +#endif + From 636d384ed4a4cfe443e7ee7f250941a5311cd1c8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 19 Jun 2015 12:04:13 +0200 Subject: [PATCH 13/13] mtd: cfi-flash: Add mtd concat support Signed-off-by: Sascha Hauer --- drivers/mtd/nor/cfi_flash.c | 92 +++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c index f9aecadb5..f2268dfde 100644 --- a/drivers/mtd/nor/cfi_flash.c +++ b/drivers/mtd/nor/cfi_flash.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "cfi_flash.h" /* @@ -920,6 +921,12 @@ static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) return 0; } +struct cfi_priv { + struct flash_info *infos; + int num_devs; + struct mtd_info **mtds; +}; + static void cfi_init_mtd(struct flash_info *info) { struct mtd_info *mtd = &info->mtd; @@ -948,39 +955,84 @@ static void cfi_init_mtd(struct flash_info *info) mtd->flags = MTD_CAP_NORFLASH; mtd->type = MTD_NORFLASH; mtd->parent = info->dev; +} - add_mtd_device(mtd, "nor", DEVICE_ID_DYNAMIC); +static int cfi_probe_one(struct flash_info *info, int num) +{ + int ret; + + info->flash_id = FLASH_UNKNOWN; + info->cmd_reset = FLASH_CMD_RESET; + info->base = dev_request_mem_region(info->dev, num); + if (IS_ERR(info->base)) + return PTR_ERR(info->base); + + ret = flash_detect_size(info); + if (ret) { + dev_warn(info->dev, "## Unknown FLASH on Bank at 0x%p - Size = 0x%08lx = %ld MB\n", + info->base, info->size, info->size << 20); + return -ENODEV; + } + + dev_info(info->dev, "found cfi flash at 0x%p, size %s\n", + info->base, size_human_readable(info->size)); + + cfi_init_mtd(info); + + return 0; } static int cfi_probe(struct device_d *dev) { - struct flash_info *info = xzalloc(sizeof(*info)); - int ret; + struct cfi_priv *priv; + int i, ret; + struct mtd_info *mtd; + const char *mtd_name = NULL; - dev->priv = info; + priv = xzalloc(sizeof(*priv)); - /* Init: no FLASHes known */ - info->flash_id = FLASH_UNKNOWN; - info->cmd_reset = FLASH_CMD_RESET; - info->base = dev_request_mem_region(dev, 0); - if (IS_ERR(info->base)) - return PTR_ERR(info->base); + priv->num_devs = dev->num_resources; + priv->infos = xzalloc(sizeof(*priv->infos) * priv->num_devs); + priv->mtds = xzalloc(sizeof(*priv->mtds) * priv->num_devs); - info->dev = dev; + of_property_read_string(dev->device_node, "linux,mtd-name", &mtd_name); - ret = flash_detect_size(info); - if (ret) { - 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; + if (!mtd_name) + mtd_name = dev_name(dev); + + dev->priv = priv; + + for (i = 0; i < priv->num_devs; i++) { + struct flash_info *info = &priv->infos[i]; + + info->dev = dev; + info->mtd.name = xstrdup(mtd_name); + + ret = cfi_probe_one(info, i); + if (ret) + return ret; + priv->mtds[i] = &priv->infos[i].mtd; } - dev_info(dev, "found cfi flash at 0x%p, size %s\n", - info->base, size_human_readable(info->size)); - dev->info = cfi_info; - cfi_init_mtd(info); + if (priv->num_devs > 1 && IS_ENABLED(CONFIG_MTD_CONCAT)) { + mtd = mtd_concat_create(priv->mtds, priv->num_devs, "nor"); + if (!mtd) { + dev_err(dev, "failed to create concat mtd device\n"); + return -ENODEV; + } + } else { + if (priv->num_devs > 1) + dev_warn(dev, "mtd concat disabled. using first chip only\n"); + mtd = &priv->infos[0].mtd; + } + + mtd->parent = dev; + + ret = add_mtd_device(mtd, "nor", DEVICE_ID_DYNAMIC); + if (ret) + return ret; return 0; }