18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Common Flash Interface support: 38c2ecf20Sopenharmony_ci * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * 2_by_8 routines added by Simon Munton 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * 4_by_16 work by Carolyn J. Smith 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * XIP support hooks by Vitaly Wool (based on code for Intel flash 148c2ecf20Sopenharmony_ci * by Nicolas Pitre) 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * 25/09/2008 Christopher Moore: TopBottom fixup for many Macronix with CFI V1.0 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * This code is GPL 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/types.h> 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/sched.h> 278c2ecf20Sopenharmony_ci#include <asm/io.h> 288c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/errno.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci#include <linux/delay.h> 338c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 348c2ecf20Sopenharmony_ci#include <linux/reboot.h> 358c2ecf20Sopenharmony_ci#include <linux/of.h> 368c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 378c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 388c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 398c2ecf20Sopenharmony_ci#include <linux/mtd/cfi.h> 408c2ecf20Sopenharmony_ci#include <linux/mtd/xip.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define AMD_BOOTLOC_BUG 438c2ecf20Sopenharmony_ci#define FORCE_WORD_WRITE 0 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MAX_RETRIES 3 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define SST49LF004B 0x0060 488c2ecf20Sopenharmony_ci#define SST49LF040B 0x0050 498c2ecf20Sopenharmony_ci#define SST49LF008A 0x005a 508c2ecf20Sopenharmony_ci#define AT49BV6416 0x00d6 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Status Register bit description. Used by flash devices that don't 548c2ecf20Sopenharmony_ci * support DQ polling (e.g. HyperFlash) 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#define CFI_SR_DRB BIT(7) 578c2ecf20Sopenharmony_ci#define CFI_SR_ESB BIT(5) 588c2ecf20Sopenharmony_ci#define CFI_SR_PSB BIT(4) 598c2ecf20Sopenharmony_ci#define CFI_SR_WBASB BIT(3) 608c2ecf20Sopenharmony_ci#define CFI_SR_SLSB BIT(1) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cienum cfi_quirks { 638c2ecf20Sopenharmony_ci CFI_QUIRK_DQ_TRUE_DATA = BIT(0), 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 678c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 688c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE 698c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 708c2ecf20Sopenharmony_ci#endif 718c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); 728c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); 738c2ecf20Sopenharmony_cistatic void cfi_amdstd_sync (struct mtd_info *); 748c2ecf20Sopenharmony_cistatic int cfi_amdstd_suspend (struct mtd_info *); 758c2ecf20Sopenharmony_cistatic void cfi_amdstd_resume (struct mtd_info *); 768c2ecf20Sopenharmony_cistatic int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); 778c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_fact_prot_info(struct mtd_info *, size_t, 788c2ecf20Sopenharmony_ci size_t *, struct otp_info *); 798c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_user_prot_info(struct mtd_info *, size_t, 808c2ecf20Sopenharmony_ci size_t *, struct otp_info *); 818c2ecf20Sopenharmony_cistatic int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 828c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t, 838c2ecf20Sopenharmony_ci size_t *, u_char *); 848c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t, 858c2ecf20Sopenharmony_ci size_t *, u_char *); 868c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t, 878c2ecf20Sopenharmony_ci size_t *, u_char *); 888c2ecf20Sopenharmony_cistatic int cfi_amdstd_lock_user_prot_reg(struct mtd_info *, loff_t, size_t); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, 918c2ecf20Sopenharmony_ci size_t *retlen, const u_char *buf); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void cfi_amdstd_destroy(struct mtd_info *); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0002(struct map_info *, int); 968c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_amdstd_setup (struct mtd_info *); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); 998c2ecf20Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); 1008c2ecf20Sopenharmony_ci#include "fwh_lock.h" 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 1038c2ecf20Sopenharmony_cistatic int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 1068c2ecf20Sopenharmony_cistatic int cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 1078c2ecf20Sopenharmony_cistatic int cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct mtd_chip_driver cfi_amdstd_chipdrv = { 1108c2ecf20Sopenharmony_ci .probe = NULL, /* Not usable directly */ 1118c2ecf20Sopenharmony_ci .destroy = cfi_amdstd_destroy, 1128c2ecf20Sopenharmony_ci .name = "cfi_cmdset_0002", 1138c2ecf20Sopenharmony_ci .module = THIS_MODULE 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Use status register to poll for Erase/write completion when DQ is not 1188c2ecf20Sopenharmony_ci * supported. This is indicated by Bit[1:0] of SoftwareFeatures field in 1198c2ecf20Sopenharmony_ci * CFI Primary Vendor-Specific Extended Query table 1.5 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic int cfi_use_status_reg(struct cfi_private *cfi) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct cfi_pri_amdstd *extp = cfi->cmdset_priv; 1248c2ecf20Sopenharmony_ci u8 poll_mask = CFI_POLL_STATUS_REG | CFI_POLL_DQ; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return extp && extp->MinorVersion >= '5' && 1278c2ecf20Sopenharmony_ci (extp->SoftwareFeatures & poll_mask) == CFI_POLL_STATUS_REG; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int cfi_check_err_status(struct map_info *map, struct flchip *chip, 1318c2ecf20Sopenharmony_ci unsigned long adr) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 1348c2ecf20Sopenharmony_ci map_word status; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!cfi_use_status_reg(cfi)) 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi, 1408c2ecf20Sopenharmony_ci cfi->device_type, NULL); 1418c2ecf20Sopenharmony_ci status = map_read(map, adr); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* The error bits are invalid while the chip's busy */ 1448c2ecf20Sopenharmony_ci if (!map_word_bitsset(map, status, CMD(CFI_SR_DRB))) 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x3a))) { 1488c2ecf20Sopenharmony_ci unsigned long chipstatus = MERGESTATUS(status); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (chipstatus & CFI_SR_ESB) 1518c2ecf20Sopenharmony_ci pr_err("%s erase operation failed, status %lx\n", 1528c2ecf20Sopenharmony_ci map->name, chipstatus); 1538c2ecf20Sopenharmony_ci if (chipstatus & CFI_SR_PSB) 1548c2ecf20Sopenharmony_ci pr_err("%s program operation failed, status %lx\n", 1558c2ecf20Sopenharmony_ci map->name, chipstatus); 1568c2ecf20Sopenharmony_ci if (chipstatus & CFI_SR_WBASB) 1578c2ecf20Sopenharmony_ci pr_err("%s buffer program command aborted, status %lx\n", 1588c2ecf20Sopenharmony_ci map->name, chipstatus); 1598c2ecf20Sopenharmony_ci if (chipstatus & CFI_SR_SLSB) 1608c2ecf20Sopenharmony_ci pr_err("%s sector write protected, status %lx\n", 1618c2ecf20Sopenharmony_ci map->name, chipstatus); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Erase/Program status bits are set on the operation failure */ 1648c2ecf20Sopenharmony_ci if (chipstatus & (CFI_SR_ESB | CFI_SR_PSB)) 1658c2ecf20Sopenharmony_ci return 1; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* #define DEBUG_CFI_FEATURES */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 1748c2ecf20Sopenharmony_cistatic void cfi_tell_features(struct cfi_pri_amdstd *extp) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci const char* erase_suspend[3] = { 1778c2ecf20Sopenharmony_ci "Not supported", "Read only", "Read/write" 1788c2ecf20Sopenharmony_ci }; 1798c2ecf20Sopenharmony_ci const char* top_bottom[6] = { 1808c2ecf20Sopenharmony_ci "No WP", "8x8KiB sectors at top & bottom, no WP", 1818c2ecf20Sopenharmony_ci "Bottom boot", "Top boot", 1828c2ecf20Sopenharmony_ci "Uniform, Bottom WP", "Uniform, Top WP" 1838c2ecf20Sopenharmony_ci }; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1); 1868c2ecf20Sopenharmony_ci printk(" Address sensitive unlock: %s\n", 1878c2ecf20Sopenharmony_ci (extp->SiliconRevision & 1) ? "Not required" : "Required"); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend)) 1908c2ecf20Sopenharmony_ci printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]); 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (extp->BlkProt == 0) 1958c2ecf20Sopenharmony_ci printk(" Block protection: Not supported\n"); 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci printk(" Block protection: %d sectors per group\n", extp->BlkProt); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci printk(" Temporary block unprotect: %s\n", 2018c2ecf20Sopenharmony_ci extp->TmpBlkUnprotect ? "Supported" : "Not supported"); 2028c2ecf20Sopenharmony_ci printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot); 2038c2ecf20Sopenharmony_ci printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps); 2048c2ecf20Sopenharmony_ci printk(" Burst mode: %s\n", 2058c2ecf20Sopenharmony_ci extp->BurstMode ? "Supported" : "Not supported"); 2068c2ecf20Sopenharmony_ci if (extp->PageMode == 0) 2078c2ecf20Sopenharmony_ci printk(" Page mode: Not supported\n"); 2088c2ecf20Sopenharmony_ci else 2098c2ecf20Sopenharmony_ci printk(" Page mode: %d word page\n", extp->PageMode << 2); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", 2128c2ecf20Sopenharmony_ci extp->VppMin >> 4, extp->VppMin & 0xf); 2138c2ecf20Sopenharmony_ci printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", 2148c2ecf20Sopenharmony_ci extp->VppMax >> 4, extp->VppMax & 0xf); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (extp->TopBottom < ARRAY_SIZE(top_bottom)) 2178c2ecf20Sopenharmony_ci printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]); 2188c2ecf20Sopenharmony_ci else 2198c2ecf20Sopenharmony_ci printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci#endif 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#ifdef AMD_BOOTLOC_BUG 2248c2ecf20Sopenharmony_ci/* Wheee. Bring me the head of someone at AMD. */ 2258c2ecf20Sopenharmony_cistatic void fixup_amd_bootblock(struct mtd_info *mtd) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2288c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2298c2ecf20Sopenharmony_ci struct cfi_pri_amdstd *extp = cfi->cmdset_priv; 2308c2ecf20Sopenharmony_ci __u8 major = extp->MajorVersion; 2318c2ecf20Sopenharmony_ci __u8 minor = extp->MinorVersion; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (((major << 8) | minor) < 0x3131) { 2348c2ecf20Sopenharmony_ci /* CFI version 1.0 => don't trust bootloc */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci pr_debug("%s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X\n", 2378c2ecf20Sopenharmony_ci map->name, cfi->mfr, cfi->id); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* AFAICS all 29LV400 with a bottom boot block have a device ID 2408c2ecf20Sopenharmony_ci * of 0x22BA in 16-bit mode and 0xBA in 8-bit mode. 2418c2ecf20Sopenharmony_ci * These were badly detected as they have the 0x80 bit set 2428c2ecf20Sopenharmony_ci * so treat them as a special case. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci if (((cfi->id == 0xBA) || (cfi->id == 0x22BA)) && 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Macronix added CFI to their 2nd generation 2478c2ecf20Sopenharmony_ci * MX29LV400C B/T but AFAICS no other 29LV400 (AMD, 2488c2ecf20Sopenharmony_ci * Fujitsu, Spansion, EON, ESI and older Macronix) 2498c2ecf20Sopenharmony_ci * has CFI. 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * Therefore also check the manufacturer. 2528c2ecf20Sopenharmony_ci * This reduces the risk of false detection due to 2538c2ecf20Sopenharmony_ci * the 8-bit device ID. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci (cfi->mfr == CFI_MFR_MACRONIX)) { 2568c2ecf20Sopenharmony_ci pr_debug("%s: Macronix MX29LV400C with bottom boot block" 2578c2ecf20Sopenharmony_ci " detected\n", map->name); 2588c2ecf20Sopenharmony_ci extp->TopBottom = 2; /* bottom boot */ 2598c2ecf20Sopenharmony_ci } else 2608c2ecf20Sopenharmony_ci if (cfi->id & 0x80) { 2618c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id); 2628c2ecf20Sopenharmony_ci extp->TopBottom = 3; /* top boot */ 2638c2ecf20Sopenharmony_ci } else { 2648c2ecf20Sopenharmony_ci extp->TopBottom = 2; /* bottom boot */ 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci pr_debug("%s: AMD CFI PRI V%c.%c has no boot block field;" 2688c2ecf20Sopenharmony_ci " deduced %s from Device ID\n", map->name, major, minor, 2698c2ecf20Sopenharmony_ci extp->TopBottom == 2 ? "bottom" : "top"); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci#endif 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE 2758c2ecf20Sopenharmony_cistatic void fixup_use_write_buffers(struct mtd_info *mtd) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2788c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2798c2ecf20Sopenharmony_ci if (cfi->cfiq->BufWriteTimeoutTyp) { 2808c2ecf20Sopenharmony_ci pr_debug("Using buffer write method\n"); 2818c2ecf20Sopenharmony_ci mtd->_write = cfi_amdstd_write_buffers; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci#endif /* !FORCE_WORD_WRITE */ 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/* Atmel chips don't use the same PRI format as AMD chips */ 2878c2ecf20Sopenharmony_cistatic void fixup_convert_atmel_pri(struct mtd_info *mtd) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2908c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2918c2ecf20Sopenharmony_ci struct cfi_pri_amdstd *extp = cfi->cmdset_priv; 2928c2ecf20Sopenharmony_ci struct cfi_pri_atmel atmel_pri; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci memcpy(&atmel_pri, extp, sizeof(atmel_pri)); 2958c2ecf20Sopenharmony_ci memset((char *)extp + 5, 0, sizeof(*extp) - 5); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x02) 2988c2ecf20Sopenharmony_ci extp->EraseSuspend = 2; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Some chips got it backwards... */ 3018c2ecf20Sopenharmony_ci if (cfi->id == AT49BV6416) { 3028c2ecf20Sopenharmony_ci if (atmel_pri.BottomBoot) 3038c2ecf20Sopenharmony_ci extp->TopBottom = 3; 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci extp->TopBottom = 2; 3068c2ecf20Sopenharmony_ci } else { 3078c2ecf20Sopenharmony_ci if (atmel_pri.BottomBoot) 3088c2ecf20Sopenharmony_ci extp->TopBottom = 2; 3098c2ecf20Sopenharmony_ci else 3108c2ecf20Sopenharmony_ci extp->TopBottom = 3; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* burst write mode not supported */ 3148c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutTyp = 0; 3158c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutMax = 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void fixup_use_secsi(struct mtd_info *mtd) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci /* Setup for chips with a secsi area */ 3218c2ecf20Sopenharmony_ci mtd->_read_user_prot_reg = cfi_amdstd_secsi_read; 3228c2ecf20Sopenharmony_ci mtd->_read_fact_prot_reg = cfi_amdstd_secsi_read; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void fixup_use_erase_chip(struct mtd_info *mtd) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3288c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3298c2ecf20Sopenharmony_ci if ((cfi->cfiq->NumEraseRegions == 1) && 3308c2ecf20Sopenharmony_ci ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) { 3318c2ecf20Sopenharmony_ci mtd->_erase = cfi_amdstd_erase_chip; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* 3378c2ecf20Sopenharmony_ci * Some Atmel chips (e.g. the AT49BV6416) power-up with all sectors 3388c2ecf20Sopenharmony_ci * locked by default. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_cistatic void fixup_use_atmel_lock(struct mtd_info *mtd) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci mtd->_lock = cfi_atmel_lock; 3438c2ecf20Sopenharmony_ci mtd->_unlock = cfi_atmel_unlock; 3448c2ecf20Sopenharmony_ci mtd->flags |= MTD_POWERUP_LOCK; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void fixup_old_sst_eraseregion(struct mtd_info *mtd) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3508c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * These flashes report two separate eraseblock regions based on the 3548c2ecf20Sopenharmony_ci * sector_erase-size and block_erase-size, although they both operate on the 3558c2ecf20Sopenharmony_ci * same memory. This is not allowed according to CFI, so we just pick the 3568c2ecf20Sopenharmony_ci * sector_erase-size. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci cfi->cfiq->NumEraseRegions = 1; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void fixup_sst39vf(struct mtd_info *mtd) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3648c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci fixup_old_sst_eraseregion(mtd); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci cfi->addr_unlock1 = 0x5555; 3698c2ecf20Sopenharmony_ci cfi->addr_unlock2 = 0x2AAA; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void fixup_sst39vf_rev_b(struct mtd_info *mtd) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3758c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci fixup_old_sst_eraseregion(mtd); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci cfi->addr_unlock1 = 0x555; 3808c2ecf20Sopenharmony_ci cfi->addr_unlock2 = 0x2AA; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci cfi->sector_erase_cmd = CMD(0x50); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3888c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci fixup_sst39vf_rev_b(mtd); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * CFI reports 1024 sectors (0x03ff+1) of 64KBytes (0x0100*256) where 3948c2ecf20Sopenharmony_ci * it should report a size of 8KBytes (0x0020*256). 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci cfi->cfiq->EraseRegionInfo[0] = 0x002003ff; 3978c2ecf20Sopenharmony_ci pr_warn("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n", 3988c2ecf20Sopenharmony_ci mtd->name); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic void fixup_s29gl064n_sectors(struct mtd_info *mtd) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 4048c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) { 4078c2ecf20Sopenharmony_ci cfi->cfiq->EraseRegionInfo[0] |= 0x0040; 4088c2ecf20Sopenharmony_ci pr_warn("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n", 4098c2ecf20Sopenharmony_ci mtd->name); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic void fixup_s29gl032n_sectors(struct mtd_info *mtd) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 4168c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) { 4198c2ecf20Sopenharmony_ci cfi->cfiq->EraseRegionInfo[1] &= ~0x0040; 4208c2ecf20Sopenharmony_ci pr_warn("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n", 4218c2ecf20Sopenharmony_ci mtd->name); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic void fixup_s29ns512p_sectors(struct mtd_info *mtd) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 4288c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * S29NS512P flash uses more than 8bits to report number of sectors, 4328c2ecf20Sopenharmony_ci * which is not permitted by CFI. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci cfi->cfiq->EraseRegionInfo[0] = 0x020001ff; 4358c2ecf20Sopenharmony_ci pr_warn("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n", 4368c2ecf20Sopenharmony_ci mtd->name); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void fixup_quirks(struct mtd_info *mtd) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 4428c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_AMD && cfi->id == 0x0c01) 4458c2ecf20Sopenharmony_ci cfi->quirks |= CFI_QUIRK_DQ_TRUE_DATA; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* Used to fix CFI-Tables of chips without Extended Query Tables */ 4498c2ecf20Sopenharmony_cistatic struct cfi_fixup cfi_nopri_fixup_table[] = { 4508c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */ 4518c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x234b, fixup_sst39vf }, /* SST39VF1601 */ 4528c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x235a, fixup_sst39vf }, /* SST39VF3202 */ 4538c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x235b, fixup_sst39vf }, /* SST39VF3201 */ 4548c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x235c, fixup_sst39vf_rev_b }, /* SST39VF3202B */ 4558c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x235d, fixup_sst39vf_rev_b }, /* SST39VF3201B */ 4568c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x236c, fixup_sst39vf_rev_b }, /* SST39VF6402B */ 4578c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x236d, fixup_sst39vf_rev_b }, /* SST39VF6401B */ 4588c2ecf20Sopenharmony_ci { 0, 0, NULL } 4598c2ecf20Sopenharmony_ci}; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic struct cfi_fixup cfi_fixup_table[] = { 4628c2ecf20Sopenharmony_ci { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri }, 4638c2ecf20Sopenharmony_ci#ifdef AMD_BOOTLOC_BUG 4648c2ecf20Sopenharmony_ci { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock }, 4658c2ecf20Sopenharmony_ci { CFI_MFR_AMIC, CFI_ID_ANY, fixup_amd_bootblock }, 4668c2ecf20Sopenharmony_ci { CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock }, 4678c2ecf20Sopenharmony_ci#endif 4688c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x0050, fixup_use_secsi }, 4698c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x0053, fixup_use_secsi }, 4708c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x0055, fixup_use_secsi }, 4718c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x0056, fixup_use_secsi }, 4728c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x005C, fixup_use_secsi }, 4738c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x005F, fixup_use_secsi }, 4748c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors }, 4758c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors }, 4768c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors }, 4778c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors }, 4788c2ecf20Sopenharmony_ci { CFI_MFR_AMD, 0x3f00, fixup_s29ns512p_sectors }, 4798c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x536a, fixup_sst38vf640x_sectorsize }, /* SST38VF6402 */ 4808c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x536b, fixup_sst38vf640x_sectorsize }, /* SST38VF6401 */ 4818c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x536c, fixup_sst38vf640x_sectorsize }, /* SST38VF6404 */ 4828c2ecf20Sopenharmony_ci { CFI_MFR_SST, 0x536d, fixup_sst38vf640x_sectorsize }, /* SST38VF6403 */ 4838c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE 4848c2ecf20Sopenharmony_ci { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers }, 4858c2ecf20Sopenharmony_ci#endif 4868c2ecf20Sopenharmony_ci { CFI_MFR_ANY, CFI_ID_ANY, fixup_quirks }, 4878c2ecf20Sopenharmony_ci { 0, 0, NULL } 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_cistatic struct cfi_fixup jedec_fixup_table[] = { 4908c2ecf20Sopenharmony_ci { CFI_MFR_SST, SST49LF004B, fixup_use_fwh_lock }, 4918c2ecf20Sopenharmony_ci { CFI_MFR_SST, SST49LF040B, fixup_use_fwh_lock }, 4928c2ecf20Sopenharmony_ci { CFI_MFR_SST, SST49LF008A, fixup_use_fwh_lock }, 4938c2ecf20Sopenharmony_ci { 0, 0, NULL } 4948c2ecf20Sopenharmony_ci}; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic struct cfi_fixup fixup_table[] = { 4978c2ecf20Sopenharmony_ci /* The CFI vendor ids and the JEDEC vendor IDs appear 4988c2ecf20Sopenharmony_ci * to be common. It is like the devices id's are as 4998c2ecf20Sopenharmony_ci * well. This table is to pick all cases where 5008c2ecf20Sopenharmony_ci * we know that is the case. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip }, 5038c2ecf20Sopenharmony_ci { CFI_MFR_ATMEL, AT49BV6416, fixup_use_atmel_lock }, 5048c2ecf20Sopenharmony_ci { 0, 0, NULL } 5058c2ecf20Sopenharmony_ci}; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void cfi_fixup_major_minor(struct cfi_private *cfi, 5098c2ecf20Sopenharmony_ci struct cfi_pri_amdstd *extp) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_SAMSUNG) { 5128c2ecf20Sopenharmony_ci if ((extp->MajorVersion == '0' && extp->MinorVersion == '0') || 5138c2ecf20Sopenharmony_ci (extp->MajorVersion == '3' && extp->MinorVersion == '3')) { 5148c2ecf20Sopenharmony_ci /* 5158c2ecf20Sopenharmony_ci * Samsung K8P2815UQB and K8D6x16UxM chips 5168c2ecf20Sopenharmony_ci * report major=0 / minor=0. 5178c2ecf20Sopenharmony_ci * K8D3x16UxC chips report major=3 / minor=3. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci printk(KERN_NOTICE " Fixing Samsung's Amd/Fujitsu" 5208c2ecf20Sopenharmony_ci " Extended Query version to 1.%c\n", 5218c2ecf20Sopenharmony_ci extp->MinorVersion); 5228c2ecf20Sopenharmony_ci extp->MajorVersion = '1'; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * SST 38VF640x chips report major=0xFF / minor=0xFF. 5288c2ecf20Sopenharmony_ci */ 5298c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_SST && (cfi->id >> 4) == 0x0536) { 5308c2ecf20Sopenharmony_ci extp->MajorVersion = '1'; 5318c2ecf20Sopenharmony_ci extp->MinorVersion = '0'; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int is_m29ew(struct cfi_private *cfi) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_INTEL && 5388c2ecf20Sopenharmony_ci ((cfi->device_type == CFI_DEVICETYPE_X8 && (cfi->id & 0xff) == 0x7e) || 5398c2ecf20Sopenharmony_ci (cfi->device_type == CFI_DEVICETYPE_X16 && cfi->id == 0x227e))) 5408c2ecf20Sopenharmony_ci return 1; 5418c2ecf20Sopenharmony_ci return 0; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/* 5458c2ecf20Sopenharmony_ci * From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 20: 5468c2ecf20Sopenharmony_ci * Some revisions of the M29EW suffer from erase suspend hang ups. In 5478c2ecf20Sopenharmony_ci * particular, it can occur when the sequence 5488c2ecf20Sopenharmony_ci * Erase Confirm -> Suspend -> Program -> Resume 5498c2ecf20Sopenharmony_ci * causes a lockup due to internal timing issues. The consequence is that the 5508c2ecf20Sopenharmony_ci * erase cannot be resumed without inserting a dummy command after programming 5518c2ecf20Sopenharmony_ci * and prior to resuming. [...] The work-around is to issue a dummy write cycle 5528c2ecf20Sopenharmony_ci * that writes an F0 command code before the RESUME command. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_cistatic void cfi_fixup_m29ew_erase_suspend(struct map_info *map, 5558c2ecf20Sopenharmony_ci unsigned long adr) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 5588c2ecf20Sopenharmony_ci /* before resume, insert a dummy 0xF0 cycle for Micron M29EW devices */ 5598c2ecf20Sopenharmony_ci if (is_m29ew(cfi)) 5608c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), adr); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* 5648c2ecf20Sopenharmony_ci * From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 22: 5658c2ecf20Sopenharmony_ci * 5668c2ecf20Sopenharmony_ci * Some revisions of the M29EW (for example, A1 and A2 step revisions) 5678c2ecf20Sopenharmony_ci * are affected by a problem that could cause a hang up when an ERASE SUSPEND 5688c2ecf20Sopenharmony_ci * command is issued after an ERASE RESUME operation without waiting for a 5698c2ecf20Sopenharmony_ci * minimum delay. The result is that once the ERASE seems to be completed 5708c2ecf20Sopenharmony_ci * (no bits are toggling), the contents of the Flash memory block on which 5718c2ecf20Sopenharmony_ci * the erase was ongoing could be inconsistent with the expected values 5728c2ecf20Sopenharmony_ci * (typically, the array value is stuck to the 0xC0, 0xC4, 0x80, or 0x84 5738c2ecf20Sopenharmony_ci * values), causing a consequent failure of the ERASE operation. 5748c2ecf20Sopenharmony_ci * The occurrence of this issue could be high, especially when file system 5758c2ecf20Sopenharmony_ci * operations on the Flash are intensive. As a result, it is recommended 5768c2ecf20Sopenharmony_ci * that a patch be applied. Intensive file system operations can cause many 5778c2ecf20Sopenharmony_ci * calls to the garbage routine to free Flash space (also by erasing physical 5788c2ecf20Sopenharmony_ci * Flash blocks) and as a result, many consecutive SUSPEND and RESUME 5798c2ecf20Sopenharmony_ci * commands can occur. The problem disappears when a delay is inserted after 5808c2ecf20Sopenharmony_ci * the RESUME command by using the udelay() function available in Linux. 5818c2ecf20Sopenharmony_ci * The DELAY value must be tuned based on the customer's platform. 5828c2ecf20Sopenharmony_ci * The maximum value that fixes the problem in all cases is 500us. 5838c2ecf20Sopenharmony_ci * But, in our experience, a delay of 30 µs to 50 µs is sufficient 5848c2ecf20Sopenharmony_ci * in most cases. 5858c2ecf20Sopenharmony_ci * We have chosen 500µs because this latency is acceptable. 5868c2ecf20Sopenharmony_ci */ 5878c2ecf20Sopenharmony_cistatic void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci /* 5908c2ecf20Sopenharmony_ci * Resolving the Delay After Resume Issue see Micron TN-13-07 5918c2ecf20Sopenharmony_ci * Worst case delay must be 500µs but 30-50µs should be ok as well 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci if (is_m29ew(cfi)) 5948c2ecf20Sopenharmony_ci cfi_udelay(500); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 6008c2ecf20Sopenharmony_ci struct device_node __maybe_unused *np = map->device_node; 6018c2ecf20Sopenharmony_ci struct mtd_info *mtd; 6028c2ecf20Sopenharmony_ci int i; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); 6058c2ecf20Sopenharmony_ci if (!mtd) 6068c2ecf20Sopenharmony_ci return NULL; 6078c2ecf20Sopenharmony_ci mtd->priv = map; 6088c2ecf20Sopenharmony_ci mtd->type = MTD_NORFLASH; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* Fill in the default mtd operations */ 6118c2ecf20Sopenharmony_ci mtd->_erase = cfi_amdstd_erase_varsize; 6128c2ecf20Sopenharmony_ci mtd->_write = cfi_amdstd_write_words; 6138c2ecf20Sopenharmony_ci mtd->_read = cfi_amdstd_read; 6148c2ecf20Sopenharmony_ci mtd->_sync = cfi_amdstd_sync; 6158c2ecf20Sopenharmony_ci mtd->_suspend = cfi_amdstd_suspend; 6168c2ecf20Sopenharmony_ci mtd->_resume = cfi_amdstd_resume; 6178c2ecf20Sopenharmony_ci mtd->_read_user_prot_reg = cfi_amdstd_read_user_prot_reg; 6188c2ecf20Sopenharmony_ci mtd->_read_fact_prot_reg = cfi_amdstd_read_fact_prot_reg; 6198c2ecf20Sopenharmony_ci mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info; 6208c2ecf20Sopenharmony_ci mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info; 6218c2ecf20Sopenharmony_ci mtd->_write_user_prot_reg = cfi_amdstd_write_user_prot_reg; 6228c2ecf20Sopenharmony_ci mtd->_lock_user_prot_reg = cfi_amdstd_lock_user_prot_reg; 6238c2ecf20Sopenharmony_ci mtd->flags = MTD_CAP_NORFLASH; 6248c2ecf20Sopenharmony_ci mtd->name = map->name; 6258c2ecf20Sopenharmony_ci mtd->writesize = 1; 6268c2ecf20Sopenharmony_ci mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci pr_debug("MTD %s(): write buffer size %d\n", __func__, 6298c2ecf20Sopenharmony_ci mtd->writebufsize); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci mtd->_panic_write = cfi_amdstd_panic_write; 6328c2ecf20Sopenharmony_ci mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (cfi->cfi_mode==CFI_MODE_CFI){ 6358c2ecf20Sopenharmony_ci unsigned char bootloc; 6368c2ecf20Sopenharmony_ci __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; 6378c2ecf20Sopenharmony_ci struct cfi_pri_amdstd *extp; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); 6408c2ecf20Sopenharmony_ci if (extp) { 6418c2ecf20Sopenharmony_ci /* 6428c2ecf20Sopenharmony_ci * It's a real CFI chip, not one for which the probe 6438c2ecf20Sopenharmony_ci * routine faked a CFI structure. 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_ci cfi_fixup_major_minor(cfi, extp); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* 6488c2ecf20Sopenharmony_ci * Valid primary extension versions are: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 6498c2ecf20Sopenharmony_ci * see: http://cs.ozerki.net/zap/pub/axim-x5/docs/cfi_r20.pdf, page 19 6508c2ecf20Sopenharmony_ci * http://www.spansion.com/Support/AppNotes/cfi_100_20011201.pdf 6518c2ecf20Sopenharmony_ci * http://www.spansion.com/Support/Datasheets/s29ws-p_00_a12_e.pdf 6528c2ecf20Sopenharmony_ci * http://www.spansion.com/Support/Datasheets/S29GL_128S_01GS_00_02_e.pdf 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_ci if (extp->MajorVersion != '1' || 6558c2ecf20Sopenharmony_ci (extp->MajorVersion == '1' && (extp->MinorVersion < '0' || extp->MinorVersion > '5'))) { 6568c2ecf20Sopenharmony_ci printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " 6578c2ecf20Sopenharmony_ci "version %c.%c (%#02x/%#02x).\n", 6588c2ecf20Sopenharmony_ci extp->MajorVersion, extp->MinorVersion, 6598c2ecf20Sopenharmony_ci extp->MajorVersion, extp->MinorVersion); 6608c2ecf20Sopenharmony_ci kfree(extp); 6618c2ecf20Sopenharmony_ci kfree(mtd); 6628c2ecf20Sopenharmony_ci return NULL; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci printk(KERN_INFO " Amd/Fujitsu Extended Query version %c.%c.\n", 6668c2ecf20Sopenharmony_ci extp->MajorVersion, extp->MinorVersion); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* Install our own private info structure */ 6698c2ecf20Sopenharmony_ci cfi->cmdset_priv = extp; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* Apply cfi device specific fixups */ 6728c2ecf20Sopenharmony_ci cfi_fixup(mtd, cfi_fixup_table); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 6758c2ecf20Sopenharmony_ci /* Tell the user about it in lots of lovely detail */ 6768c2ecf20Sopenharmony_ci cfi_tell_features(extp); 6778c2ecf20Sopenharmony_ci#endif 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6808c2ecf20Sopenharmony_ci if (np && of_property_read_bool( 6818c2ecf20Sopenharmony_ci np, "use-advanced-sector-protection") 6828c2ecf20Sopenharmony_ci && extp->BlkProtUnprot == 8) { 6838c2ecf20Sopenharmony_ci printk(KERN_INFO " Advanced Sector Protection (PPB Locking) supported\n"); 6848c2ecf20Sopenharmony_ci mtd->_lock = cfi_ppb_lock; 6858c2ecf20Sopenharmony_ci mtd->_unlock = cfi_ppb_unlock; 6868c2ecf20Sopenharmony_ci mtd->_is_locked = cfi_ppb_is_locked; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci#endif 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci bootloc = extp->TopBottom; 6918c2ecf20Sopenharmony_ci if ((bootloc < 2) || (bootloc > 5)) { 6928c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: CFI contains unrecognised boot " 6938c2ecf20Sopenharmony_ci "bank location (%d). Assuming bottom.\n", 6948c2ecf20Sopenharmony_ci map->name, bootloc); 6958c2ecf20Sopenharmony_ci bootloc = 2; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { 6998c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Swapping erase regions for top-boot CFI table.\n", map->name); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) { 7028c2ecf20Sopenharmony_ci int j = (cfi->cfiq->NumEraseRegions-1)-i; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci swap(cfi->cfiq->EraseRegionInfo[i], 7058c2ecf20Sopenharmony_ci cfi->cfiq->EraseRegionInfo[j]); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci /* Set the default CFI lock/unlock addresses */ 7098c2ecf20Sopenharmony_ci cfi->addr_unlock1 = 0x555; 7108c2ecf20Sopenharmony_ci cfi->addr_unlock2 = 0x2aa; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci cfi_fixup(mtd, cfi_nopri_fixup_table); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (!cfi->addr_unlock1 || !cfi->addr_unlock2) { 7158c2ecf20Sopenharmony_ci kfree(mtd); 7168c2ecf20Sopenharmony_ci return NULL; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci } /* CFI mode */ 7208c2ecf20Sopenharmony_ci else if (cfi->cfi_mode == CFI_MODE_JEDEC) { 7218c2ecf20Sopenharmony_ci /* Apply jedec specific fixups */ 7228c2ecf20Sopenharmony_ci cfi_fixup(mtd, jedec_fixup_table); 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci /* Apply generic fixups */ 7258c2ecf20Sopenharmony_ci cfi_fixup(mtd, fixup_table); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci for (i=0; i< cfi->numchips; i++) { 7288c2ecf20Sopenharmony_ci cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; 7298c2ecf20Sopenharmony_ci cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; 7308c2ecf20Sopenharmony_ci cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp; 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * First calculate the timeout max according to timeout field 7338c2ecf20Sopenharmony_ci * of struct cfi_ident that probed from chip's CFI aera, if 7348c2ecf20Sopenharmony_ci * available. Specify a minimum of 2000us, in case the CFI data 7358c2ecf20Sopenharmony_ci * is wrong. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci if (cfi->cfiq->BufWriteTimeoutTyp && 7388c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutMax) 7398c2ecf20Sopenharmony_ci cfi->chips[i].buffer_write_time_max = 7408c2ecf20Sopenharmony_ci 1 << (cfi->cfiq->BufWriteTimeoutTyp + 7418c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutMax); 7428c2ecf20Sopenharmony_ci else 7438c2ecf20Sopenharmony_ci cfi->chips[i].buffer_write_time_max = 0; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci cfi->chips[i].buffer_write_time_max = 7468c2ecf20Sopenharmony_ci max(cfi->chips[i].buffer_write_time_max, 2000); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci cfi->chips[i].ref_point_counter = 0; 7498c2ecf20Sopenharmony_ci init_waitqueue_head(&(cfi->chips[i].wq)); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci map->fldrv = &cfi_amdstd_chipdrv; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return cfi_amdstd_setup(mtd); 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); 7578c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); 7588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0002); 7598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0006); 7608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0701); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 7658c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 7668c2ecf20Sopenharmony_ci unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; 7678c2ecf20Sopenharmony_ci unsigned long offset = 0; 7688c2ecf20Sopenharmony_ci int i,j; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci printk(KERN_NOTICE "number of %s chips: %d\n", 7718c2ecf20Sopenharmony_ci (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); 7728c2ecf20Sopenharmony_ci /* Select the correct geometry setup */ 7738c2ecf20Sopenharmony_ci mtd->size = devsize * cfi->numchips; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; 7768c2ecf20Sopenharmony_ci mtd->eraseregions = kmalloc_array(mtd->numeraseregions, 7778c2ecf20Sopenharmony_ci sizeof(struct mtd_erase_region_info), 7788c2ecf20Sopenharmony_ci GFP_KERNEL); 7798c2ecf20Sopenharmony_ci if (!mtd->eraseregions) 7808c2ecf20Sopenharmony_ci goto setup_err; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { 7838c2ecf20Sopenharmony_ci unsigned long ernum, ersize; 7848c2ecf20Sopenharmony_ci ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; 7858c2ecf20Sopenharmony_ci ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (mtd->erasesize < ersize) { 7888c2ecf20Sopenharmony_ci mtd->erasesize = ersize; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci for (j=0; j<cfi->numchips; j++) { 7918c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; 7928c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; 7938c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci offset += (ersize * ernum); 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci if (offset != devsize) { 7988c2ecf20Sopenharmony_ci /* Argh */ 7998c2ecf20Sopenharmony_ci printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); 8008c2ecf20Sopenharmony_ci goto setup_err; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 8048c2ecf20Sopenharmony_ci register_reboot_notifier(&mtd->reboot_notifier); 8058c2ecf20Sopenharmony_ci return mtd; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci setup_err: 8088c2ecf20Sopenharmony_ci kfree(mtd->eraseregions); 8098c2ecf20Sopenharmony_ci kfree(mtd); 8108c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 8118c2ecf20Sopenharmony_ci return NULL; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci/* 8158c2ecf20Sopenharmony_ci * Return true if the chip is ready and has the correct value. 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * Ready is one of: read mode, query mode, erase-suspend-read mode (in any 8188c2ecf20Sopenharmony_ci * non-suspended sector) and is indicated by no toggle bits toggling. 8198c2ecf20Sopenharmony_ci * 8208c2ecf20Sopenharmony_ci * Error are indicated by toggling bits or bits held with the wrong value, 8218c2ecf20Sopenharmony_ci * or with bits toggling. 8228c2ecf20Sopenharmony_ci * 8238c2ecf20Sopenharmony_ci * Note that anything more complicated than checking if no bits are toggling 8248c2ecf20Sopenharmony_ci * (including checking DQ5 for an error status) is tricky to get working 8258c2ecf20Sopenharmony_ci * correctly and is therefore not done (particularly with interleaved chips 8268c2ecf20Sopenharmony_ci * as each chip must be checked independently of the others). 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_cistatic int __xipram chip_ready(struct map_info *map, struct flchip *chip, 8298c2ecf20Sopenharmony_ci unsigned long addr, map_word *expected) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 8328c2ecf20Sopenharmony_ci map_word d, t; 8338c2ecf20Sopenharmony_ci int ret; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (cfi_use_status_reg(cfi)) { 8368c2ecf20Sopenharmony_ci map_word ready = CMD(CFI_SR_DRB); 8378c2ecf20Sopenharmony_ci /* 8388c2ecf20Sopenharmony_ci * For chips that support status register, check device 8398c2ecf20Sopenharmony_ci * ready bit 8408c2ecf20Sopenharmony_ci */ 8418c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi, 8428c2ecf20Sopenharmony_ci cfi->device_type, NULL); 8438c2ecf20Sopenharmony_ci t = map_read(map, addr); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return map_word_andequal(map, t, ready, ready); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci d = map_read(map, addr); 8498c2ecf20Sopenharmony_ci t = map_read(map, addr); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = map_word_equal(map, d, t); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (!ret || !expected) 8548c2ecf20Sopenharmony_ci return ret; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return map_word_equal(map, t, *expected); 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int __xipram chip_good(struct map_info *map, struct flchip *chip, 8608c2ecf20Sopenharmony_ci unsigned long addr, map_word *expected) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 8638c2ecf20Sopenharmony_ci map_word *datum = expected; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (cfi->quirks & CFI_QUIRK_DQ_TRUE_DATA) 8668c2ecf20Sopenharmony_ci datum = NULL; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci return chip_ready(map, chip, addr, datum); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 8748c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 8758c2ecf20Sopenharmony_ci unsigned long timeo; 8768c2ecf20Sopenharmony_ci struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci resettime: 8798c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 8808c2ecf20Sopenharmony_ci retry: 8818c2ecf20Sopenharmony_ci switch (chip->state) { 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci case FL_STATUS: 8848c2ecf20Sopenharmony_ci for (;;) { 8858c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, NULL)) 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 8898c2ecf20Sopenharmony_ci printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); 8908c2ecf20Sopenharmony_ci return -EIO; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8938c2ecf20Sopenharmony_ci cfi_udelay(1); 8948c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 8958c2ecf20Sopenharmony_ci /* Someone else might have been playing with it. */ 8968c2ecf20Sopenharmony_ci goto retry; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci case FL_READY: 9008c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 9018c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 9028c2ecf20Sopenharmony_ci return 0; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci case FL_ERASING: 9058c2ecf20Sopenharmony_ci if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) || 9068c2ecf20Sopenharmony_ci !(mode == FL_READY || mode == FL_POINT || 9078c2ecf20Sopenharmony_ci (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) 9088c2ecf20Sopenharmony_ci goto sleep; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Do not allow suspend iff read/write to EB address */ 9118c2ecf20Sopenharmony_ci if ((adr & chip->in_progress_block_mask) == 9128c2ecf20Sopenharmony_ci chip->in_progress_block_addr) 9138c2ecf20Sopenharmony_ci goto sleep; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* Erase suspend */ 9168c2ecf20Sopenharmony_ci /* It's harmless to issue the Erase-Suspend and Erase-Resume 9178c2ecf20Sopenharmony_ci * commands when the erase algorithm isn't in progress. */ 9188c2ecf20Sopenharmony_ci map_write(map, CMD(0xB0), chip->in_progress_block_addr); 9198c2ecf20Sopenharmony_ci chip->oldstate = FL_ERASING; 9208c2ecf20Sopenharmony_ci chip->state = FL_ERASE_SUSPENDING; 9218c2ecf20Sopenharmony_ci chip->erase_suspended = 1; 9228c2ecf20Sopenharmony_ci for (;;) { 9238c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, NULL)) 9248c2ecf20Sopenharmony_ci break; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 9278c2ecf20Sopenharmony_ci /* Should have suspended the erase by now. 9288c2ecf20Sopenharmony_ci * Send an Erase-Resume command as either 9298c2ecf20Sopenharmony_ci * there was an error (so leave the erase 9308c2ecf20Sopenharmony_ci * routine to recover from it) or we trying to 9318c2ecf20Sopenharmony_ci * use the erase-in-progress sector. */ 9328c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 9338c2ecf20Sopenharmony_ci printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__); 9348c2ecf20Sopenharmony_ci return -EIO; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 9388c2ecf20Sopenharmony_ci cfi_udelay(1); 9398c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9408c2ecf20Sopenharmony_ci /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. 9418c2ecf20Sopenharmony_ci So we can just loop here. */ 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci chip->state = FL_READY; 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci case FL_XIP_WHILE_ERASING: 9478c2ecf20Sopenharmony_ci if (mode != FL_READY && mode != FL_POINT && 9488c2ecf20Sopenharmony_ci (!cfip || !(cfip->EraseSuspend&2))) 9498c2ecf20Sopenharmony_ci goto sleep; 9508c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 9518c2ecf20Sopenharmony_ci chip->state = FL_READY; 9528c2ecf20Sopenharmony_ci return 0; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci case FL_SHUTDOWN: 9558c2ecf20Sopenharmony_ci /* The machine is rebooting */ 9568c2ecf20Sopenharmony_ci return -EIO; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci case FL_POINT: 9598c2ecf20Sopenharmony_ci /* Only if there's no operation suspended... */ 9608c2ecf20Sopenharmony_ci if (mode == FL_READY && chip->oldstate == FL_READY) 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci fallthrough; 9638c2ecf20Sopenharmony_ci default: 9648c2ecf20Sopenharmony_ci sleep: 9658c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 9668c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 9678c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 9688c2ecf20Sopenharmony_ci schedule(); 9698c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 9708c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9718c2ecf20Sopenharmony_ci goto resettime; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci switch(chip->oldstate) { 9818c2ecf20Sopenharmony_ci case FL_ERASING: 9828c2ecf20Sopenharmony_ci cfi_fixup_m29ew_erase_suspend(map, 9838c2ecf20Sopenharmony_ci chip->in_progress_block_addr); 9848c2ecf20Sopenharmony_ci map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr); 9858c2ecf20Sopenharmony_ci cfi_fixup_m29ew_delay_after_resume(cfi); 9868c2ecf20Sopenharmony_ci chip->oldstate = FL_READY; 9878c2ecf20Sopenharmony_ci chip->state = FL_ERASING; 9888c2ecf20Sopenharmony_ci break; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci case FL_XIP_WHILE_ERASING: 9918c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 9928c2ecf20Sopenharmony_ci chip->oldstate = FL_READY; 9938c2ecf20Sopenharmony_ci break; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci case FL_READY: 9968c2ecf20Sopenharmony_ci case FL_STATUS: 9978c2ecf20Sopenharmony_ci break; 9988c2ecf20Sopenharmony_ci default: 9998c2ecf20Sopenharmony_ci printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci wake_up(&chip->wq); 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_XIP 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/* 10078c2ecf20Sopenharmony_ci * No interrupt what so ever can be serviced while the flash isn't in array 10088c2ecf20Sopenharmony_ci * mode. This is ensured by the xip_disable() and xip_enable() functions 10098c2ecf20Sopenharmony_ci * enclosing any code path where the flash is known not to be in array mode. 10108c2ecf20Sopenharmony_ci * And within a XIP disabled code path, only functions marked with __xipram 10118c2ecf20Sopenharmony_ci * may be called and nothing else (it's a good thing to inspect generated 10128c2ecf20Sopenharmony_ci * assembly to make sure inline functions were actually inlined and that gcc 10138c2ecf20Sopenharmony_ci * didn't emit calls to its own support functions). Also configuring MTD CFI 10148c2ecf20Sopenharmony_ci * support to a single buswidth and a single interleave is also recommended. 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic void xip_disable(struct map_info *map, struct flchip *chip, 10188c2ecf20Sopenharmony_ci unsigned long adr) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci /* TODO: chips with no XIP use should ignore and return */ 10218c2ecf20Sopenharmony_ci (void) map_read(map, adr); /* ensure mmu mapping is up to date */ 10228c2ecf20Sopenharmony_ci local_irq_disable(); 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic void __xipram xip_enable(struct map_info *map, struct flchip *chip, 10268c2ecf20Sopenharmony_ci unsigned long adr) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (chip->state != FL_POINT && chip->state != FL_READY) { 10318c2ecf20Sopenharmony_ci map_write(map, CMD(0xf0), adr); 10328c2ecf20Sopenharmony_ci chip->state = FL_READY; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci (void) map_read(map, adr); 10358c2ecf20Sopenharmony_ci xip_iprefetch(); 10368c2ecf20Sopenharmony_ci local_irq_enable(); 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci/* 10408c2ecf20Sopenharmony_ci * When a delay is required for the flash operation to complete, the 10418c2ecf20Sopenharmony_ci * xip_udelay() function is polling for both the given timeout and pending 10428c2ecf20Sopenharmony_ci * (but still masked) hardware interrupts. Whenever there is an interrupt 10438c2ecf20Sopenharmony_ci * pending then the flash erase operation is suspended, array mode restored 10448c2ecf20Sopenharmony_ci * and interrupts unmasked. Task scheduling might also happen at that 10458c2ecf20Sopenharmony_ci * point. The CPU eventually returns from the interrupt or the call to 10468c2ecf20Sopenharmony_ci * schedule() and the suspended flash operation is resumed for the remaining 10478c2ecf20Sopenharmony_ci * of the delay period. 10488c2ecf20Sopenharmony_ci * 10498c2ecf20Sopenharmony_ci * Warning: this function _will_ fool interrupt latency tracing tools. 10508c2ecf20Sopenharmony_ci */ 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic void __xipram xip_udelay(struct map_info *map, struct flchip *chip, 10538c2ecf20Sopenharmony_ci unsigned long adr, int usec) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 10568c2ecf20Sopenharmony_ci struct cfi_pri_amdstd *extp = cfi->cmdset_priv; 10578c2ecf20Sopenharmony_ci map_word status, OK = CMD(0x80); 10588c2ecf20Sopenharmony_ci unsigned long suspended, start = xip_currtime(); 10598c2ecf20Sopenharmony_ci flstate_t oldstate; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci do { 10628c2ecf20Sopenharmony_ci cpu_relax(); 10638c2ecf20Sopenharmony_ci if (xip_irqpending() && extp && 10648c2ecf20Sopenharmony_ci ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) && 10658c2ecf20Sopenharmony_ci (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { 10668c2ecf20Sopenharmony_ci /* 10678c2ecf20Sopenharmony_ci * Let's suspend the erase operation when supported. 10688c2ecf20Sopenharmony_ci * Note that we currently don't try to suspend 10698c2ecf20Sopenharmony_ci * interleaved chips if there is already another 10708c2ecf20Sopenharmony_ci * operation suspended (imagine what happens 10718c2ecf20Sopenharmony_ci * when one chip was already done with the current 10728c2ecf20Sopenharmony_ci * operation while another chip suspended it, then 10738c2ecf20Sopenharmony_ci * we resume the whole thing at once). Yes, it 10748c2ecf20Sopenharmony_ci * can happen! 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_ci map_write(map, CMD(0xb0), adr); 10778c2ecf20Sopenharmony_ci usec -= xip_elapsed_since(start); 10788c2ecf20Sopenharmony_ci suspended = xip_currtime(); 10798c2ecf20Sopenharmony_ci do { 10808c2ecf20Sopenharmony_ci if (xip_elapsed_since(suspended) > 100000) { 10818c2ecf20Sopenharmony_ci /* 10828c2ecf20Sopenharmony_ci * The chip doesn't want to suspend 10838c2ecf20Sopenharmony_ci * after waiting for 100 msecs. 10848c2ecf20Sopenharmony_ci * This is a critical error but there 10858c2ecf20Sopenharmony_ci * is not much we can do here. 10868c2ecf20Sopenharmony_ci */ 10878c2ecf20Sopenharmony_ci return; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci status = map_read(map, adr); 10908c2ecf20Sopenharmony_ci } while (!map_word_andequal(map, status, OK, OK)); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* Suspend succeeded */ 10938c2ecf20Sopenharmony_ci oldstate = chip->state; 10948c2ecf20Sopenharmony_ci if (!map_word_bitsset(map, status, CMD(0x40))) 10958c2ecf20Sopenharmony_ci break; 10968c2ecf20Sopenharmony_ci chip->state = FL_XIP_WHILE_ERASING; 10978c2ecf20Sopenharmony_ci chip->erase_suspended = 1; 10988c2ecf20Sopenharmony_ci map_write(map, CMD(0xf0), adr); 10998c2ecf20Sopenharmony_ci (void) map_read(map, adr); 11008c2ecf20Sopenharmony_ci xip_iprefetch(); 11018c2ecf20Sopenharmony_ci local_irq_enable(); 11028c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 11038c2ecf20Sopenharmony_ci xip_iprefetch(); 11048c2ecf20Sopenharmony_ci cond_resched(); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* 11078c2ecf20Sopenharmony_ci * We're back. However someone else might have 11088c2ecf20Sopenharmony_ci * decided to go write to the chip if we are in 11098c2ecf20Sopenharmony_ci * a suspended erase state. If so let's wait 11108c2ecf20Sopenharmony_ci * until it's done. 11118c2ecf20Sopenharmony_ci */ 11128c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 11138c2ecf20Sopenharmony_ci while (chip->state != FL_XIP_WHILE_ERASING) { 11148c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 11158c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 11168c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 11178c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 11188c2ecf20Sopenharmony_ci schedule(); 11198c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 11208c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci /* Disallow XIP again */ 11238c2ecf20Sopenharmony_ci local_irq_disable(); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* Correct Erase Suspend Hangups for M29EW */ 11268c2ecf20Sopenharmony_ci cfi_fixup_m29ew_erase_suspend(map, adr); 11278c2ecf20Sopenharmony_ci /* Resume the write or erase operation */ 11288c2ecf20Sopenharmony_ci map_write(map, cfi->sector_erase_cmd, adr); 11298c2ecf20Sopenharmony_ci chip->state = oldstate; 11308c2ecf20Sopenharmony_ci start = xip_currtime(); 11318c2ecf20Sopenharmony_ci } else if (usec >= 1000000/HZ) { 11328c2ecf20Sopenharmony_ci /* 11338c2ecf20Sopenharmony_ci * Try to save on CPU power when waiting delay 11348c2ecf20Sopenharmony_ci * is at least a system timer tick period. 11358c2ecf20Sopenharmony_ci * No need to be extremely accurate here. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ci xip_cpu_idle(); 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci status = map_read(map, adr); 11408c2ecf20Sopenharmony_ci } while (!map_word_andequal(map, status, OK, OK) 11418c2ecf20Sopenharmony_ci && xip_elapsed_since(start) < usec); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec) 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci/* 11478c2ecf20Sopenharmony_ci * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while 11488c2ecf20Sopenharmony_ci * the flash is actively programming or erasing since we have to poll for 11498c2ecf20Sopenharmony_ci * the operation to complete anyway. We can't do that in a generic way with 11508c2ecf20Sopenharmony_ci * a XIP setup so do it before the actual flash operation in this case 11518c2ecf20Sopenharmony_ci * and stub it out from INVALIDATE_CACHE_UDELAY. 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(map, from, size) \ 11548c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, from, size) 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ 11578c2ecf20Sopenharmony_ci UDELAY(map, chip, adr, usec) 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci/* 11608c2ecf20Sopenharmony_ci * Extra notes: 11618c2ecf20Sopenharmony_ci * 11628c2ecf20Sopenharmony_ci * Activating this XIP support changes the way the code works a bit. For 11638c2ecf20Sopenharmony_ci * example the code to suspend the current process when concurrent access 11648c2ecf20Sopenharmony_ci * happens is never executed because xip_udelay() will always return with the 11658c2ecf20Sopenharmony_ci * same chip state as it was entered with. This is why there is no care for 11668c2ecf20Sopenharmony_ci * the presence of add_wait_queue() or schedule() calls from within a couple 11678c2ecf20Sopenharmony_ci * xip_disable()'d areas of code, like in do_erase_oneblock for example. 11688c2ecf20Sopenharmony_ci * The queueing and scheduling are always happening within xip_udelay(). 11698c2ecf20Sopenharmony_ci * 11708c2ecf20Sopenharmony_ci * Similarly, get_chip() and put_chip() just happen to always be executed 11718c2ecf20Sopenharmony_ci * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state 11728c2ecf20Sopenharmony_ci * is in array mode, therefore never executing many cases therein and not 11738c2ecf20Sopenharmony_ci * causing any problem with XIP. 11748c2ecf20Sopenharmony_ci */ 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci#else 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci#define xip_disable(map, chip, adr) 11798c2ecf20Sopenharmony_ci#define xip_enable(map, chip, adr) 11808c2ecf20Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(x...) 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci#define UDELAY(map, chip, adr, usec) \ 11838c2ecf20Sopenharmony_cido { \ 11848c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); \ 11858c2ecf20Sopenharmony_ci cfi_udelay(usec); \ 11868c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); \ 11878c2ecf20Sopenharmony_ci} while (0) 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ 11908c2ecf20Sopenharmony_cido { \ 11918c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); \ 11928c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, adr, len); \ 11938c2ecf20Sopenharmony_ci cfi_udelay(usec); \ 11948c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); \ 11958c2ecf20Sopenharmony_ci} while (0) 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci#endif 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci unsigned long cmd_addr; 12028c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 12038c2ecf20Sopenharmony_ci int ret; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci adr += chip->start; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci /* Ensure cmd read/writes are aligned. */ 12088c2ecf20Sopenharmony_ci cmd_addr = adr & ~(map_bankwidth(map)-1); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 12118c2ecf20Sopenharmony_ci ret = get_chip(map, chip, cmd_addr, FL_READY); 12128c2ecf20Sopenharmony_ci if (ret) { 12138c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12148c2ecf20Sopenharmony_ci return ret; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (chip->state != FL_POINT && chip->state != FL_READY) { 12188c2ecf20Sopenharmony_ci map_write(map, CMD(0xf0), cmd_addr); 12198c2ecf20Sopenharmony_ci chip->state = FL_READY; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci map_copy_from(map, buf, adr, len); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci put_chip(map, chip, cmd_addr); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 12348c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 12358c2ecf20Sopenharmony_ci unsigned long ofs; 12368c2ecf20Sopenharmony_ci int chipnum; 12378c2ecf20Sopenharmony_ci int ret = 0; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci /* ofs: offset within the first chip that the first read should start */ 12408c2ecf20Sopenharmony_ci chipnum = (from >> cfi->chipshift); 12418c2ecf20Sopenharmony_ci ofs = from - (chipnum << cfi->chipshift); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci while (len) { 12448c2ecf20Sopenharmony_ci unsigned long thislen; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if ((len + ofs -1) >> cfi->chipshift) 12508c2ecf20Sopenharmony_ci thislen = (1<<cfi->chipshift) - ofs; 12518c2ecf20Sopenharmony_ci else 12528c2ecf20Sopenharmony_ci thislen = len; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); 12558c2ecf20Sopenharmony_ci if (ret) 12568c2ecf20Sopenharmony_ci break; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci *retlen += thislen; 12598c2ecf20Sopenharmony_ci len -= thislen; 12608c2ecf20Sopenharmony_ci buf += thislen; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci ofs = 0; 12638c2ecf20Sopenharmony_ci chipnum++; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci return ret; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_citypedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, 12698c2ecf20Sopenharmony_ci loff_t adr, size_t len, u_char *buf, size_t grouplen); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic inline void otp_enter(struct map_info *map, struct flchip *chip, 12728c2ecf20Sopenharmony_ci loff_t adr, size_t len) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 12778c2ecf20Sopenharmony_ci cfi->device_type, NULL); 12788c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 12798c2ecf20Sopenharmony_ci cfi->device_type, NULL); 12808c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, 12818c2ecf20Sopenharmony_ci cfi->device_type, NULL); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, chip->start + adr, len); 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic inline void otp_exit(struct map_info *map, struct flchip *chip, 12878c2ecf20Sopenharmony_ci loff_t adr, size_t len) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 12928c2ecf20Sopenharmony_ci cfi->device_type, NULL); 12938c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 12948c2ecf20Sopenharmony_ci cfi->device_type, NULL); 12958c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, 12968c2ecf20Sopenharmony_ci cfi->device_type, NULL); 12978c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, 12988c2ecf20Sopenharmony_ci cfi->device_type, NULL); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, chip->start + adr, len); 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic inline int do_read_secsi_onechip(struct map_info *map, 13048c2ecf20Sopenharmony_ci struct flchip *chip, loff_t adr, 13058c2ecf20Sopenharmony_ci size_t len, u_char *buf, 13068c2ecf20Sopenharmony_ci size_t grouplen) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci retry: 13118c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci if (chip->state != FL_READY){ 13148c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 13158c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci schedule(); 13208c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci goto retry; 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci adr += chip->start; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci chip->state = FL_READY; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci otp_enter(map, chip, adr, len); 13308c2ecf20Sopenharmony_ci map_copy_from(map, buf, adr, len); 13318c2ecf20Sopenharmony_ci otp_exit(map, chip, adr, len); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci wake_up(&chip->wq); 13348c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci return 0; 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 13428c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 13438c2ecf20Sopenharmony_ci unsigned long ofs; 13448c2ecf20Sopenharmony_ci int chipnum; 13458c2ecf20Sopenharmony_ci int ret = 0; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci /* ofs: offset within the first chip that the first read should start */ 13488c2ecf20Sopenharmony_ci /* 8 secsi bytes per chip */ 13498c2ecf20Sopenharmony_ci chipnum=from>>3; 13508c2ecf20Sopenharmony_ci ofs=from & 7; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci while (len) { 13538c2ecf20Sopenharmony_ci unsigned long thislen; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 13568c2ecf20Sopenharmony_ci break; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if ((len + ofs -1) >> 3) 13598c2ecf20Sopenharmony_ci thislen = (1<<3) - ofs; 13608c2ecf20Sopenharmony_ci else 13618c2ecf20Sopenharmony_ci thislen = len; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs, 13648c2ecf20Sopenharmony_ci thislen, buf, 0); 13658c2ecf20Sopenharmony_ci if (ret) 13668c2ecf20Sopenharmony_ci break; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci *retlen += thislen; 13698c2ecf20Sopenharmony_ci len -= thislen; 13708c2ecf20Sopenharmony_ci buf += thislen; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci ofs = 0; 13738c2ecf20Sopenharmony_ci chipnum++; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci return ret; 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, 13798c2ecf20Sopenharmony_ci unsigned long adr, map_word datum, 13808c2ecf20Sopenharmony_ci int mode); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_cistatic int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr, 13838c2ecf20Sopenharmony_ci size_t len, u_char *buf, size_t grouplen) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci int ret; 13868c2ecf20Sopenharmony_ci while (len) { 13878c2ecf20Sopenharmony_ci unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1); 13888c2ecf20Sopenharmony_ci int gap = adr - bus_ofs; 13898c2ecf20Sopenharmony_ci int n = min_t(int, len, map_bankwidth(map) - gap); 13908c2ecf20Sopenharmony_ci map_word datum = map_word_ff(map); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci if (n != map_bankwidth(map)) { 13938c2ecf20Sopenharmony_ci /* partial write of a word, load old contents */ 13948c2ecf20Sopenharmony_ci otp_enter(map, chip, bus_ofs, map_bankwidth(map)); 13958c2ecf20Sopenharmony_ci datum = map_read(map, bus_ofs); 13968c2ecf20Sopenharmony_ci otp_exit(map, chip, bus_ofs, map_bankwidth(map)); 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci datum = map_word_load_partial(map, datum, buf, gap, n); 14008c2ecf20Sopenharmony_ci ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE); 14018c2ecf20Sopenharmony_ci if (ret) 14028c2ecf20Sopenharmony_ci return ret; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci adr += n; 14058c2ecf20Sopenharmony_ci buf += n; 14068c2ecf20Sopenharmony_ci len -= n; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ci} 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_cistatic int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr, 14138c2ecf20Sopenharmony_ci size_t len, u_char *buf, size_t grouplen) 14148c2ecf20Sopenharmony_ci{ 14158c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 14168c2ecf20Sopenharmony_ci uint8_t lockreg; 14178c2ecf20Sopenharmony_ci unsigned long timeo; 14188c2ecf20Sopenharmony_ci int ret; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci /* make sure area matches group boundaries */ 14218c2ecf20Sopenharmony_ci if ((adr != 0) || (len != grouplen)) 14228c2ecf20Sopenharmony_ci return -EINVAL; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 14258c2ecf20Sopenharmony_ci ret = get_chip(map, chip, chip->start, FL_LOCKING); 14268c2ecf20Sopenharmony_ci if (ret) { 14278c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 14288c2ecf20Sopenharmony_ci return ret; 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci chip->state = FL_LOCKING; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci /* Enter lock register command */ 14338c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 14348c2ecf20Sopenharmony_ci cfi->device_type, NULL); 14358c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 14368c2ecf20Sopenharmony_ci cfi->device_type, NULL); 14378c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x40, cfi->addr_unlock1, chip->start, map, cfi, 14388c2ecf20Sopenharmony_ci cfi->device_type, NULL); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* read lock register */ 14418c2ecf20Sopenharmony_ci lockreg = cfi_read_query(map, 0); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci /* set bit 0 to protect extended memory block */ 14448c2ecf20Sopenharmony_ci lockreg &= ~0x01; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci /* set bit 0 to protect extended memory block */ 14478c2ecf20Sopenharmony_ci /* write lock register */ 14488c2ecf20Sopenharmony_ci map_write(map, CMD(0xA0), chip->start); 14498c2ecf20Sopenharmony_ci map_write(map, CMD(lockreg), chip->start); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* wait for chip to become ready */ 14528c2ecf20Sopenharmony_ci timeo = jiffies + msecs_to_jiffies(2); 14538c2ecf20Sopenharmony_ci for (;;) { 14548c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, NULL)) 14558c2ecf20Sopenharmony_ci break; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 14588c2ecf20Sopenharmony_ci pr_err("Waiting for chip to be ready timed out.\n"); 14598c2ecf20Sopenharmony_ci ret = -EIO; 14608c2ecf20Sopenharmony_ci break; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci UDELAY(map, chip, 0, 1); 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* exit protection commands */ 14668c2ecf20Sopenharmony_ci map_write(map, CMD(0x90), chip->start); 14678c2ecf20Sopenharmony_ci map_write(map, CMD(0x00), chip->start); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci chip->state = FL_READY; 14708c2ecf20Sopenharmony_ci put_chip(map, chip, chip->start); 14718c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci return ret; 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_cistatic int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, 14778c2ecf20Sopenharmony_ci size_t *retlen, u_char *buf, 14788c2ecf20Sopenharmony_ci otp_op_t action, int user_regs) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 14818c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 14828c2ecf20Sopenharmony_ci int ofs_factor = cfi->interleave * cfi->device_type; 14838c2ecf20Sopenharmony_ci unsigned long base; 14848c2ecf20Sopenharmony_ci int chipnum; 14858c2ecf20Sopenharmony_ci struct flchip *chip; 14868c2ecf20Sopenharmony_ci uint8_t otp, lockreg; 14878c2ecf20Sopenharmony_ci int ret; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci size_t user_size, factory_size, otpsize; 14908c2ecf20Sopenharmony_ci loff_t user_offset, factory_offset, otpoffset; 14918c2ecf20Sopenharmony_ci int user_locked = 0, otplocked; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci *retlen = 0; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci for (chipnum = 0; chipnum < cfi->numchips; chipnum++) { 14968c2ecf20Sopenharmony_ci chip = &cfi->chips[chipnum]; 14978c2ecf20Sopenharmony_ci factory_size = 0; 14988c2ecf20Sopenharmony_ci user_size = 0; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci /* Micron M29EW family */ 15018c2ecf20Sopenharmony_ci if (is_m29ew(cfi)) { 15028c2ecf20Sopenharmony_ci base = chip->start; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* check whether secsi area is factory locked 15058c2ecf20Sopenharmony_ci or user lockable */ 15068c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 15078c2ecf20Sopenharmony_ci ret = get_chip(map, chip, base, FL_CFI_QUERY); 15088c2ecf20Sopenharmony_ci if (ret) { 15098c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 15108c2ecf20Sopenharmony_ci return ret; 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci cfi_qry_mode_on(base, map, cfi); 15138c2ecf20Sopenharmony_ci otp = cfi_read_query(map, base + 0x3 * ofs_factor); 15148c2ecf20Sopenharmony_ci cfi_qry_mode_off(base, map, cfi); 15158c2ecf20Sopenharmony_ci put_chip(map, chip, base); 15168c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci if (otp & 0x80) { 15198c2ecf20Sopenharmony_ci /* factory locked */ 15208c2ecf20Sopenharmony_ci factory_offset = 0; 15218c2ecf20Sopenharmony_ci factory_size = 0x100; 15228c2ecf20Sopenharmony_ci } else { 15238c2ecf20Sopenharmony_ci /* customer lockable */ 15248c2ecf20Sopenharmony_ci user_offset = 0; 15258c2ecf20Sopenharmony_ci user_size = 0x100; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 15288c2ecf20Sopenharmony_ci ret = get_chip(map, chip, base, FL_LOCKING); 15298c2ecf20Sopenharmony_ci if (ret) { 15308c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 15318c2ecf20Sopenharmony_ci return ret; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci /* Enter lock register command */ 15358c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, 15368c2ecf20Sopenharmony_ci chip->start, map, cfi, 15378c2ecf20Sopenharmony_ci cfi->device_type, NULL); 15388c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, 15398c2ecf20Sopenharmony_ci chip->start, map, cfi, 15408c2ecf20Sopenharmony_ci cfi->device_type, NULL); 15418c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x40, cfi->addr_unlock1, 15428c2ecf20Sopenharmony_ci chip->start, map, cfi, 15438c2ecf20Sopenharmony_ci cfi->device_type, NULL); 15448c2ecf20Sopenharmony_ci /* read lock register */ 15458c2ecf20Sopenharmony_ci lockreg = cfi_read_query(map, 0); 15468c2ecf20Sopenharmony_ci /* exit protection commands */ 15478c2ecf20Sopenharmony_ci map_write(map, CMD(0x90), chip->start); 15488c2ecf20Sopenharmony_ci map_write(map, CMD(0x00), chip->start); 15498c2ecf20Sopenharmony_ci put_chip(map, chip, chip->start); 15508c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci user_locked = ((lockreg & 0x01) == 0x00); 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci otpsize = user_regs ? user_size : factory_size; 15578c2ecf20Sopenharmony_ci if (!otpsize) 15588c2ecf20Sopenharmony_ci continue; 15598c2ecf20Sopenharmony_ci otpoffset = user_regs ? user_offset : factory_offset; 15608c2ecf20Sopenharmony_ci otplocked = user_regs ? user_locked : 1; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci if (!action) { 15638c2ecf20Sopenharmony_ci /* return otpinfo */ 15648c2ecf20Sopenharmony_ci struct otp_info *otpinfo; 15658c2ecf20Sopenharmony_ci len -= sizeof(*otpinfo); 15668c2ecf20Sopenharmony_ci if (len <= 0) 15678c2ecf20Sopenharmony_ci return -ENOSPC; 15688c2ecf20Sopenharmony_ci otpinfo = (struct otp_info *)buf; 15698c2ecf20Sopenharmony_ci otpinfo->start = from; 15708c2ecf20Sopenharmony_ci otpinfo->length = otpsize; 15718c2ecf20Sopenharmony_ci otpinfo->locked = otplocked; 15728c2ecf20Sopenharmony_ci buf += sizeof(*otpinfo); 15738c2ecf20Sopenharmony_ci *retlen += sizeof(*otpinfo); 15748c2ecf20Sopenharmony_ci from += otpsize; 15758c2ecf20Sopenharmony_ci } else if ((from < otpsize) && (len > 0)) { 15768c2ecf20Sopenharmony_ci size_t size; 15778c2ecf20Sopenharmony_ci size = (len < otpsize - from) ? len : otpsize - from; 15788c2ecf20Sopenharmony_ci ret = action(map, chip, otpoffset + from, size, buf, 15798c2ecf20Sopenharmony_ci otpsize); 15808c2ecf20Sopenharmony_ci if (ret < 0) 15818c2ecf20Sopenharmony_ci return ret; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci buf += size; 15848c2ecf20Sopenharmony_ci len -= size; 15858c2ecf20Sopenharmony_ci *retlen += size; 15868c2ecf20Sopenharmony_ci from = 0; 15878c2ecf20Sopenharmony_ci } else { 15888c2ecf20Sopenharmony_ci from -= otpsize; 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci return 0; 15928c2ecf20Sopenharmony_ci} 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_fact_prot_info(struct mtd_info *mtd, size_t len, 15958c2ecf20Sopenharmony_ci size_t *retlen, struct otp_info *buf) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf, 15988c2ecf20Sopenharmony_ci NULL, 0); 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_user_prot_info(struct mtd_info *mtd, size_t len, 16028c2ecf20Sopenharmony_ci size_t *retlen, struct otp_info *buf) 16038c2ecf20Sopenharmony_ci{ 16048c2ecf20Sopenharmony_ci return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf, 16058c2ecf20Sopenharmony_ci NULL, 1); 16068c2ecf20Sopenharmony_ci} 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, 16098c2ecf20Sopenharmony_ci size_t len, size_t *retlen, 16108c2ecf20Sopenharmony_ci u_char *buf) 16118c2ecf20Sopenharmony_ci{ 16128c2ecf20Sopenharmony_ci return cfi_amdstd_otp_walk(mtd, from, len, retlen, 16138c2ecf20Sopenharmony_ci buf, do_read_secsi_onechip, 0); 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, 16178c2ecf20Sopenharmony_ci size_t len, size_t *retlen, 16188c2ecf20Sopenharmony_ci u_char *buf) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci return cfi_amdstd_otp_walk(mtd, from, len, retlen, 16218c2ecf20Sopenharmony_ci buf, do_read_secsi_onechip, 1); 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from, 16258c2ecf20Sopenharmony_ci size_t len, size_t *retlen, 16268c2ecf20Sopenharmony_ci u_char *buf) 16278c2ecf20Sopenharmony_ci{ 16288c2ecf20Sopenharmony_ci return cfi_amdstd_otp_walk(mtd, from, len, retlen, buf, 16298c2ecf20Sopenharmony_ci do_otp_write, 1); 16308c2ecf20Sopenharmony_ci} 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_cistatic int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, 16338c2ecf20Sopenharmony_ci size_t len) 16348c2ecf20Sopenharmony_ci{ 16358c2ecf20Sopenharmony_ci size_t retlen; 16368c2ecf20Sopenharmony_ci return cfi_amdstd_otp_walk(mtd, from, len, &retlen, NULL, 16378c2ecf20Sopenharmony_ci do_otp_lock, 1); 16388c2ecf20Sopenharmony_ci} 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword_once(struct map_info *map, 16418c2ecf20Sopenharmony_ci struct flchip *chip, 16428c2ecf20Sopenharmony_ci unsigned long adr, map_word datum, 16438c2ecf20Sopenharmony_ci int mode, struct cfi_private *cfi) 16448c2ecf20Sopenharmony_ci{ 16458c2ecf20Sopenharmony_ci unsigned long timeo = jiffies + HZ; 16468c2ecf20Sopenharmony_ci /* 16478c2ecf20Sopenharmony_ci * We use a 1ms + 1 jiffies generic timeout for writes (most devices 16488c2ecf20Sopenharmony_ci * have a max write time of a few hundreds usec). However, we should 16498c2ecf20Sopenharmony_ci * use the maximum timeout value given by the chip at probe time 16508c2ecf20Sopenharmony_ci * instead. Unfortunately, struct flchip does have a field for 16518c2ecf20Sopenharmony_ci * maximum timeout, only for typical which can be far too short 16528c2ecf20Sopenharmony_ci * depending of the conditions. The ' + 1' is to avoid having a 16538c2ecf20Sopenharmony_ci * timeout of 0 jiffies if HZ is smaller than 1000. 16548c2ecf20Sopenharmony_ci */ 16558c2ecf20Sopenharmony_ci unsigned long uWriteTimeout = (HZ / 1000) + 1; 16568c2ecf20Sopenharmony_ci int ret = 0; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 16598c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); 16608c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 16618c2ecf20Sopenharmony_ci map_write(map, datum, adr); 16628c2ecf20Sopenharmony_ci chip->state = mode; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci INVALIDATE_CACHE_UDELAY(map, chip, 16658c2ecf20Sopenharmony_ci adr, map_bankwidth(map), 16668c2ecf20Sopenharmony_ci chip->word_write_time); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci /* See comment above for timeout value. */ 16698c2ecf20Sopenharmony_ci timeo = jiffies + uWriteTimeout; 16708c2ecf20Sopenharmony_ci for (;;) { 16718c2ecf20Sopenharmony_ci if (chip->state != mode) { 16728c2ecf20Sopenharmony_ci /* Someone's suspended the write. Sleep */ 16738c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 16768c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 16778c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 16788c2ecf20Sopenharmony_ci schedule(); 16798c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 16808c2ecf20Sopenharmony_ci timeo = jiffies + (HZ / 2); /* FIXME */ 16818c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 16828c2ecf20Sopenharmony_ci continue; 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci /* 16868c2ecf20Sopenharmony_ci * We check "time_after" and "!chip_good" before checking 16878c2ecf20Sopenharmony_ci * "chip_good" to avoid the failure due to scheduling. 16888c2ecf20Sopenharmony_ci */ 16898c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo) && 16908c2ecf20Sopenharmony_ci !chip_good(map, chip, adr, &datum)) { 16918c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 16928c2ecf20Sopenharmony_ci printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); 16938c2ecf20Sopenharmony_ci xip_disable(map, chip, adr); 16948c2ecf20Sopenharmony_ci ret = -EIO; 16958c2ecf20Sopenharmony_ci break; 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (chip_good(map, chip, adr, &datum)) { 16998c2ecf20Sopenharmony_ci if (cfi_check_err_status(map, chip, adr)) 17008c2ecf20Sopenharmony_ci ret = -EIO; 17018c2ecf20Sopenharmony_ci break; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 17058c2ecf20Sopenharmony_ci UDELAY(map, chip, adr, 1); 17068c2ecf20Sopenharmony_ci } 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci return ret; 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword_start(struct map_info *map, 17128c2ecf20Sopenharmony_ci struct flchip *chip, 17138c2ecf20Sopenharmony_ci unsigned long adr, int mode) 17148c2ecf20Sopenharmony_ci{ 17158c2ecf20Sopenharmony_ci int ret; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, mode); 17208c2ecf20Sopenharmony_ci if (ret) { 17218c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 17228c2ecf20Sopenharmony_ci return ret; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (mode == FL_OTP_WRITE) 17268c2ecf20Sopenharmony_ci otp_enter(map, chip, adr, map_bankwidth(map)); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci return ret; 17298c2ecf20Sopenharmony_ci} 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_cistatic void __xipram do_write_oneword_done(struct map_info *map, 17328c2ecf20Sopenharmony_ci struct flchip *chip, 17338c2ecf20Sopenharmony_ci unsigned long adr, int mode) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci if (mode == FL_OTP_WRITE) 17368c2ecf20Sopenharmony_ci otp_exit(map, chip, adr, map_bankwidth(map)); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci chip->state = FL_READY; 17398c2ecf20Sopenharmony_ci DISABLE_VPP(map); 17408c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword_retry(struct map_info *map, 17468c2ecf20Sopenharmony_ci struct flchip *chip, 17478c2ecf20Sopenharmony_ci unsigned long adr, map_word datum, 17488c2ecf20Sopenharmony_ci int mode) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 17518c2ecf20Sopenharmony_ci int ret = 0; 17528c2ecf20Sopenharmony_ci map_word oldd; 17538c2ecf20Sopenharmony_ci int retry_cnt = 0; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci /* 17568c2ecf20Sopenharmony_ci * Check for a NOP for the case when the datum to write is already 17578c2ecf20Sopenharmony_ci * present - it saves time and works around buggy chips that corrupt 17588c2ecf20Sopenharmony_ci * data at other locations when 0xff is written to a location that 17598c2ecf20Sopenharmony_ci * already contains 0xff. 17608c2ecf20Sopenharmony_ci */ 17618c2ecf20Sopenharmony_ci oldd = map_read(map, adr); 17628c2ecf20Sopenharmony_ci if (map_word_equal(map, oldd, datum)) { 17638c2ecf20Sopenharmony_ci pr_debug("MTD %s(): NOP\n", __func__); 17648c2ecf20Sopenharmony_ci return ret; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); 17688c2ecf20Sopenharmony_ci ENABLE_VPP(map); 17698c2ecf20Sopenharmony_ci xip_disable(map, chip, adr); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci retry: 17728c2ecf20Sopenharmony_ci ret = do_write_oneword_once(map, chip, adr, datum, mode, cfi); 17738c2ecf20Sopenharmony_ci if (ret) { 17748c2ecf20Sopenharmony_ci /* reset on all failures. */ 17758c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), chip->start); 17768c2ecf20Sopenharmony_ci /* FIXME - should have reset delay before continuing */ 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (++retry_cnt <= MAX_RETRIES) { 17798c2ecf20Sopenharmony_ci ret = 0; 17808c2ecf20Sopenharmony_ci goto retry; 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci return ret; 17868c2ecf20Sopenharmony_ci} 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, 17898c2ecf20Sopenharmony_ci unsigned long adr, map_word datum, 17908c2ecf20Sopenharmony_ci int mode) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci int ret; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci adr += chip->start; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", __func__, adr, 17978c2ecf20Sopenharmony_ci datum.x[0]); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci ret = do_write_oneword_start(map, chip, adr, mode); 18008c2ecf20Sopenharmony_ci if (ret) 18018c2ecf20Sopenharmony_ci return ret; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci ret = do_write_oneword_retry(map, chip, adr, datum, mode); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci do_write_oneword_done(map, chip, adr, mode); 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci return ret; 18088c2ecf20Sopenharmony_ci} 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, 18128c2ecf20Sopenharmony_ci size_t *retlen, const u_char *buf) 18138c2ecf20Sopenharmony_ci{ 18148c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 18158c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 18168c2ecf20Sopenharmony_ci int ret; 18178c2ecf20Sopenharmony_ci int chipnum; 18188c2ecf20Sopenharmony_ci unsigned long ofs, chipstart; 18198c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci chipnum = to >> cfi->chipshift; 18228c2ecf20Sopenharmony_ci ofs = to - (chipnum << cfi->chipshift); 18238c2ecf20Sopenharmony_ci chipstart = cfi->chips[chipnum].start; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci /* If it's not bus-aligned, do the first byte write */ 18268c2ecf20Sopenharmony_ci if (ofs & (map_bankwidth(map)-1)) { 18278c2ecf20Sopenharmony_ci unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); 18288c2ecf20Sopenharmony_ci int i = ofs - bus_ofs; 18298c2ecf20Sopenharmony_ci int n = 0; 18308c2ecf20Sopenharmony_ci map_word tmp_buf; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci retry: 18338c2ecf20Sopenharmony_ci mutex_lock(&cfi->chips[chipnum].mutex); 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci if (cfi->chips[chipnum].state != FL_READY) { 18368c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 18378c2ecf20Sopenharmony_ci add_wait_queue(&cfi->chips[chipnum].wq, &wait); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci mutex_unlock(&cfi->chips[chipnum].mutex); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci schedule(); 18428c2ecf20Sopenharmony_ci remove_wait_queue(&cfi->chips[chipnum].wq, &wait); 18438c2ecf20Sopenharmony_ci goto retry; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci /* Load 'tmp_buf' with old contents of flash */ 18478c2ecf20Sopenharmony_ci tmp_buf = map_read(map, bus_ofs+chipstart); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci mutex_unlock(&cfi->chips[chipnum].mutex); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci /* Number of bytes to copy from buffer */ 18528c2ecf20Sopenharmony_ci n = min_t(int, len, map_bankwidth(map)-i); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci ret = do_write_oneword(map, &cfi->chips[chipnum], 18578c2ecf20Sopenharmony_ci bus_ofs, tmp_buf, FL_WRITING); 18588c2ecf20Sopenharmony_ci if (ret) 18598c2ecf20Sopenharmony_ci return ret; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci ofs += n; 18628c2ecf20Sopenharmony_ci buf += n; 18638c2ecf20Sopenharmony_ci (*retlen) += n; 18648c2ecf20Sopenharmony_ci len -= n; 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 18678c2ecf20Sopenharmony_ci chipnum ++; 18688c2ecf20Sopenharmony_ci ofs = 0; 18698c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 18708c2ecf20Sopenharmony_ci return 0; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* We are now aligned, write as much as possible */ 18758c2ecf20Sopenharmony_ci while(len >= map_bankwidth(map)) { 18768c2ecf20Sopenharmony_ci map_word datum; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci datum = map_word_load(map, buf); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci ret = do_write_oneword(map, &cfi->chips[chipnum], 18818c2ecf20Sopenharmony_ci ofs, datum, FL_WRITING); 18828c2ecf20Sopenharmony_ci if (ret) 18838c2ecf20Sopenharmony_ci return ret; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci ofs += map_bankwidth(map); 18868c2ecf20Sopenharmony_ci buf += map_bankwidth(map); 18878c2ecf20Sopenharmony_ci (*retlen) += map_bankwidth(map); 18888c2ecf20Sopenharmony_ci len -= map_bankwidth(map); 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 18918c2ecf20Sopenharmony_ci chipnum ++; 18928c2ecf20Sopenharmony_ci ofs = 0; 18938c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 18948c2ecf20Sopenharmony_ci return 0; 18958c2ecf20Sopenharmony_ci chipstart = cfi->chips[chipnum].start; 18968c2ecf20Sopenharmony_ci } 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci /* Write the trailing bytes if any */ 19008c2ecf20Sopenharmony_ci if (len & (map_bankwidth(map)-1)) { 19018c2ecf20Sopenharmony_ci map_word tmp_buf; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci retry1: 19048c2ecf20Sopenharmony_ci mutex_lock(&cfi->chips[chipnum].mutex); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci if (cfi->chips[chipnum].state != FL_READY) { 19078c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 19088c2ecf20Sopenharmony_ci add_wait_queue(&cfi->chips[chipnum].wq, &wait); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci mutex_unlock(&cfi->chips[chipnum].mutex); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci schedule(); 19138c2ecf20Sopenharmony_ci remove_wait_queue(&cfi->chips[chipnum].wq, &wait); 19148c2ecf20Sopenharmony_ci goto retry1; 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci tmp_buf = map_read(map, ofs + chipstart); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci mutex_unlock(&cfi->chips[chipnum].mutex); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci ret = do_write_oneword(map, &cfi->chips[chipnum], 19248c2ecf20Sopenharmony_ci ofs, tmp_buf, FL_WRITING); 19258c2ecf20Sopenharmony_ci if (ret) 19268c2ecf20Sopenharmony_ci return ret; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci (*retlen) += len; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci return 0; 19328c2ecf20Sopenharmony_ci} 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE 19358c2ecf20Sopenharmony_cistatic int __xipram do_write_buffer_wait(struct map_info *map, 19368c2ecf20Sopenharmony_ci struct flchip *chip, unsigned long adr, 19378c2ecf20Sopenharmony_ci map_word datum) 19388c2ecf20Sopenharmony_ci{ 19398c2ecf20Sopenharmony_ci unsigned long timeo; 19408c2ecf20Sopenharmony_ci unsigned long u_write_timeout; 19418c2ecf20Sopenharmony_ci int ret = 0; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci /* 19448c2ecf20Sopenharmony_ci * Timeout is calculated according to CFI data, if available. 19458c2ecf20Sopenharmony_ci * See more comments in cfi_cmdset_0002(). 19468c2ecf20Sopenharmony_ci */ 19478c2ecf20Sopenharmony_ci u_write_timeout = usecs_to_jiffies(chip->buffer_write_time_max); 19488c2ecf20Sopenharmony_ci timeo = jiffies + u_write_timeout; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci for (;;) { 19518c2ecf20Sopenharmony_ci if (chip->state != FL_WRITING) { 19528c2ecf20Sopenharmony_ci /* Someone's suspended the write. Sleep */ 19538c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 19568c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 19578c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 19588c2ecf20Sopenharmony_ci schedule(); 19598c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 19608c2ecf20Sopenharmony_ci timeo = jiffies + (HZ / 2); /* FIXME */ 19618c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 19628c2ecf20Sopenharmony_ci continue; 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci /* 19668c2ecf20Sopenharmony_ci * We check "time_after" and "!chip_good" before checking 19678c2ecf20Sopenharmony_ci * "chip_good" to avoid the failure due to scheduling. 19688c2ecf20Sopenharmony_ci */ 19698c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo) && 19708c2ecf20Sopenharmony_ci !chip_good(map, chip, adr, &datum)) { 19718c2ecf20Sopenharmony_ci pr_err("MTD %s(): software timeout, address:0x%.8lx.\n", 19728c2ecf20Sopenharmony_ci __func__, adr); 19738c2ecf20Sopenharmony_ci ret = -EIO; 19748c2ecf20Sopenharmony_ci break; 19758c2ecf20Sopenharmony_ci } 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci if (chip_good(map, chip, adr, &datum)) { 19788c2ecf20Sopenharmony_ci if (cfi_check_err_status(map, chip, adr)) 19798c2ecf20Sopenharmony_ci ret = -EIO; 19808c2ecf20Sopenharmony_ci break; 19818c2ecf20Sopenharmony_ci } 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 19848c2ecf20Sopenharmony_ci UDELAY(map, chip, adr, 1); 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci return ret; 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_cistatic void __xipram do_write_buffer_reset(struct map_info *map, 19918c2ecf20Sopenharmony_ci struct flchip *chip, 19928c2ecf20Sopenharmony_ci struct cfi_private *cfi) 19938c2ecf20Sopenharmony_ci{ 19948c2ecf20Sopenharmony_ci /* 19958c2ecf20Sopenharmony_ci * Recovery from write-buffer programming failures requires 19968c2ecf20Sopenharmony_ci * the write-to-buffer-reset sequence. Since the last part 19978c2ecf20Sopenharmony_ci * of the sequence also works as a normal reset, we can run 19988c2ecf20Sopenharmony_ci * the same commands regardless of why we are here. 19998c2ecf20Sopenharmony_ci * See e.g. 20008c2ecf20Sopenharmony_ci * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf 20018c2ecf20Sopenharmony_ci */ 20028c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 20038c2ecf20Sopenharmony_ci cfi->device_type, NULL); 20048c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 20058c2ecf20Sopenharmony_ci cfi->device_type, NULL); 20068c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi, 20078c2ecf20Sopenharmony_ci cfi->device_type, NULL); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci /* FIXME - should have reset delay before continuing */ 20108c2ecf20Sopenharmony_ci} 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci/* 20138c2ecf20Sopenharmony_ci * FIXME: interleaved mode not tested, and probably not supported! 20148c2ecf20Sopenharmony_ci */ 20158c2ecf20Sopenharmony_cistatic int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, 20168c2ecf20Sopenharmony_ci unsigned long adr, const u_char *buf, 20178c2ecf20Sopenharmony_ci int len) 20188c2ecf20Sopenharmony_ci{ 20198c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 20208c2ecf20Sopenharmony_ci int ret; 20218c2ecf20Sopenharmony_ci unsigned long cmd_adr; 20228c2ecf20Sopenharmony_ci int z, words; 20238c2ecf20Sopenharmony_ci map_word datum; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci adr += chip->start; 20268c2ecf20Sopenharmony_ci cmd_adr = adr; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 20298c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, FL_WRITING); 20308c2ecf20Sopenharmony_ci if (ret) { 20318c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 20328c2ecf20Sopenharmony_ci return ret; 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci datum = map_word_load(map, buf); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", 20388c2ecf20Sopenharmony_ci __func__, adr, datum.x[0]); 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci XIP_INVAL_CACHED_RANGE(map, adr, len); 20418c2ecf20Sopenharmony_ci ENABLE_VPP(map); 20428c2ecf20Sopenharmony_ci xip_disable(map, chip, cmd_adr); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 20458c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci /* Write Buffer Load */ 20488c2ecf20Sopenharmony_ci map_write(map, CMD(0x25), cmd_adr); 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci chip->state = FL_WRITING_TO_BUFFER; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci /* Write length of data to come */ 20538c2ecf20Sopenharmony_ci words = len / map_bankwidth(map); 20548c2ecf20Sopenharmony_ci map_write(map, CMD(words - 1), cmd_adr); 20558c2ecf20Sopenharmony_ci /* Write data */ 20568c2ecf20Sopenharmony_ci z = 0; 20578c2ecf20Sopenharmony_ci while(z < words * map_bankwidth(map)) { 20588c2ecf20Sopenharmony_ci datum = map_word_load(map, buf); 20598c2ecf20Sopenharmony_ci map_write(map, datum, adr + z); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci z += map_bankwidth(map); 20628c2ecf20Sopenharmony_ci buf += map_bankwidth(map); 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci z -= map_bankwidth(map); 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci adr += z; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci /* Write Buffer Program Confirm: GO GO GO */ 20698c2ecf20Sopenharmony_ci map_write(map, CMD(0x29), cmd_adr); 20708c2ecf20Sopenharmony_ci chip->state = FL_WRITING; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci INVALIDATE_CACHE_UDELAY(map, chip, 20738c2ecf20Sopenharmony_ci adr, map_bankwidth(map), 20748c2ecf20Sopenharmony_ci chip->word_write_time); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci ret = do_write_buffer_wait(map, chip, adr, datum); 20778c2ecf20Sopenharmony_ci if (ret) 20788c2ecf20Sopenharmony_ci do_write_buffer_reset(map, chip, cfi); 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci chip->state = FL_READY; 20838c2ecf20Sopenharmony_ci DISABLE_VPP(map); 20848c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 20858c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci return ret; 20888c2ecf20Sopenharmony_ci} 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, 20928c2ecf20Sopenharmony_ci size_t *retlen, const u_char *buf) 20938c2ecf20Sopenharmony_ci{ 20948c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 20958c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 20968c2ecf20Sopenharmony_ci int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 20978c2ecf20Sopenharmony_ci int ret; 20988c2ecf20Sopenharmony_ci int chipnum; 20998c2ecf20Sopenharmony_ci unsigned long ofs; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci chipnum = to >> cfi->chipshift; 21028c2ecf20Sopenharmony_ci ofs = to - (chipnum << cfi->chipshift); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci /* If it's not bus-aligned, do the first word write */ 21058c2ecf20Sopenharmony_ci if (ofs & (map_bankwidth(map)-1)) { 21068c2ecf20Sopenharmony_ci size_t local_len = (-ofs)&(map_bankwidth(map)-1); 21078c2ecf20Sopenharmony_ci if (local_len > len) 21088c2ecf20Sopenharmony_ci local_len = len; 21098c2ecf20Sopenharmony_ci ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift), 21108c2ecf20Sopenharmony_ci local_len, retlen, buf); 21118c2ecf20Sopenharmony_ci if (ret) 21128c2ecf20Sopenharmony_ci return ret; 21138c2ecf20Sopenharmony_ci ofs += local_len; 21148c2ecf20Sopenharmony_ci buf += local_len; 21158c2ecf20Sopenharmony_ci len -= local_len; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 21188c2ecf20Sopenharmony_ci chipnum ++; 21198c2ecf20Sopenharmony_ci ofs = 0; 21208c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 21218c2ecf20Sopenharmony_ci return 0; 21228c2ecf20Sopenharmony_ci } 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci /* Write buffer is worth it only if more than one word to write... */ 21268c2ecf20Sopenharmony_ci while (len >= map_bankwidth(map) * 2) { 21278c2ecf20Sopenharmony_ci /* We must not cross write block boundaries */ 21288c2ecf20Sopenharmony_ci int size = wbufsize - (ofs & (wbufsize-1)); 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci if (size > len) 21318c2ecf20Sopenharmony_ci size = len; 21328c2ecf20Sopenharmony_ci if (size % map_bankwidth(map)) 21338c2ecf20Sopenharmony_ci size -= size % map_bankwidth(map); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci ret = do_write_buffer(map, &cfi->chips[chipnum], 21368c2ecf20Sopenharmony_ci ofs, buf, size); 21378c2ecf20Sopenharmony_ci if (ret) 21388c2ecf20Sopenharmony_ci return ret; 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci ofs += size; 21418c2ecf20Sopenharmony_ci buf += size; 21428c2ecf20Sopenharmony_ci (*retlen) += size; 21438c2ecf20Sopenharmony_ci len -= size; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 21468c2ecf20Sopenharmony_ci chipnum ++; 21478c2ecf20Sopenharmony_ci ofs = 0; 21488c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 21498c2ecf20Sopenharmony_ci return 0; 21508c2ecf20Sopenharmony_ci } 21518c2ecf20Sopenharmony_ci } 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci if (len) { 21548c2ecf20Sopenharmony_ci size_t retlen_dregs = 0; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift), 21578c2ecf20Sopenharmony_ci len, &retlen_dregs, buf); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci *retlen += retlen_dregs; 21608c2ecf20Sopenharmony_ci return ret; 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci return 0; 21648c2ecf20Sopenharmony_ci} 21658c2ecf20Sopenharmony_ci#endif /* !FORCE_WORD_WRITE */ 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci/* 21688c2ecf20Sopenharmony_ci * Wait for the flash chip to become ready to write data 21698c2ecf20Sopenharmony_ci * 21708c2ecf20Sopenharmony_ci * This is only called during the panic_write() path. When panic_write() 21718c2ecf20Sopenharmony_ci * is called, the kernel is in the process of a panic, and will soon be 21728c2ecf20Sopenharmony_ci * dead. Therefore we don't take any locks, and attempt to get access 21738c2ecf20Sopenharmony_ci * to the chip as soon as possible. 21748c2ecf20Sopenharmony_ci */ 21758c2ecf20Sopenharmony_cistatic int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip, 21768c2ecf20Sopenharmony_ci unsigned long adr) 21778c2ecf20Sopenharmony_ci{ 21788c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 21798c2ecf20Sopenharmony_ci int retries = 10; 21808c2ecf20Sopenharmony_ci int i; 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci /* 21838c2ecf20Sopenharmony_ci * If the driver thinks the chip is idle, and no toggle bits 21848c2ecf20Sopenharmony_ci * are changing, then the chip is actually idle for sure. 21858c2ecf20Sopenharmony_ci */ 21868c2ecf20Sopenharmony_ci if (chip->state == FL_READY && chip_ready(map, chip, adr, NULL)) 21878c2ecf20Sopenharmony_ci return 0; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci /* 21908c2ecf20Sopenharmony_ci * Try several times to reset the chip and then wait for it 21918c2ecf20Sopenharmony_ci * to become idle. The upper limit of a few milliseconds of 21928c2ecf20Sopenharmony_ci * delay isn't a big problem: the kernel is dying anyway. It 21938c2ecf20Sopenharmony_ci * is more important to save the messages. 21948c2ecf20Sopenharmony_ci */ 21958c2ecf20Sopenharmony_ci while (retries > 0) { 21968c2ecf20Sopenharmony_ci const unsigned long timeo = (HZ / 1000) + 1; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci /* send the reset command */ 21998c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), chip->start); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci /* wait for the chip to become ready */ 22028c2ecf20Sopenharmony_ci for (i = 0; i < jiffies_to_usecs(timeo); i++) { 22038c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, NULL)) 22048c2ecf20Sopenharmony_ci return 0; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci udelay(1); 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci retries--; 22108c2ecf20Sopenharmony_ci } 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci /* the chip never became ready */ 22138c2ecf20Sopenharmony_ci return -EBUSY; 22148c2ecf20Sopenharmony_ci} 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci/* 22178c2ecf20Sopenharmony_ci * Write out one word of data to a single flash chip during a kernel panic 22188c2ecf20Sopenharmony_ci * 22198c2ecf20Sopenharmony_ci * This is only called during the panic_write() path. When panic_write() 22208c2ecf20Sopenharmony_ci * is called, the kernel is in the process of a panic, and will soon be 22218c2ecf20Sopenharmony_ci * dead. Therefore we don't take any locks, and attempt to get access 22228c2ecf20Sopenharmony_ci * to the chip as soon as possible. 22238c2ecf20Sopenharmony_ci * 22248c2ecf20Sopenharmony_ci * The implementation of this routine is intentionally similar to 22258c2ecf20Sopenharmony_ci * do_write_oneword(), in order to ease code maintenance. 22268c2ecf20Sopenharmony_ci */ 22278c2ecf20Sopenharmony_cistatic int do_panic_write_oneword(struct map_info *map, struct flchip *chip, 22288c2ecf20Sopenharmony_ci unsigned long adr, map_word datum) 22298c2ecf20Sopenharmony_ci{ 22308c2ecf20Sopenharmony_ci const unsigned long uWriteTimeout = (HZ / 1000) + 1; 22318c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 22328c2ecf20Sopenharmony_ci int retry_cnt = 0; 22338c2ecf20Sopenharmony_ci map_word oldd; 22348c2ecf20Sopenharmony_ci int ret; 22358c2ecf20Sopenharmony_ci int i; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci adr += chip->start; 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci ret = cfi_amdstd_panic_wait(map, chip, adr); 22408c2ecf20Sopenharmony_ci if (ret) 22418c2ecf20Sopenharmony_ci return ret; 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci pr_debug("MTD %s(): PANIC WRITE 0x%.8lx(0x%.8lx)\n", 22448c2ecf20Sopenharmony_ci __func__, adr, datum.x[0]); 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci /* 22478c2ecf20Sopenharmony_ci * Check for a NOP for the case when the datum to write is already 22488c2ecf20Sopenharmony_ci * present - it saves time and works around buggy chips that corrupt 22498c2ecf20Sopenharmony_ci * data at other locations when 0xff is written to a location that 22508c2ecf20Sopenharmony_ci * already contains 0xff. 22518c2ecf20Sopenharmony_ci */ 22528c2ecf20Sopenharmony_ci oldd = map_read(map, adr); 22538c2ecf20Sopenharmony_ci if (map_word_equal(map, oldd, datum)) { 22548c2ecf20Sopenharmony_ci pr_debug("MTD %s(): NOP\n", __func__); 22558c2ecf20Sopenharmony_ci goto op_done; 22568c2ecf20Sopenharmony_ci } 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci ENABLE_VPP(map); 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ciretry: 22618c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 22628c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); 22638c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 22648c2ecf20Sopenharmony_ci map_write(map, datum, adr); 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) { 22678c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, NULL)) 22688c2ecf20Sopenharmony_ci break; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci udelay(1); 22718c2ecf20Sopenharmony_ci } 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci if (!chip_ready(map, chip, adr, &datum) || 22748c2ecf20Sopenharmony_ci cfi_check_err_status(map, chip, adr)) { 22758c2ecf20Sopenharmony_ci /* reset on all failures. */ 22768c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), chip->start); 22778c2ecf20Sopenharmony_ci /* FIXME - should have reset delay before continuing */ 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci if (++retry_cnt <= MAX_RETRIES) 22808c2ecf20Sopenharmony_ci goto retry; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci ret = -EIO; 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ciop_done: 22868c2ecf20Sopenharmony_ci DISABLE_VPP(map); 22878c2ecf20Sopenharmony_ci return ret; 22888c2ecf20Sopenharmony_ci} 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci/* 22918c2ecf20Sopenharmony_ci * Write out some data during a kernel panic 22928c2ecf20Sopenharmony_ci * 22938c2ecf20Sopenharmony_ci * This is used by the mtdoops driver to save the dying messages from a 22948c2ecf20Sopenharmony_ci * kernel which has panic'd. 22958c2ecf20Sopenharmony_ci * 22968c2ecf20Sopenharmony_ci * This routine ignores all of the locking used throughout the rest of the 22978c2ecf20Sopenharmony_ci * driver, in order to ensure that the data gets written out no matter what 22988c2ecf20Sopenharmony_ci * state this driver (and the flash chip itself) was in when the kernel crashed. 22998c2ecf20Sopenharmony_ci * 23008c2ecf20Sopenharmony_ci * The implementation of this routine is intentionally similar to 23018c2ecf20Sopenharmony_ci * cfi_amdstd_write_words(), in order to ease code maintenance. 23028c2ecf20Sopenharmony_ci */ 23038c2ecf20Sopenharmony_cistatic int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, 23048c2ecf20Sopenharmony_ci size_t *retlen, const u_char *buf) 23058c2ecf20Sopenharmony_ci{ 23068c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 23078c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 23088c2ecf20Sopenharmony_ci unsigned long ofs, chipstart; 23098c2ecf20Sopenharmony_ci int ret; 23108c2ecf20Sopenharmony_ci int chipnum; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci chipnum = to >> cfi->chipshift; 23138c2ecf20Sopenharmony_ci ofs = to - (chipnum << cfi->chipshift); 23148c2ecf20Sopenharmony_ci chipstart = cfi->chips[chipnum].start; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci /* If it's not bus aligned, do the first byte write */ 23178c2ecf20Sopenharmony_ci if (ofs & (map_bankwidth(map) - 1)) { 23188c2ecf20Sopenharmony_ci unsigned long bus_ofs = ofs & ~(map_bankwidth(map) - 1); 23198c2ecf20Sopenharmony_ci int i = ofs - bus_ofs; 23208c2ecf20Sopenharmony_ci int n = 0; 23218c2ecf20Sopenharmony_ci map_word tmp_buf; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], bus_ofs); 23248c2ecf20Sopenharmony_ci if (ret) 23258c2ecf20Sopenharmony_ci return ret; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci /* Load 'tmp_buf' with old contents of flash */ 23288c2ecf20Sopenharmony_ci tmp_buf = map_read(map, bus_ofs + chipstart); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci /* Number of bytes to copy from buffer */ 23318c2ecf20Sopenharmony_ci n = min_t(int, len, map_bankwidth(map) - i); 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n); 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci ret = do_panic_write_oneword(map, &cfi->chips[chipnum], 23368c2ecf20Sopenharmony_ci bus_ofs, tmp_buf); 23378c2ecf20Sopenharmony_ci if (ret) 23388c2ecf20Sopenharmony_ci return ret; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci ofs += n; 23418c2ecf20Sopenharmony_ci buf += n; 23428c2ecf20Sopenharmony_ci (*retlen) += n; 23438c2ecf20Sopenharmony_ci len -= n; 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 23468c2ecf20Sopenharmony_ci chipnum++; 23478c2ecf20Sopenharmony_ci ofs = 0; 23488c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 23498c2ecf20Sopenharmony_ci return 0; 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci } 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci /* We are now aligned, write as much as possible */ 23548c2ecf20Sopenharmony_ci while (len >= map_bankwidth(map)) { 23558c2ecf20Sopenharmony_ci map_word datum; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci datum = map_word_load(map, buf); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci ret = do_panic_write_oneword(map, &cfi->chips[chipnum], 23608c2ecf20Sopenharmony_ci ofs, datum); 23618c2ecf20Sopenharmony_ci if (ret) 23628c2ecf20Sopenharmony_ci return ret; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci ofs += map_bankwidth(map); 23658c2ecf20Sopenharmony_ci buf += map_bankwidth(map); 23668c2ecf20Sopenharmony_ci (*retlen) += map_bankwidth(map); 23678c2ecf20Sopenharmony_ci len -= map_bankwidth(map); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 23708c2ecf20Sopenharmony_ci chipnum++; 23718c2ecf20Sopenharmony_ci ofs = 0; 23728c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 23738c2ecf20Sopenharmony_ci return 0; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci chipstart = cfi->chips[chipnum].start; 23768c2ecf20Sopenharmony_ci } 23778c2ecf20Sopenharmony_ci } 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci /* Write the trailing bytes if any */ 23808c2ecf20Sopenharmony_ci if (len & (map_bankwidth(map) - 1)) { 23818c2ecf20Sopenharmony_ci map_word tmp_buf; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], ofs); 23848c2ecf20Sopenharmony_ci if (ret) 23858c2ecf20Sopenharmony_ci return ret; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci tmp_buf = map_read(map, ofs + chipstart); 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci ret = do_panic_write_oneword(map, &cfi->chips[chipnum], 23928c2ecf20Sopenharmony_ci ofs, tmp_buf); 23938c2ecf20Sopenharmony_ci if (ret) 23948c2ecf20Sopenharmony_ci return ret; 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci (*retlen) += len; 23978c2ecf20Sopenharmony_ci } 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci return 0; 24008c2ecf20Sopenharmony_ci} 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci/* 24048c2ecf20Sopenharmony_ci * Handle devices with one erase region, that only implement 24058c2ecf20Sopenharmony_ci * the chip erase command. 24068c2ecf20Sopenharmony_ci */ 24078c2ecf20Sopenharmony_cistatic int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) 24088c2ecf20Sopenharmony_ci{ 24098c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 24108c2ecf20Sopenharmony_ci unsigned long timeo = jiffies + HZ; 24118c2ecf20Sopenharmony_ci unsigned long int adr; 24128c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 24138c2ecf20Sopenharmony_ci int ret; 24148c2ecf20Sopenharmony_ci int retry_cnt = 0; 24158c2ecf20Sopenharmony_ci map_word datum = map_word_ff(map); 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci adr = cfi->addr_unlock1; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 24208c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, FL_ERASING); 24218c2ecf20Sopenharmony_ci if (ret) { 24228c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 24238c2ecf20Sopenharmony_ci return ret; 24248c2ecf20Sopenharmony_ci } 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci pr_debug("MTD %s(): ERASE 0x%.8lx\n", 24278c2ecf20Sopenharmony_ci __func__, chip->start); 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci XIP_INVAL_CACHED_RANGE(map, adr, map->size); 24308c2ecf20Sopenharmony_ci ENABLE_VPP(map); 24318c2ecf20Sopenharmony_ci xip_disable(map, chip, adr); 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci retry: 24348c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 24358c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); 24368c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 24378c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 24388c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); 24398c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci chip->state = FL_ERASING; 24428c2ecf20Sopenharmony_ci chip->erase_suspended = 0; 24438c2ecf20Sopenharmony_ci chip->in_progress_block_addr = adr; 24448c2ecf20Sopenharmony_ci chip->in_progress_block_mask = ~(map->size - 1); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci INVALIDATE_CACHE_UDELAY(map, chip, 24478c2ecf20Sopenharmony_ci adr, map->size, 24488c2ecf20Sopenharmony_ci chip->erase_time*500); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*20); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci for (;;) { 24538c2ecf20Sopenharmony_ci if (chip->state != FL_ERASING) { 24548c2ecf20Sopenharmony_ci /* Someone's suspended the erase. Sleep */ 24558c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 24568c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 24578c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 24588c2ecf20Sopenharmony_ci schedule(); 24598c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 24608c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 24618c2ecf20Sopenharmony_ci continue; 24628c2ecf20Sopenharmony_ci } 24638c2ecf20Sopenharmony_ci if (chip->erase_suspended) { 24648c2ecf20Sopenharmony_ci /* This erase was suspended and resumed. 24658c2ecf20Sopenharmony_ci Adjust the timeout */ 24668c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*20); /* FIXME */ 24678c2ecf20Sopenharmony_ci chip->erase_suspended = 0; 24688c2ecf20Sopenharmony_ci } 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, &datum)) { 24718c2ecf20Sopenharmony_ci if (cfi_check_err_status(map, chip, adr)) 24728c2ecf20Sopenharmony_ci ret = -EIO; 24738c2ecf20Sopenharmony_ci break; 24748c2ecf20Sopenharmony_ci } 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 24778c2ecf20Sopenharmony_ci printk(KERN_WARNING "MTD %s(): software timeout\n", 24788c2ecf20Sopenharmony_ci __func__); 24798c2ecf20Sopenharmony_ci ret = -EIO; 24808c2ecf20Sopenharmony_ci break; 24818c2ecf20Sopenharmony_ci } 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 24848c2ecf20Sopenharmony_ci UDELAY(map, chip, adr, 1000000/HZ); 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci /* Did we succeed? */ 24878c2ecf20Sopenharmony_ci if (ret) { 24888c2ecf20Sopenharmony_ci /* reset on all failures. */ 24898c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), chip->start); 24908c2ecf20Sopenharmony_ci /* FIXME - should have reset delay before continuing */ 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci if (++retry_cnt <= MAX_RETRIES) { 24938c2ecf20Sopenharmony_ci ret = 0; 24948c2ecf20Sopenharmony_ci goto retry; 24958c2ecf20Sopenharmony_ci } 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci chip->state = FL_READY; 24998c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 25008c2ecf20Sopenharmony_ci DISABLE_VPP(map); 25018c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 25028c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci return ret; 25058c2ecf20Sopenharmony_ci} 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_cistatic int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) 25098c2ecf20Sopenharmony_ci{ 25108c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 25118c2ecf20Sopenharmony_ci unsigned long timeo = jiffies + HZ; 25128c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 25138c2ecf20Sopenharmony_ci int ret; 25148c2ecf20Sopenharmony_ci int retry_cnt = 0; 25158c2ecf20Sopenharmony_ci map_word datum = map_word_ff(map); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci adr += chip->start; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 25208c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, FL_ERASING); 25218c2ecf20Sopenharmony_ci if (ret) { 25228c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 25238c2ecf20Sopenharmony_ci return ret; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci pr_debug("MTD %s(): ERASE 0x%.8lx\n", 25278c2ecf20Sopenharmony_ci __func__, adr); 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci XIP_INVAL_CACHED_RANGE(map, adr, len); 25308c2ecf20Sopenharmony_ci ENABLE_VPP(map); 25318c2ecf20Sopenharmony_ci xip_disable(map, chip, adr); 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci retry: 25348c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 25358c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); 25368c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 25378c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 25388c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); 25398c2ecf20Sopenharmony_ci map_write(map, cfi->sector_erase_cmd, adr); 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci chip->state = FL_ERASING; 25428c2ecf20Sopenharmony_ci chip->erase_suspended = 0; 25438c2ecf20Sopenharmony_ci chip->in_progress_block_addr = adr; 25448c2ecf20Sopenharmony_ci chip->in_progress_block_mask = ~(len - 1); 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci INVALIDATE_CACHE_UDELAY(map, chip, 25478c2ecf20Sopenharmony_ci adr, len, 25488c2ecf20Sopenharmony_ci chip->erase_time*500); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*20); 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci for (;;) { 25538c2ecf20Sopenharmony_ci if (chip->state != FL_ERASING) { 25548c2ecf20Sopenharmony_ci /* Someone's suspended the erase. Sleep */ 25558c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 25568c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 25578c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 25588c2ecf20Sopenharmony_ci schedule(); 25598c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 25608c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 25618c2ecf20Sopenharmony_ci continue; 25628c2ecf20Sopenharmony_ci } 25638c2ecf20Sopenharmony_ci if (chip->erase_suspended) { 25648c2ecf20Sopenharmony_ci /* This erase was suspended and resumed. 25658c2ecf20Sopenharmony_ci Adjust the timeout */ 25668c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*20); /* FIXME */ 25678c2ecf20Sopenharmony_ci chip->erase_suspended = 0; 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, &datum)) { 25718c2ecf20Sopenharmony_ci if (cfi_check_err_status(map, chip, adr)) 25728c2ecf20Sopenharmony_ci ret = -EIO; 25738c2ecf20Sopenharmony_ci break; 25748c2ecf20Sopenharmony_ci } 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 25778c2ecf20Sopenharmony_ci printk(KERN_WARNING "MTD %s(): software timeout\n", 25788c2ecf20Sopenharmony_ci __func__); 25798c2ecf20Sopenharmony_ci ret = -EIO; 25808c2ecf20Sopenharmony_ci break; 25818c2ecf20Sopenharmony_ci } 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 25848c2ecf20Sopenharmony_ci UDELAY(map, chip, adr, 1000000/HZ); 25858c2ecf20Sopenharmony_ci } 25868c2ecf20Sopenharmony_ci /* Did we succeed? */ 25878c2ecf20Sopenharmony_ci if (ret) { 25888c2ecf20Sopenharmony_ci /* reset on all failures. */ 25898c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), chip->start); 25908c2ecf20Sopenharmony_ci /* FIXME - should have reset delay before continuing */ 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci if (++retry_cnt <= MAX_RETRIES) { 25938c2ecf20Sopenharmony_ci ret = 0; 25948c2ecf20Sopenharmony_ci goto retry; 25958c2ecf20Sopenharmony_ci } 25968c2ecf20Sopenharmony_ci } 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci chip->state = FL_READY; 25998c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 26008c2ecf20Sopenharmony_ci DISABLE_VPP(map); 26018c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 26028c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 26038c2ecf20Sopenharmony_ci return ret; 26048c2ecf20Sopenharmony_ci} 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) 26088c2ecf20Sopenharmony_ci{ 26098c2ecf20Sopenharmony_ci return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr, 26108c2ecf20Sopenharmony_ci instr->len, NULL); 26118c2ecf20Sopenharmony_ci} 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) 26158c2ecf20Sopenharmony_ci{ 26168c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 26178c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci if (instr->addr != 0) 26208c2ecf20Sopenharmony_ci return -EINVAL; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci if (instr->len != mtd->size) 26238c2ecf20Sopenharmony_ci return -EINVAL; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci return do_erase_chip(map, &cfi->chips[0]); 26268c2ecf20Sopenharmony_ci} 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_cistatic int do_atmel_lock(struct map_info *map, struct flchip *chip, 26298c2ecf20Sopenharmony_ci unsigned long adr, int len, void *thunk) 26308c2ecf20Sopenharmony_ci{ 26318c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 26328c2ecf20Sopenharmony_ci int ret; 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 26358c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr + chip->start, FL_LOCKING); 26368c2ecf20Sopenharmony_ci if (ret) 26378c2ecf20Sopenharmony_ci goto out_unlock; 26388c2ecf20Sopenharmony_ci chip->state = FL_LOCKING; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len); 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 26438c2ecf20Sopenharmony_ci cfi->device_type, NULL); 26448c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 26458c2ecf20Sopenharmony_ci cfi->device_type, NULL); 26468c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, 26478c2ecf20Sopenharmony_ci cfi->device_type, NULL); 26488c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 26498c2ecf20Sopenharmony_ci cfi->device_type, NULL); 26508c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 26518c2ecf20Sopenharmony_ci cfi->device_type, NULL); 26528c2ecf20Sopenharmony_ci map_write(map, CMD(0x40), chip->start + adr); 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci chip->state = FL_READY; 26558c2ecf20Sopenharmony_ci put_chip(map, chip, adr + chip->start); 26568c2ecf20Sopenharmony_ci ret = 0; 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ciout_unlock: 26598c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 26608c2ecf20Sopenharmony_ci return ret; 26618c2ecf20Sopenharmony_ci} 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_cistatic int do_atmel_unlock(struct map_info *map, struct flchip *chip, 26648c2ecf20Sopenharmony_ci unsigned long adr, int len, void *thunk) 26658c2ecf20Sopenharmony_ci{ 26668c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 26678c2ecf20Sopenharmony_ci int ret; 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 26708c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr + chip->start, FL_UNLOCKING); 26718c2ecf20Sopenharmony_ci if (ret) 26728c2ecf20Sopenharmony_ci goto out_unlock; 26738c2ecf20Sopenharmony_ci chip->state = FL_UNLOCKING; 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len); 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 26788c2ecf20Sopenharmony_ci cfi->device_type, NULL); 26798c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci chip->state = FL_READY; 26828c2ecf20Sopenharmony_ci put_chip(map, chip, adr + chip->start); 26838c2ecf20Sopenharmony_ci ret = 0; 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ciout_unlock: 26868c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 26878c2ecf20Sopenharmony_ci return ret; 26888c2ecf20Sopenharmony_ci} 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_cistatic int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 26918c2ecf20Sopenharmony_ci{ 26928c2ecf20Sopenharmony_ci return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL); 26938c2ecf20Sopenharmony_ci} 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_cistatic int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 26968c2ecf20Sopenharmony_ci{ 26978c2ecf20Sopenharmony_ci return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); 26988c2ecf20Sopenharmony_ci} 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci/* 27018c2ecf20Sopenharmony_ci * Advanced Sector Protection - PPB (Persistent Protection Bit) locking 27028c2ecf20Sopenharmony_ci */ 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_cistruct ppb_lock { 27058c2ecf20Sopenharmony_ci struct flchip *chip; 27068c2ecf20Sopenharmony_ci unsigned long adr; 27078c2ecf20Sopenharmony_ci int locked; 27088c2ecf20Sopenharmony_ci}; 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_LOCK ((void *)1) 27118c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *)2) 27128c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_GETLOCK ((void *)3) 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_cistatic int __maybe_unused do_ppb_xxlock(struct map_info *map, 27158c2ecf20Sopenharmony_ci struct flchip *chip, 27168c2ecf20Sopenharmony_ci unsigned long adr, int len, void *thunk) 27178c2ecf20Sopenharmony_ci{ 27188c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 27198c2ecf20Sopenharmony_ci unsigned long timeo; 27208c2ecf20Sopenharmony_ci int ret; 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci adr += chip->start; 27238c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 27248c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, FL_LOCKING); 27258c2ecf20Sopenharmony_ci if (ret) { 27268c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 27278c2ecf20Sopenharmony_ci return ret; 27288c2ecf20Sopenharmony_ci } 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci pr_debug("MTD %s(): XXLOCK 0x%08lx len %d\n", __func__, adr, len); 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 27338c2ecf20Sopenharmony_ci cfi->device_type, NULL); 27348c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 27358c2ecf20Sopenharmony_ci cfi->device_type, NULL); 27368c2ecf20Sopenharmony_ci /* PPB entry command */ 27378c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xC0, cfi->addr_unlock1, chip->start, map, cfi, 27388c2ecf20Sopenharmony_ci cfi->device_type, NULL); 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { 27418c2ecf20Sopenharmony_ci chip->state = FL_LOCKING; 27428c2ecf20Sopenharmony_ci map_write(map, CMD(0xA0), adr); 27438c2ecf20Sopenharmony_ci map_write(map, CMD(0x00), adr); 27448c2ecf20Sopenharmony_ci } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { 27458c2ecf20Sopenharmony_ci /* 27468c2ecf20Sopenharmony_ci * Unlocking of one specific sector is not supported, so we 27478c2ecf20Sopenharmony_ci * have to unlock all sectors of this device instead 27488c2ecf20Sopenharmony_ci */ 27498c2ecf20Sopenharmony_ci chip->state = FL_UNLOCKING; 27508c2ecf20Sopenharmony_ci map_write(map, CMD(0x80), chip->start); 27518c2ecf20Sopenharmony_ci map_write(map, CMD(0x30), chip->start); 27528c2ecf20Sopenharmony_ci } else if (thunk == DO_XXLOCK_ONEBLOCK_GETLOCK) { 27538c2ecf20Sopenharmony_ci chip->state = FL_JEDEC_QUERY; 27548c2ecf20Sopenharmony_ci /* Return locked status: 0->locked, 1->unlocked */ 27558c2ecf20Sopenharmony_ci ret = !cfi_read_query(map, adr); 27568c2ecf20Sopenharmony_ci } else 27578c2ecf20Sopenharmony_ci BUG(); 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci /* 27608c2ecf20Sopenharmony_ci * Wait for some time as unlocking of all sectors takes quite long 27618c2ecf20Sopenharmony_ci */ 27628c2ecf20Sopenharmony_ci timeo = jiffies + msecs_to_jiffies(2000); /* 2s max (un)locking */ 27638c2ecf20Sopenharmony_ci for (;;) { 27648c2ecf20Sopenharmony_ci if (chip_ready(map, chip, adr, NULL)) 27658c2ecf20Sopenharmony_ci break; 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 27688c2ecf20Sopenharmony_ci printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); 27698c2ecf20Sopenharmony_ci ret = -EIO; 27708c2ecf20Sopenharmony_ci break; 27718c2ecf20Sopenharmony_ci } 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci UDELAY(map, chip, adr, 1); 27748c2ecf20Sopenharmony_ci } 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci /* Exit BC commands */ 27778c2ecf20Sopenharmony_ci map_write(map, CMD(0x90), chip->start); 27788c2ecf20Sopenharmony_ci map_write(map, CMD(0x00), chip->start); 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci chip->state = FL_READY; 27818c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 27828c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci return ret; 27858c2ecf20Sopenharmony_ci} 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_cistatic int __maybe_unused cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, 27888c2ecf20Sopenharmony_ci uint64_t len) 27898c2ecf20Sopenharmony_ci{ 27908c2ecf20Sopenharmony_ci return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, 27918c2ecf20Sopenharmony_ci DO_XXLOCK_ONEBLOCK_LOCK); 27928c2ecf20Sopenharmony_ci} 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_cistatic int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, 27958c2ecf20Sopenharmony_ci uint64_t len) 27968c2ecf20Sopenharmony_ci{ 27978c2ecf20Sopenharmony_ci struct mtd_erase_region_info *regions = mtd->eraseregions; 27988c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 27998c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 28008c2ecf20Sopenharmony_ci struct ppb_lock *sect; 28018c2ecf20Sopenharmony_ci unsigned long adr; 28028c2ecf20Sopenharmony_ci loff_t offset; 28038c2ecf20Sopenharmony_ci uint64_t length; 28048c2ecf20Sopenharmony_ci int chipnum; 28058c2ecf20Sopenharmony_ci int i; 28068c2ecf20Sopenharmony_ci int sectors; 28078c2ecf20Sopenharmony_ci int ret; 28088c2ecf20Sopenharmony_ci int max_sectors; 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci /* 28118c2ecf20Sopenharmony_ci * PPB unlocking always unlocks all sectors of the flash chip. 28128c2ecf20Sopenharmony_ci * We need to re-lock all previously locked sectors. So lets 28138c2ecf20Sopenharmony_ci * first check the locking status of all sectors and save 28148c2ecf20Sopenharmony_ci * it for future use. 28158c2ecf20Sopenharmony_ci */ 28168c2ecf20Sopenharmony_ci max_sectors = 0; 28178c2ecf20Sopenharmony_ci for (i = 0; i < mtd->numeraseregions; i++) 28188c2ecf20Sopenharmony_ci max_sectors += regions[i].numblocks; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci sect = kcalloc(max_sectors, sizeof(struct ppb_lock), GFP_KERNEL); 28218c2ecf20Sopenharmony_ci if (!sect) 28228c2ecf20Sopenharmony_ci return -ENOMEM; 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci /* 28258c2ecf20Sopenharmony_ci * This code to walk all sectors is a slightly modified version 28268c2ecf20Sopenharmony_ci * of the cfi_varsize_frob() code. 28278c2ecf20Sopenharmony_ci */ 28288c2ecf20Sopenharmony_ci i = 0; 28298c2ecf20Sopenharmony_ci chipnum = 0; 28308c2ecf20Sopenharmony_ci adr = 0; 28318c2ecf20Sopenharmony_ci sectors = 0; 28328c2ecf20Sopenharmony_ci offset = 0; 28338c2ecf20Sopenharmony_ci length = mtd->size; 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci while (length) { 28368c2ecf20Sopenharmony_ci int size = regions[i].erasesize; 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci /* 28398c2ecf20Sopenharmony_ci * Only test sectors that shall not be unlocked. The other 28408c2ecf20Sopenharmony_ci * sectors shall be unlocked, so lets keep their locking 28418c2ecf20Sopenharmony_ci * status at "unlocked" (locked=0) for the final re-locking. 28428c2ecf20Sopenharmony_ci */ 28438c2ecf20Sopenharmony_ci if ((offset < ofs) || (offset >= (ofs + len))) { 28448c2ecf20Sopenharmony_ci sect[sectors].chip = &cfi->chips[chipnum]; 28458c2ecf20Sopenharmony_ci sect[sectors].adr = adr; 28468c2ecf20Sopenharmony_ci sect[sectors].locked = do_ppb_xxlock( 28478c2ecf20Sopenharmony_ci map, &cfi->chips[chipnum], adr, 0, 28488c2ecf20Sopenharmony_ci DO_XXLOCK_ONEBLOCK_GETLOCK); 28498c2ecf20Sopenharmony_ci } 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci adr += size; 28528c2ecf20Sopenharmony_ci offset += size; 28538c2ecf20Sopenharmony_ci length -= size; 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci if (offset == regions[i].offset + size * regions[i].numblocks) 28568c2ecf20Sopenharmony_ci i++; 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci if (adr >> cfi->chipshift) { 28598c2ecf20Sopenharmony_ci if (offset >= (ofs + len)) 28608c2ecf20Sopenharmony_ci break; 28618c2ecf20Sopenharmony_ci adr = 0; 28628c2ecf20Sopenharmony_ci chipnum++; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 28658c2ecf20Sopenharmony_ci break; 28668c2ecf20Sopenharmony_ci } 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci sectors++; 28698c2ecf20Sopenharmony_ci if (sectors >= max_sectors) { 28708c2ecf20Sopenharmony_ci printk(KERN_ERR "Only %d sectors for PPB locking supported!\n", 28718c2ecf20Sopenharmony_ci max_sectors); 28728c2ecf20Sopenharmony_ci kfree(sect); 28738c2ecf20Sopenharmony_ci return -EINVAL; 28748c2ecf20Sopenharmony_ci } 28758c2ecf20Sopenharmony_ci } 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci /* Now unlock the whole chip */ 28788c2ecf20Sopenharmony_ci ret = cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, 28798c2ecf20Sopenharmony_ci DO_XXLOCK_ONEBLOCK_UNLOCK); 28808c2ecf20Sopenharmony_ci if (ret) { 28818c2ecf20Sopenharmony_ci kfree(sect); 28828c2ecf20Sopenharmony_ci return ret; 28838c2ecf20Sopenharmony_ci } 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci /* 28868c2ecf20Sopenharmony_ci * PPB unlocking always unlocks all sectors of the flash chip. 28878c2ecf20Sopenharmony_ci * We need to re-lock all previously locked sectors. 28888c2ecf20Sopenharmony_ci */ 28898c2ecf20Sopenharmony_ci for (i = 0; i < sectors; i++) { 28908c2ecf20Sopenharmony_ci if (sect[i].locked) 28918c2ecf20Sopenharmony_ci do_ppb_xxlock(map, sect[i].chip, sect[i].adr, 0, 28928c2ecf20Sopenharmony_ci DO_XXLOCK_ONEBLOCK_LOCK); 28938c2ecf20Sopenharmony_ci } 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci kfree(sect); 28968c2ecf20Sopenharmony_ci return ret; 28978c2ecf20Sopenharmony_ci} 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_cistatic int __maybe_unused cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, 29008c2ecf20Sopenharmony_ci uint64_t len) 29018c2ecf20Sopenharmony_ci{ 29028c2ecf20Sopenharmony_ci return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, 29038c2ecf20Sopenharmony_ci DO_XXLOCK_ONEBLOCK_GETLOCK) ? 1 : 0; 29048c2ecf20Sopenharmony_ci} 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_cistatic void cfi_amdstd_sync (struct mtd_info *mtd) 29078c2ecf20Sopenharmony_ci{ 29088c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 29098c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 29108c2ecf20Sopenharmony_ci int i; 29118c2ecf20Sopenharmony_ci struct flchip *chip; 29128c2ecf20Sopenharmony_ci int ret = 0; 29138c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 29168c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci retry: 29198c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 29208c2ecf20Sopenharmony_ci 29218c2ecf20Sopenharmony_ci switch(chip->state) { 29228c2ecf20Sopenharmony_ci case FL_READY: 29238c2ecf20Sopenharmony_ci case FL_STATUS: 29248c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 29258c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 29268c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 29278c2ecf20Sopenharmony_ci chip->state = FL_SYNCING; 29288c2ecf20Sopenharmony_ci /* No need to wake_up() on this state change - 29298c2ecf20Sopenharmony_ci * as the whole point is that nobody can do anything 29308c2ecf20Sopenharmony_ci * with the chip now anyway. 29318c2ecf20Sopenharmony_ci */ 29328c2ecf20Sopenharmony_ci fallthrough; 29338c2ecf20Sopenharmony_ci case FL_SYNCING: 29348c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 29358c2ecf20Sopenharmony_ci break; 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci default: 29388c2ecf20Sopenharmony_ci /* Not an idle state */ 29398c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 29408c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 29418c2ecf20Sopenharmony_ci 29428c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci schedule(); 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci goto retry; 29498c2ecf20Sopenharmony_ci } 29508c2ecf20Sopenharmony_ci } 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci /* Unlock the chips again */ 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci for (i--; i >=0; i--) { 29558c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci if (chip->state == FL_SYNCING) { 29608c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 29618c2ecf20Sopenharmony_ci wake_up(&chip->wq); 29628c2ecf20Sopenharmony_ci } 29638c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 29648c2ecf20Sopenharmony_ci } 29658c2ecf20Sopenharmony_ci} 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_cistatic int cfi_amdstd_suspend(struct mtd_info *mtd) 29698c2ecf20Sopenharmony_ci{ 29708c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 29718c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 29728c2ecf20Sopenharmony_ci int i; 29738c2ecf20Sopenharmony_ci struct flchip *chip; 29748c2ecf20Sopenharmony_ci int ret = 0; 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 29778c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci switch(chip->state) { 29828c2ecf20Sopenharmony_ci case FL_READY: 29838c2ecf20Sopenharmony_ci case FL_STATUS: 29848c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 29858c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 29868c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 29878c2ecf20Sopenharmony_ci chip->state = FL_PM_SUSPENDED; 29888c2ecf20Sopenharmony_ci /* No need to wake_up() on this state change - 29898c2ecf20Sopenharmony_ci * as the whole point is that nobody can do anything 29908c2ecf20Sopenharmony_ci * with the chip now anyway. 29918c2ecf20Sopenharmony_ci */ 29928c2ecf20Sopenharmony_ci case FL_PM_SUSPENDED: 29938c2ecf20Sopenharmony_ci break; 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci default: 29968c2ecf20Sopenharmony_ci ret = -EAGAIN; 29978c2ecf20Sopenharmony_ci break; 29988c2ecf20Sopenharmony_ci } 29998c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 30008c2ecf20Sopenharmony_ci } 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci /* Unlock the chips again */ 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_ci if (ret) { 30058c2ecf20Sopenharmony_ci for (i--; i >=0; i--) { 30068c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 30118c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 30128c2ecf20Sopenharmony_ci wake_up(&chip->wq); 30138c2ecf20Sopenharmony_ci } 30148c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 30158c2ecf20Sopenharmony_ci } 30168c2ecf20Sopenharmony_ci } 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci return ret; 30198c2ecf20Sopenharmony_ci} 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_cistatic void cfi_amdstd_resume(struct mtd_info *mtd) 30238c2ecf20Sopenharmony_ci{ 30248c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 30258c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 30268c2ecf20Sopenharmony_ci int i; 30278c2ecf20Sopenharmony_ci struct flchip *chip; 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci for (i=0; i<cfi->numchips; i++) { 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 30368c2ecf20Sopenharmony_ci chip->state = FL_READY; 30378c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), chip->start); 30388c2ecf20Sopenharmony_ci wake_up(&chip->wq); 30398c2ecf20Sopenharmony_ci } 30408c2ecf20Sopenharmony_ci else 30418c2ecf20Sopenharmony_ci printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n"); 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 30448c2ecf20Sopenharmony_ci } 30458c2ecf20Sopenharmony_ci} 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci/* 30498c2ecf20Sopenharmony_ci * Ensure that the flash device is put back into read array mode before 30508c2ecf20Sopenharmony_ci * unloading the driver or rebooting. On some systems, rebooting while 30518c2ecf20Sopenharmony_ci * the flash is in query/program/erase mode will prevent the CPU from 30528c2ecf20Sopenharmony_ci * fetching the bootloader code, requiring a hard reset or power cycle. 30538c2ecf20Sopenharmony_ci */ 30548c2ecf20Sopenharmony_cistatic int cfi_amdstd_reset(struct mtd_info *mtd) 30558c2ecf20Sopenharmony_ci{ 30568c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 30578c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 30588c2ecf20Sopenharmony_ci int i, ret; 30598c2ecf20Sopenharmony_ci struct flchip *chip; 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci for (i = 0; i < cfi->numchips; i++) { 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); 30688c2ecf20Sopenharmony_ci if (!ret) { 30698c2ecf20Sopenharmony_ci map_write(map, CMD(0xF0), chip->start); 30708c2ecf20Sopenharmony_ci chip->state = FL_SHUTDOWN; 30718c2ecf20Sopenharmony_ci put_chip(map, chip, chip->start); 30728c2ecf20Sopenharmony_ci } 30738c2ecf20Sopenharmony_ci 30748c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 30758c2ecf20Sopenharmony_ci } 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci return 0; 30788c2ecf20Sopenharmony_ci} 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_cistatic int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val, 30828c2ecf20Sopenharmony_ci void *v) 30838c2ecf20Sopenharmony_ci{ 30848c2ecf20Sopenharmony_ci struct mtd_info *mtd; 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci mtd = container_of(nb, struct mtd_info, reboot_notifier); 30878c2ecf20Sopenharmony_ci cfi_amdstd_reset(mtd); 30888c2ecf20Sopenharmony_ci return NOTIFY_DONE; 30898c2ecf20Sopenharmony_ci} 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_cistatic void cfi_amdstd_destroy(struct mtd_info *mtd) 30938c2ecf20Sopenharmony_ci{ 30948c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 30958c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci cfi_amdstd_reset(mtd); 30988c2ecf20Sopenharmony_ci unregister_reboot_notifier(&mtd->reboot_notifier); 30998c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 31008c2ecf20Sopenharmony_ci kfree(cfi->cfiq); 31018c2ecf20Sopenharmony_ci kfree(cfi); 31028c2ecf20Sopenharmony_ci kfree(mtd->eraseregions); 31038c2ecf20Sopenharmony_ci} 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 31068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al."); 31078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); 31088c2ecf20Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0006"); 31098c2ecf20Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0701"); 3110