From patchwork Sat Apr 2 07:17:01 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Williams X-Patchwork-Id: 89429 X-Patchwork-Delegate: sr@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 0E99FB6F44 for ; Sat, 2 Apr 2011 18:17:37 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 954B92808C; Sat, 2 Apr 2011 09:17:34 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Lsi4Z7rC3VqW; Sat, 2 Apr 2011 09:17:33 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id F3D4428087; Sat, 2 Apr 2011 09:17:29 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 94AB228087 for ; Sat, 2 Apr 2011 09:17:26 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id HdsCuflYxber for ; Sat, 2 Apr 2011 09:17:21 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail3.caviumnetworks.com (mail3.caviumnetworks.com [12.108.191.235]) by theia.denx.de (Postfix) with ESMTP id 0A7B728083 for ; Sat, 2 Apr 2011 09:17:19 +0200 (CEST) Received: from caexch01.caveonetworks.com (Not Verified[192.168.16.9]) by mail3.caviumnetworks.com with MailMarshal (v6, 7, 2, 8378) id ; Sat, 02 Apr 2011 00:18:15 -0700 Received: from caexch01.caveonetworks.com ([192.168.16.9]) by caexch01.caveonetworks.com with Microsoft SMTPSVC(6.0.3790.4675); Sat, 2 Apr 2011 00:17:17 -0700 Received: from localhost.localdomain ([12.108.191.236]) by caexch01.caveonetworks.com over TLS secured channel with Microsoft SMTPSVC(6.0.3790.4675); Sat, 2 Apr 2011 00:17:17 -0700 From: Aaron Williams To: u-boot@lists.denx.de, wd@denx.de, sr@denx.de, Aaron.Williams@caviumnetworks.com Date: Sat, 2 Apr 2011 00:17:01 -0700 Message-Id: <1301728621-27620-1-git-send-email-aaron.williams@caviumnetworks.com> X-Mailer: git-send-email 1.7.3.4 X-OriginalArrivalTime: 02 Apr 2011 07:17:17.0368 (UTC) FILETIME=[FF7CA380:01CBF105] Cc: aaron.williams@caviumnetworks.com Subject: [U-Boot] [PATCH] Fix CFI flash driver for 8-bit bus support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This patch corrects the addresses used when working with Spansion/AMD FLASH chips. Addressing for 8 and 16 bits is almost identical except in the 16-bit case the LSB of the address is always 0. The confusion arose because the addresses in the datasheet for 16-bit mode are word addresses but this code assumed it was byte addresses. I have only been able to test this on our Octeon boards which use either an 8-bit or 16-bit bus. I have not tested the case where there's an 8-bit part on a 16-bit bus. This patch also adds some delays as suggested by Spansion. If a part can be both 8 and 16-bits, it forces it to work in 8-bit mode if an 8-bit bus is detected. -Aaron Williams Signed-off-by: Aaron Williams Tested-by: Albert ARIBAUD --- drivers/mtd/cfi_flash.c | 93 ++++++++++++++++++++++++++++++++++------------- include/mtd/cfi_flash.h | 40 ++++++++++---------- 2 files changed, 87 insertions(+), 46 deletions(-) diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 0909fe7..eab1fbe 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -10,6 +10,9 @@ * * Copyright (C) 2006 * Tolunay Orkun + * + * Copyright (C) 2011 Cavium Networks, Inc. + * Aaron Williams * * See file CREDITS for list of people who contributed to this * project. @@ -32,7 +35,6 @@ */ /* The DEBUG define must be before common to enable debugging */ -/* #define DEBUG */ #include #include @@ -209,9 +211,11 @@ unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect) static inline void * flash_map (flash_info_t * info, flash_sect_t sect, uint offset) { - unsigned int byte_offset = offset * info->portwidth; - - return (void *)(info->start[sect] + byte_offset); + unsigned int byte_offset = offset * info->portwidth / info->chipwidth; + unsigned int addr = (info->start[sect] + byte_offset); + unsigned int mask = 0xffffffff << (info->portwidth - 1); + + return (void *)(addr & mask); } static inline void flash_unmap(flash_info_t *info, flash_sect_t sect, @@ -397,6 +401,8 @@ void flash_write_cmd (flash_info_t * info, flash_sect_t sect, #endif flash_write64(cword.ll, addr); break; + default: + debug ("fwc: Unknown port width %d\n", info->portwidth); } /* Ensure all the instructions are fully finished */ @@ -581,6 +587,7 @@ static int flash_status_check (flash_info_t * info, flash_sect_t sector, prompt, info->start[sector], flash_read_long (info, sector, 0)); flash_write_cmd (info, sector, 0, info->cmd_reset); + udelay(1); return ERR_TIMOUT; } udelay (1); /* also triggers watchdog */ @@ -628,6 +635,7 @@ static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, puts ("Vpp Low Error.\n"); } flash_write_cmd (info, sector, 0, info->cmd_reset); + udelay(1); break; default: break; @@ -744,12 +752,8 @@ static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) static flash_sect_t find_sector (flash_info_t * info, ulong addr) { static flash_sect_t saved_sector = 0; /* previously found sector */ - static flash_info_t *saved_info = 0; /* previously used flash bank */ flash_sect_t sector = saved_sector; - if ((info != saved_info) || (sector >= info->sector_count)) - sector = 0; - while ((info->start[sector] < addr) && (sector < info->sector_count - 1)) sector++; @@ -761,7 +765,6 @@ static flash_sect_t find_sector (flash_info_t * info, ulong addr) sector--; saved_sector = sector; - saved_info = info; return sector; } @@ -825,12 +828,15 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest, switch (info->portwidth) { case FLASH_CFI_8BIT: + debug("%s: 8-bit 0x%02x\n", __func__, cword.c); flash_write8(cword.c, dstaddr); break; case FLASH_CFI_16BIT: + debug("%s: 16-bit 0x%04x\n", __func__, cword.w); flash_write16(cword.w, dstaddr); break; case FLASH_CFI_32BIT: + debug("%s: 32-bit 0x%08lx\n", __func__, cword.l); flash_write32(cword.l, dstaddr); break; case FLASH_CFI_64BIT: @@ -1043,6 +1049,8 @@ int flash_erase (flash_info_t * info, int s_first, int s_last) int prot; flash_sect_t sect; int st; + + debug("%s: erasing sectors %d to %d\n", __func__, s_first, s_last); if (info->flash_id != FLASH_MAN_CFI) { puts ("Can't erase unknown flash type - aborted\n"); @@ -1082,6 +1090,7 @@ int flash_erase (flash_info_t * info, int s_first, int s_last) break; case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: + flash_write_cmd (info, 0, 0, AMD_CMD_RESET); flash_unlock_seq (info, sect); flash_write_cmd (info, sect, info->addr_unlock1, @@ -1121,6 +1130,8 @@ int flash_erase (flash_info_t * info, int s_first, int s_last) rcode = 1; else if (flash_verbose) putc ('.'); + } else { + debug("\nSector %d is protected.\n", sect); } } @@ -1490,6 +1501,7 @@ void flash_read_user_serial (flash_info_t * info, void *buffer, int offset, flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); memcpy (dst, src + offset, len); flash_write_cmd (info, 0, 0, info->cmd_reset); + udelay(1); flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src); } @@ -1505,6 +1517,7 @@ void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset, flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); memcpy (buffer, src + offset, len); flash_write_cmd (info, 0, 0, info->cmd_reset); + udelay(1); flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src); } @@ -1536,6 +1549,7 @@ static void cfi_reverse_geometry(struct cfi_qry *qry) static void cmdset_intel_read_jedec_ids(flash_info_t *info) { flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); + udelay(1); flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID); udelay(1000); /* some flash are slow to respond */ info->manufacturer_id = flash_read_uchar (info, @@ -1573,8 +1587,7 @@ static void cmdset_amd_read_jedec_ids(flash_info_t *info) flash_unlock_seq(info, 0); flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID); udelay(1000); /* some flash are slow to respond */ - - manuId = flash_read_uchar (info, FLASH_OFFSET_MANUFACTURER_ID); + manuId = flash_read_uchar(info, FLASH_OFFSET_MANUFACTURER_ID); /* JEDEC JEP106Z specifies ID codes up to bank 7 */ while (manuId == FLASH_CONTINUATION_CODE && bankId < 0x800) { bankId += 0x100; @@ -1604,6 +1617,7 @@ static void cmdset_amd_read_jedec_ids(flash_info_t *info) break; } flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + udelay(1); } static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry) @@ -1719,7 +1733,7 @@ static void flash_read_cfi (flash_info_t *info, void *buf, unsigned int i; for (i = 0; i < len; i++) - p[i] = flash_read_uchar(info, start + i); + p[i] = flash_read_uchar(info, start + (i * 2)); } void __flash_cmd_reset(flash_info_t *info) @@ -1730,6 +1744,7 @@ void __flash_cmd_reset(flash_info_t *info) * that AMD flash roms ignore the Intel command. */ flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + udelay(1); flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); } void flash_cmd_reset(flash_info_t *info) @@ -1739,21 +1754,38 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) { int cfi_offset; - /* Issue FLASH reset command */ - flash_cmd_reset(info); - for (cfi_offset=0; cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint); cfi_offset++) { + /* Issue FLASH reset command */ + flash_cmd_reset(info); flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset], FLASH_CMD_CFI); if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q') - && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R') - && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) { + && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'R') + && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 4, 'Y')) { flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP, sizeof(struct cfi_qry)); +#ifdef CONFIG_SYS_FLASH_INTERFACE_WIDTH + info->interface = CONFIG_SYS_FLASH_INTERFACE_WIDTH; +#else info->interface = le16_to_cpu(qry->interface_desc); - + /* Some flash chips can support multiple bus widths. + * In this case, override the interface width and + * limit it to the port width. + */ + if ((info->interface == FLASH_CFI_X8X16) && + (info->portwidth == FLASH_CFI_8BIT)) { + debug("Overriding 16-bit interface width to " + " 8-bit port width.\n"); + info->interface = FLASH_CFI_X8; + } else if ((info->interface == FLASH_CFI_X16X32) && + (info->portwidth == FLASH_CFI_16BIT)) { + debug("Overriding 16-bit interface width to " + " 16-bit port width.\n"); + info->interface = FLASH_CFI_X16; + } +#endif info->cfi_offset = flash_offset_cfi[cfi_offset]; debug ("device interface is %d\n", info->interface); @@ -1764,8 +1796,8 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) info->chipwidth << CFI_FLASH_SHIFT_WIDTH); /* calculate command offsets as in the Linux driver */ - info->addr_unlock1 = 0x555; - info->addr_unlock2 = 0x2aa; + info->addr_unlock1 = 0xaaa; + info->addr_unlock2 = 0x555; /* * modify the unlock address if we are @@ -1799,8 +1831,11 @@ static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) for (info->chipwidth = FLASH_CFI_BY8; info->chipwidth <= info->portwidth; info->chipwidth <<= 1) - if (__flash_detect_cfi(info, qry)) + if (__flash_detect_cfi(info, qry)) { + debug("Found CFI flash, portwidth %d, chipwidth %d\n", + info->portwidth, info->chipwidth); return 1; + } } debug ("not found\n"); return 0; @@ -1819,7 +1854,7 @@ static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry) /* CFI < 1.1, try to guess from device id */ if ((info->device_id & 0x80) != 0) cfi_reverse_geometry(qry); - } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) { + } else if (flash_read_uchar(info, info->ext_addr + 0x1e) == 3) { /* CFI >= 1.1, deduct from top/bottom flag */ /* note: ext_addr is valid since cfi_version > 0 */ cfi_reverse_geometry(qry); @@ -1891,14 +1926,15 @@ ulong flash_get_size (phys_addr_t base, int banknum) if (flash_detect_cfi (info, &qry)) { info->vendor = le16_to_cpu(qry.p_id); - info->ext_addr = le16_to_cpu(qry.p_adr); + info->ext_addr = le16_to_cpu(qry.p_adr) * 2; + debug("extended address is 0x%x\n", info->ext_addr); num_erase_regions = qry.num_erase_regions; if (info->ext_addr) { info->cfi_version = (ushort) flash_read_uchar (info, - info->ext_addr + 3) << 8; + info->ext_addr + 6) << 8; info->cfi_version |= (ushort) flash_read_uchar (info, - info->ext_addr + 4); + info->ext_addr + 8); } #ifdef DEBUG @@ -1945,13 +1981,16 @@ ulong flash_get_size (phys_addr_t base, int banknum) debug ("device id is 0x%x\n", info->device_id); debug ("device id2 is 0x%x\n", info->device_id2); debug ("cfi version is 0x%04x\n", info->cfi_version); - + debug ("port width: %d, chipwidth: %d, interface: %d\n", + info->portwidth, info->chipwidth, info->interface); size_ratio = info->portwidth / info->chipwidth; +#if 0 /* 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; } +#endif debug ("size_ratio %d port %d bits chip %d bits\n", size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); @@ -2023,6 +2062,8 @@ ulong flash_get_size (phys_addr_t base, int banknum) sect_cnt++; } } + debug ("port width: %d, chipwidth: %d, interface: %d\n", + info->portwidth, info->chipwidth, info->interface); info->sector_count = sect_cnt; info->buffer_size = 1 << le16_to_cpu(qry.max_buf_write_size); diff --git a/include/mtd/cfi_flash.h b/include/mtd/cfi_flash.h index 3245b44..edcf9be 100644 --- a/include/mtd/cfi_flash.h +++ b/include/mtd/cfi_flash.h @@ -71,29 +71,29 @@ #define FLASH_CONTINUATION_CODE 0x7F #define FLASH_OFFSET_MANUFACTURER_ID 0x00 -#define FLASH_OFFSET_DEVICE_ID 0x01 -#define FLASH_OFFSET_DEVICE_ID2 0x0E -#define FLASH_OFFSET_DEVICE_ID3 0x0F -#define FLASH_OFFSET_CFI 0x55 +#define FLASH_OFFSET_DEVICE_ID 0x02 +#define FLASH_OFFSET_DEVICE_ID2 0x1C +#define FLASH_OFFSET_DEVICE_ID3 0x1E +#define FLASH_OFFSET_CFI 0xAA #define FLASH_OFFSET_CFI_ALT 0x555 -#define FLASH_OFFSET_CFI_RESP 0x10 -#define FLASH_OFFSET_PRIMARY_VENDOR 0x13 +#define FLASH_OFFSET_CFI_RESP 0x20 +#define FLASH_OFFSET_PRIMARY_VENDOR 0x26 /* extended query table primary address */ -#define FLASH_OFFSET_EXT_QUERY_T_P_ADDR 0x15 +#define FLASH_OFFSET_EXT_QUERY_T_P_ADDR 0x2A #define FLASH_OFFSET_WTOUT 0x1F -#define FLASH_OFFSET_WBTOUT 0x20 -#define FLASH_OFFSET_ETOUT 0x21 -#define FLASH_OFFSET_CETOUT 0x22 -#define FLASH_OFFSET_WMAX_TOUT 0x23 -#define FLASH_OFFSET_WBMAX_TOUT 0x24 -#define FLASH_OFFSET_EMAX_TOUT 0x25 -#define FLASH_OFFSET_CEMAX_TOUT 0x26 -#define FLASH_OFFSET_SIZE 0x27 -#define FLASH_OFFSET_INTERFACE 0x28 -#define FLASH_OFFSET_BUFFER_SIZE 0x2A -#define FLASH_OFFSET_NUM_ERASE_REGIONS 0x2C -#define FLASH_OFFSET_ERASE_REGIONS 0x2D -#define FLASH_OFFSET_PROTECT 0x02 +#define FLASH_OFFSET_WBTOUT 0x40 +#define FLASH_OFFSET_ETOUT 0x4A +#define FLASH_OFFSET_CETOUT 0x44 +#define FLASH_OFFSET_WMAX_TOUT 0x46 +#define FLASH_OFFSET_WBMAX_TOUT 0x48 +#define FLASH_OFFSET_EMAX_TOUT 0x4A +#define FLASH_OFFSET_CEMAX_TOUT 0x4C +#define FLASH_OFFSET_SIZE 0x4E +#define FLASH_OFFSET_INTERFACE 0x50 +#define FLASH_OFFSET_BUFFER_SIZE 0x54 +#define FLASH_OFFSET_NUM_ERASE_REGIONS 0x58 +#define FLASH_OFFSET_ERASE_REGIONS 0x5A +#define FLASH_OFFSET_PROTECT 0x04 #define FLASH_OFFSET_USER_PROTECTION 0x85 #define FLASH_OFFSET_INTEL_PROTECTION 0x81