| #include <common.h> |
| #include <stdio.h> |
| #include "cfi_flash.h" |
| |
| /*----------------------------------------------------------------------- |
| * Reverse the order of the erase regions in the CFI QRY structure. |
| * This is needed for chips that are either a) correctly detected as |
| * top-boot, or b) buggy. |
| */ |
| static void cfi_reverse_geometry(struct cfi_qry *qry) |
| { |
| unsigned int i, j; |
| u32 tmp; |
| |
| for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) { |
| tmp = qry->erase_region_info[i]; |
| qry->erase_region_info[i] = qry->erase_region_info[j]; |
| qry->erase_region_info[j] = tmp; |
| } |
| } |
| |
| 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); |
| } |
| |
| /* |
| * read jedec ids from device and set corresponding fields in info struct |
| * |
| * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct |
| * |
| */ |
| static void amd_read_jedec_ids (struct flash_info *info) |
| { |
| info->cmd_reset = AMD_CMD_RESET; |
| info->manufacturer_id = 0; |
| info->device_id = 0; |
| info->device_id2 = 0; |
| |
| /* calculate command offsets as in the Linux driver */ |
| info->addr_unlock1 = 0x555; |
| info->addr_unlock2 = 0x2AA; |
| |
| /* |
| * modify the unlock address if we are in compatibility mode |
| */ |
| if ( /* x8/x16 in x8 mode */ |
| ((info->chipwidth == FLASH_CFI_BY8) && |
| (info->interface == FLASH_CFI_X8X16)) || |
| /* x16/x32 in x16 mode */ |
| ((info->chipwidth == FLASH_CFI_BY16) && |
| (info->interface == FLASH_CFI_X16X32))) |
| { |
| info->addr_unlock1 = 0xaaa; |
| info->addr_unlock2 = 0x555; |
| } |
| |
| flash_write_cmd(info, 0, 0, info->cmd_reset); |
| flash_unlock_seq(info); |
| flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID); |
| udelay(1000); /* some flash are slow to respond */ |
| |
| info->manufacturer_id = jedec_read_mfr(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, |
| FLASH_OFFSET_DEVICE_ID2); |
| info->device_id2 <<= 8; |
| 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) |
| { |
| void *addr; |
| cfiword_t cword; |
| int retval; |
| |
| 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)) { |
| retval = flash_read16(addr) != flash_read16(addr); |
| } else if (bankwidth_is_4(info)) { |
| retval = flash_read32(addr) != flash_read32(addr); |
| } else if (bankwidth_is_8(info)) { |
| retval = ( (flash_read32( addr ) != flash_read32( addr )) || |
| (flash_read32(addr+4) != flash_read32(addr+4)) ); |
| } else |
| retval = 0; |
| |
| return retval; |
| } |
| |
| /* |
| * 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) |
| { |
| return flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); |
| } |
| |
| 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); |
| flash_unlock_seq(info); |
| flash_write_cmd (info, sect, 0, AMD_CMD_ERASE_SECTOR); |
| |
| return flash_status_check(info, sect, info->erase_blk_tout, "erase"); |
| } |
| |
| static void amd_flash_prepare_write(struct flash_info *info) |
| { |
| flash_unlock_seq(info); |
| flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE); |
| } |
| |
| #ifdef CONFIG_CFI_BUFFER_WRITE |
| static int amd_flash_write_cfibuffer (struct flash_info *info, ulong dest, const uchar * cp, |
| int len) |
| { |
| flash_sect_t sector; |
| int cnt; |
| int retcode; |
| void *src = (void*)cp; |
| void *dst = (void *)dest; |
| cfiword_t cword; |
| |
| sector = find_sector (info, dest); |
| |
| flash_unlock_seq(info); |
| flash_make_cmd (info, AMD_CMD_WRITE_TO_BUFFER, &cword); |
| flash_write_word(info, cword, (void *)dest); |
| |
| if (bankwidth_is_1(info)) { |
| cnt = len; |
| flash_write_cmd (info, sector, 0, (uchar) cnt - 1); |
| while (cnt-- > 0) { |
| flash_write8(flash_read8(src), dst); |
| src += 1, dst += 1; |
| } |
| } else if (bankwidth_is_2(info)) { |
| cnt = len >> 1; |
| flash_write_cmd (info, sector, 0, (uchar) cnt - 1); |
| while (cnt-- > 0) { |
| flash_write16(flash_read16(src), dst); |
| src += 2, dst += 2; |
| } |
| } else if (bankwidth_is_4(info)) { |
| cnt = len >> 2; |
| flash_write_cmd (info, sector, 0, (uchar) cnt - 1); |
| while (cnt-- > 0) { |
| flash_write32(flash_read32(src), dst); |
| src += 4, dst += 4; |
| } |
| } else if (bankwidth_is_8(info)) { |
| cnt = len >> 3; |
| flash_write_cmd (info, sector, 0, (uchar) cnt - 1); |
| while (cnt-- > 0) { |
| flash_write64(flash_read64(src), dst); |
| src += 8, dst += 8; |
| } |
| } |
| |
| flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM); |
| retcode = 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) |
| { |
| if (info->manufacturer_id != (uchar)ATM_MANUFACT) |
| return 0; |
| |
| if (prot) { |
| flash_unlock_seq (info); |
| flash_write_cmd (info, 0, info->addr_unlock1, |
| ATM_CMD_SOFTLOCK_START); |
| flash_unlock_seq (info); |
| flash_write_cmd (info, sector, 0, ATM_CMD_LOCK_SECT); |
| } else { |
| flash_write_cmd (info, 0, info->addr_unlock1, |
| AMD_CMD_UNLOCK_START); |
| if (info->device_id == ATM_ID_BV6416) |
| flash_write_cmd (info, sector, 0, |
| ATM_CMD_UNLOCK_SECT); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Manufacturer-specific quirks. Add workarounds for geometry |
| * reversal, etc. here. |
| */ |
| 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) { |
| /* reverse geometry if top boot part */ |
| if (info->cfi_version < 0x3131) { |
| /* 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) { |
| /* CFI >= 1.1, deduct from top/bottom flag */ |
| /* note: ext_addr is valid since cfi_version > 0 */ |
| cfi_reverse_geometry(qry); |
| } |
| } |
| } |
| |
| static void flash_fixup_atmel(struct flash_info *info, struct cfi_qry *qry) |
| { |
| int reverse_geometry = 0; |
| |
| /* Check the "top boot" bit in the PRI */ |
| if (info->ext_addr && !(flash_read_uchar(info, info->ext_addr + 6) & 1)) |
| reverse_geometry = 1; |
| |
| /* AT49BV6416(T) list the erase regions in the wrong order. |
| * However, the device ID is identical with the non-broken |
| * AT49BV642D since u-boot only reads the low byte (they |
| * differ in the high byte.) So leave out this fixup for now. |
| */ |
| if (info->device_id == 0xd6 || info->device_id == 0xd2) |
| reverse_geometry = !reverse_geometry; |
| |
| if (reverse_geometry) |
| cfi_reverse_geometry(qry); |
| } |
| |
| static void amd_flash_fixup(struct flash_info *info, struct cfi_qry *qry) |
| { |
| /* Do manufacturer-specific fixups */ |
| switch (info->manufacturer_id) { |
| case 0x0001: |
| flash_fixup_amd(info, qry); |
| break; |
| case 0x001f: |
| flash_fixup_atmel(info, qry); |
| break; |
| } |
| } |
| |
| struct cfi_cmd_set cfi_cmd_set_amd = { |
| .flash_write_cfibuffer = amd_flash_write_cfibuffer, |
| .flash_erase_one = amd_flash_erase_one, |
| .flash_is_busy = amd_flash_is_busy, |
| .flash_read_jedec_ids = amd_read_jedec_ids, |
| .flash_prepare_write = amd_flash_prepare_write, |
| .flash_status_check = flash_generic_status_check, |
| .flash_real_protect = amd_flash_real_protect, |
| .flash_fixup = amd_flash_fixup, |
| }; |
| |