18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Common Flash Interface support: 38c2ecf20Sopenharmony_ci * Intel Extended Vendor Command Set (ID 0x0001) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) 2000 Red Hat. GPL'd 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * 10/10/2000 Nicolas Pitre <nico@fluxnic.net> 98c2ecf20Sopenharmony_ci * - completely revamped method functions so they are aware and 108c2ecf20Sopenharmony_ci * independent of the flash geometry (buswidth, interleave, etc.) 118c2ecf20Sopenharmony_ci * - scalability vs code size is completely set at compile-time 128c2ecf20Sopenharmony_ci * (see include/linux/mtd/cfi.h for selection) 138c2ecf20Sopenharmony_ci * - optimized write buffer method 148c2ecf20Sopenharmony_ci * 02/05/2002 Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com> 158c2ecf20Sopenharmony_ci * - reworked lock/unlock/erase support for var size flash 168c2ecf20Sopenharmony_ci * 21/03/2007 Rodolfo Giometti <giometti@linux.it> 178c2ecf20Sopenharmony_ci * - auto unlock sectors on resume for auto locking flash on power up 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/sched.h> 248c2ecf20Sopenharmony_ci#include <asm/io.h> 258c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/errno.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/delay.h> 308c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 318c2ecf20Sopenharmony_ci#include <linux/reboot.h> 328c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 338c2ecf20Sopenharmony_ci#include <linux/mtd/xip.h> 348c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 358c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 368c2ecf20Sopenharmony_ci#include <linux/mtd/cfi.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ 398c2ecf20Sopenharmony_ci/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci// debugging, turns off buffer write mode if set to 1 428c2ecf20Sopenharmony_ci#define FORCE_WORD_WRITE 0 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Intel chips */ 458c2ecf20Sopenharmony_ci#define I82802AB 0x00ad 468c2ecf20Sopenharmony_ci#define I82802AC 0x00ac 478c2ecf20Sopenharmony_ci#define PF38F4476 0x881c 488c2ecf20Sopenharmony_ci#define M28F00AP30 0x8963 498c2ecf20Sopenharmony_ci/* STMicroelectronics chips */ 508c2ecf20Sopenharmony_ci#define M50LPW080 0x002F 518c2ecf20Sopenharmony_ci#define M50FLW080A 0x0080 528c2ecf20Sopenharmony_ci#define M50FLW080B 0x0081 538c2ecf20Sopenharmony_ci/* Atmel chips */ 548c2ecf20Sopenharmony_ci#define AT49BV640D 0x02de 558c2ecf20Sopenharmony_ci#define AT49BV640DT 0x02db 568c2ecf20Sopenharmony_ci/* Sharp chips */ 578c2ecf20Sopenharmony_ci#define LH28F640BFHE_PTTL90 0x00b0 588c2ecf20Sopenharmony_ci#define LH28F640BFHE_PBTL90 0x00b1 598c2ecf20Sopenharmony_ci#define LH28F640BFHE_PTTL70A 0x00b2 608c2ecf20Sopenharmony_ci#define LH28F640BFHE_PBTL70A 0x00b3 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 638c2ecf20Sopenharmony_cistatic int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 648c2ecf20Sopenharmony_cistatic int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 658c2ecf20Sopenharmony_cistatic int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *); 668c2ecf20Sopenharmony_cistatic int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); 678c2ecf20Sopenharmony_cistatic void cfi_intelext_sync (struct mtd_info *); 688c2ecf20Sopenharmony_cistatic int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 698c2ecf20Sopenharmony_cistatic int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 708c2ecf20Sopenharmony_cistatic int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs, 718c2ecf20Sopenharmony_ci uint64_t len); 728c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_OTP 738c2ecf20Sopenharmony_cistatic int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 748c2ecf20Sopenharmony_cistatic int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 758c2ecf20Sopenharmony_cistatic int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 768c2ecf20Sopenharmony_cistatic int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); 778c2ecf20Sopenharmony_cistatic int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t, 788c2ecf20Sopenharmony_ci size_t *, struct otp_info *); 798c2ecf20Sopenharmony_cistatic int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t, 808c2ecf20Sopenharmony_ci size_t *, struct otp_info *); 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_cistatic int cfi_intelext_suspend (struct mtd_info *); 838c2ecf20Sopenharmony_cistatic void cfi_intelext_resume (struct mtd_info *); 848c2ecf20Sopenharmony_cistatic int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void cfi_intelext_destroy(struct mtd_info *); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0001(struct map_info *, int); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_intelext_setup (struct mtd_info *); 918c2ecf20Sopenharmony_cistatic int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, 948c2ecf20Sopenharmony_ci size_t *retlen, void **virt, resource_size_t *phys); 958c2ecf20Sopenharmony_cistatic int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode); 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_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * *********** SETUP AND PROBE BITS *********** 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic struct mtd_chip_driver cfi_intelext_chipdrv = { 1098c2ecf20Sopenharmony_ci .probe = NULL, /* Not usable directly */ 1108c2ecf20Sopenharmony_ci .destroy = cfi_intelext_destroy, 1118c2ecf20Sopenharmony_ci .name = "cfi_cmdset_0001", 1128c2ecf20Sopenharmony_ci .module = THIS_MODULE 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* #define DEBUG_LOCK_BITS */ 1168c2ecf20Sopenharmony_ci/* #define DEBUG_CFI_FEATURES */ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 1198c2ecf20Sopenharmony_cistatic void cfi_tell_features(struct cfi_pri_intelext *extp) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci int i; 1228c2ecf20Sopenharmony_ci printk(" Extended Query version %c.%c\n", extp->MajorVersion, extp->MinorVersion); 1238c2ecf20Sopenharmony_ci printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); 1248c2ecf20Sopenharmony_ci printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); 1258c2ecf20Sopenharmony_ci printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); 1268c2ecf20Sopenharmony_ci printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); 1278c2ecf20Sopenharmony_ci printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); 1288c2ecf20Sopenharmony_ci printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); 1298c2ecf20Sopenharmony_ci printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); 1308c2ecf20Sopenharmony_ci printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); 1318c2ecf20Sopenharmony_ci printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); 1328c2ecf20Sopenharmony_ci printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); 1338c2ecf20Sopenharmony_ci printk(" - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported"); 1348c2ecf20Sopenharmony_ci printk(" - Extended Flash Array: %s\n", extp->FeatureSupport&1024?"supported":"unsupported"); 1358c2ecf20Sopenharmony_ci for (i=11; i<32; i++) { 1368c2ecf20Sopenharmony_ci if (extp->FeatureSupport & (1<<i)) 1378c2ecf20Sopenharmony_ci printk(" - Unknown Bit %X: supported\n", i); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); 1418c2ecf20Sopenharmony_ci printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); 1428c2ecf20Sopenharmony_ci for (i=1; i<8; i++) { 1438c2ecf20Sopenharmony_ci if (extp->SuspendCmdSupport & (1<<i)) 1448c2ecf20Sopenharmony_ci printk(" - Unknown Bit %X: supported\n", i); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); 1488c2ecf20Sopenharmony_ci printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); 1498c2ecf20Sopenharmony_ci printk(" - Lock-Down Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); 1508c2ecf20Sopenharmony_ci for (i=2; i<3; i++) { 1518c2ecf20Sopenharmony_ci if (extp->BlkStatusRegMask & (1<<i)) 1528c2ecf20Sopenharmony_ci printk(" - Unknown Bit %X Active: yes\n",i); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci printk(" - EFA Lock Bit: %s\n", extp->BlkStatusRegMask&16?"yes":"no"); 1558c2ecf20Sopenharmony_ci printk(" - EFA Lock-Down Bit: %s\n", extp->BlkStatusRegMask&32?"yes":"no"); 1568c2ecf20Sopenharmony_ci for (i=6; i<16; i++) { 1578c2ecf20Sopenharmony_ci if (extp->BlkStatusRegMask & (1<<i)) 1588c2ecf20Sopenharmony_ci printk(" - Unknown Bit %X Active: yes\n",i); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", 1628c2ecf20Sopenharmony_ci extp->VccOptimal >> 4, extp->VccOptimal & 0xf); 1638c2ecf20Sopenharmony_ci if (extp->VppOptimal) 1648c2ecf20Sopenharmony_ci printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", 1658c2ecf20Sopenharmony_ci extp->VppOptimal >> 4, extp->VppOptimal & 0xf); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci#endif 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Atmel chips don't use the same PRI format as Intel chips */ 1708c2ecf20Sopenharmony_cistatic void fixup_convert_atmel_pri(struct mtd_info *mtd) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 1738c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 1748c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 1758c2ecf20Sopenharmony_ci struct cfi_pri_atmel atmel_pri; 1768c2ecf20Sopenharmony_ci uint32_t features = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Reverse byteswapping */ 1798c2ecf20Sopenharmony_ci extp->FeatureSupport = cpu_to_le32(extp->FeatureSupport); 1808c2ecf20Sopenharmony_ci extp->BlkStatusRegMask = cpu_to_le16(extp->BlkStatusRegMask); 1818c2ecf20Sopenharmony_ci extp->ProtRegAddr = cpu_to_le16(extp->ProtRegAddr); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci memcpy(&atmel_pri, extp, sizeof(atmel_pri)); 1848c2ecf20Sopenharmony_ci memset((char *)extp + 5, 0, sizeof(*extp) - 5); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci printk(KERN_ERR "atmel Features: %02x\n", atmel_pri.Features); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x01) /* chip erase supported */ 1898c2ecf20Sopenharmony_ci features |= (1<<0); 1908c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x02) /* erase suspend supported */ 1918c2ecf20Sopenharmony_ci features |= (1<<1); 1928c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x04) /* program suspend supported */ 1938c2ecf20Sopenharmony_ci features |= (1<<2); 1948c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x08) /* simultaneous operations supported */ 1958c2ecf20Sopenharmony_ci features |= (1<<9); 1968c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x20) /* page mode read supported */ 1978c2ecf20Sopenharmony_ci features |= (1<<7); 1988c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x40) /* queued erase supported */ 1998c2ecf20Sopenharmony_ci features |= (1<<4); 2008c2ecf20Sopenharmony_ci if (atmel_pri.Features & 0x80) /* Protection bits supported */ 2018c2ecf20Sopenharmony_ci features |= (1<<6); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci extp->FeatureSupport = features; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* burst write mode not supported */ 2068c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutTyp = 0; 2078c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutMax = 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void fixup_at49bv640dx_lock(struct mtd_info *mtd) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2138c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2148c2ecf20Sopenharmony_ci struct cfi_pri_intelext *cfip = cfi->cmdset_priv; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci cfip->FeatureSupport |= (1 << 5); 2178c2ecf20Sopenharmony_ci mtd->flags |= MTD_POWERUP_LOCK; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE 2218c2ecf20Sopenharmony_ci/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ 2228c2ecf20Sopenharmony_cistatic void fixup_intel_strataflash(struct mtd_info *mtd) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2258c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2268c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci printk(KERN_WARNING "cfi_cmdset_0001: Suspend " 2298c2ecf20Sopenharmony_ci "erase on write disabled.\n"); 2308c2ecf20Sopenharmony_ci extp->SuspendCmdSupport &= ~1; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci#endif 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND 2358c2ecf20Sopenharmony_cistatic void fixup_no_write_suspend(struct mtd_info *mtd) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2388c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2398c2ecf20Sopenharmony_ci struct cfi_pri_intelext *cfip = cfi->cmdset_priv; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (cfip && (cfip->FeatureSupport&4)) { 2428c2ecf20Sopenharmony_ci cfip->FeatureSupport &= ~4; 2438c2ecf20Sopenharmony_ci printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n"); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci#endif 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void fixup_st_m28w320ct(struct mtd_info *mtd) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2518c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ 2548c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void fixup_st_m28w320cb(struct mtd_info *mtd) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2608c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Note this is done after the region info is endian swapped */ 2638c2ecf20Sopenharmony_ci cfi->cfiq->EraseRegionInfo[1] = 2648c2ecf20Sopenharmony_ci (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int is_LH28F640BF(struct cfi_private *cfi) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci /* Sharp LH28F640BF Family */ 2708c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_SHARP && ( 2718c2ecf20Sopenharmony_ci cfi->id == LH28F640BFHE_PTTL90 || cfi->id == LH28F640BFHE_PBTL90 || 2728c2ecf20Sopenharmony_ci cfi->id == LH28F640BFHE_PTTL70A || cfi->id == LH28F640BFHE_PBTL70A)) 2738c2ecf20Sopenharmony_ci return 1; 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void fixup_LH28F640BF(struct mtd_info *mtd) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 2808c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2818c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Reset the Partition Configuration Register on LH28F640BF 2848c2ecf20Sopenharmony_ci * to a single partition (PCR = 0x000): PCR is embedded into A0-A15. */ 2858c2ecf20Sopenharmony_ci if (is_LH28F640BF(cfi)) { 2868c2ecf20Sopenharmony_ci printk(KERN_INFO "Reset Partition Config. Register: 1 Partition of 4 planes\n"); 2878c2ecf20Sopenharmony_ci map_write(map, CMD(0x60), 0); 2888c2ecf20Sopenharmony_ci map_write(map, CMD(0x04), 0); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* We have set one single partition thus 2918c2ecf20Sopenharmony_ci * Simultaneous Operations are not allowed */ 2928c2ecf20Sopenharmony_ci printk(KERN_INFO "cfi_cmdset_0001: Simultaneous Operations disabled\n"); 2938c2ecf20Sopenharmony_ci extp->FeatureSupport &= ~512; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void fixup_use_point(struct mtd_info *mtd) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3008c2ecf20Sopenharmony_ci if (!mtd->_point && map_is_linear(map)) { 3018c2ecf20Sopenharmony_ci mtd->_point = cfi_intelext_point; 3028c2ecf20Sopenharmony_ci mtd->_unpoint = cfi_intelext_unpoint; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void fixup_use_write_buffers(struct mtd_info *mtd) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3098c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3108c2ecf20Sopenharmony_ci if (cfi->cfiq->BufWriteTimeoutTyp) { 3118c2ecf20Sopenharmony_ci printk(KERN_INFO "Using buffer write method\n" ); 3128c2ecf20Sopenharmony_ci mtd->_write = cfi_intelext_write_buffers; 3138c2ecf20Sopenharmony_ci mtd->_writev = cfi_intelext_writev; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* 3188c2ecf20Sopenharmony_ci * Some chips power-up with all sectors locked by default. 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_cistatic void fixup_unlock_powerup_lock(struct mtd_info *mtd) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3238c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3248c2ecf20Sopenharmony_ci struct cfi_pri_intelext *cfip = cfi->cmdset_priv; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (cfip->FeatureSupport&32) { 3278c2ecf20Sopenharmony_ci printk(KERN_INFO "Using auto-unlock on power-up/resume\n" ); 3288c2ecf20Sopenharmony_ci mtd->flags |= MTD_POWERUP_LOCK; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct cfi_fixup cfi_fixup_table[] = { 3338c2ecf20Sopenharmony_ci { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri }, 3348c2ecf20Sopenharmony_ci { CFI_MFR_ATMEL, AT49BV640D, fixup_at49bv640dx_lock }, 3358c2ecf20Sopenharmony_ci { CFI_MFR_ATMEL, AT49BV640DT, fixup_at49bv640dx_lock }, 3368c2ecf20Sopenharmony_ci#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE 3378c2ecf20Sopenharmony_ci { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash }, 3388c2ecf20Sopenharmony_ci#endif 3398c2ecf20Sopenharmony_ci#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND 3408c2ecf20Sopenharmony_ci { CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend }, 3418c2ecf20Sopenharmony_ci#endif 3428c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE 3438c2ecf20Sopenharmony_ci { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers }, 3448c2ecf20Sopenharmony_ci#endif 3458c2ecf20Sopenharmony_ci { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct }, 3468c2ecf20Sopenharmony_ci { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb }, 3478c2ecf20Sopenharmony_ci { CFI_MFR_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock }, 3488c2ecf20Sopenharmony_ci { CFI_MFR_SHARP, CFI_ID_ANY, fixup_unlock_powerup_lock }, 3498c2ecf20Sopenharmony_ci { CFI_MFR_SHARP, CFI_ID_ANY, fixup_LH28F640BF }, 3508c2ecf20Sopenharmony_ci { 0, 0, NULL } 3518c2ecf20Sopenharmony_ci}; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic struct cfi_fixup jedec_fixup_table[] = { 3548c2ecf20Sopenharmony_ci { CFI_MFR_INTEL, I82802AB, fixup_use_fwh_lock }, 3558c2ecf20Sopenharmony_ci { CFI_MFR_INTEL, I82802AC, fixup_use_fwh_lock }, 3568c2ecf20Sopenharmony_ci { CFI_MFR_ST, M50LPW080, fixup_use_fwh_lock }, 3578c2ecf20Sopenharmony_ci { CFI_MFR_ST, M50FLW080A, fixup_use_fwh_lock }, 3588c2ecf20Sopenharmony_ci { CFI_MFR_ST, M50FLW080B, fixup_use_fwh_lock }, 3598c2ecf20Sopenharmony_ci { 0, 0, NULL } 3608c2ecf20Sopenharmony_ci}; 3618c2ecf20Sopenharmony_cistatic struct cfi_fixup fixup_table[] = { 3628c2ecf20Sopenharmony_ci /* The CFI vendor ids and the JEDEC vendor IDs appear 3638c2ecf20Sopenharmony_ci * to be common. It is like the devices id's are as 3648c2ecf20Sopenharmony_ci * well. This table is to pick all cases where 3658c2ecf20Sopenharmony_ci * we know that is the case. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point }, 3688c2ecf20Sopenharmony_ci { 0, 0, NULL } 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void cfi_fixup_major_minor(struct cfi_private *cfi, 3728c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_INTEL && 3758c2ecf20Sopenharmony_ci cfi->id == PF38F4476 && extp->MinorVersion == '3') 3768c2ecf20Sopenharmony_ci extp->MinorVersion = '1'; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * Micron(was Numonyx) 1Gbit bottom boot are buggy w.r.t 3838c2ecf20Sopenharmony_ci * Erase Supend for their small Erase Blocks(0x8000) 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_INTEL && cfi->id == M28F00AP30) 3868c2ecf20Sopenharmony_ci return 1; 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic inline struct cfi_pri_intelext * 3918c2ecf20Sopenharmony_ciread_pri_intelext(struct map_info *map, __u16 adr) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3948c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp; 3958c2ecf20Sopenharmony_ci unsigned int extra_size = 0; 3968c2ecf20Sopenharmony_ci unsigned int extp_size = sizeof(*extp); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci again: 3998c2ecf20Sopenharmony_ci extp = (struct cfi_pri_intelext *)cfi_read_pri(map, adr, extp_size, "Intel/Sharp"); 4008c2ecf20Sopenharmony_ci if (!extp) 4018c2ecf20Sopenharmony_ci return NULL; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci cfi_fixup_major_minor(cfi, extp); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (extp->MajorVersion != '1' || 4068c2ecf20Sopenharmony_ci (extp->MinorVersion < '0' || extp->MinorVersion > '5')) { 4078c2ecf20Sopenharmony_ci printk(KERN_ERR " Unknown Intel/Sharp Extended Query " 4088c2ecf20Sopenharmony_ci "version %c.%c.\n", extp->MajorVersion, 4098c2ecf20Sopenharmony_ci extp->MinorVersion); 4108c2ecf20Sopenharmony_ci kfree(extp); 4118c2ecf20Sopenharmony_ci return NULL; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Do some byteswapping if necessary */ 4158c2ecf20Sopenharmony_ci extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); 4168c2ecf20Sopenharmony_ci extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); 4178c2ecf20Sopenharmony_ci extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (extp->MinorVersion >= '0') { 4208c2ecf20Sopenharmony_ci extra_size = 0; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Protection Register info */ 4238c2ecf20Sopenharmony_ci if (extp->NumProtectionFields) { 4248c2ecf20Sopenharmony_ci struct cfi_intelext_otpinfo *otp = 4258c2ecf20Sopenharmony_ci (struct cfi_intelext_otpinfo *)&extp->extra[0]; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci extra_size += (extp->NumProtectionFields - 1) * 4288c2ecf20Sopenharmony_ci sizeof(struct cfi_intelext_otpinfo); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (extp_size >= sizeof(*extp) + extra_size) { 4318c2ecf20Sopenharmony_ci int i; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* Do some byteswapping if necessary */ 4348c2ecf20Sopenharmony_ci for (i = 0; i < extp->NumProtectionFields - 1; i++) { 4358c2ecf20Sopenharmony_ci otp->ProtRegAddr = le32_to_cpu(otp->ProtRegAddr); 4368c2ecf20Sopenharmony_ci otp->FactGroups = le16_to_cpu(otp->FactGroups); 4378c2ecf20Sopenharmony_ci otp->UserGroups = le16_to_cpu(otp->UserGroups); 4388c2ecf20Sopenharmony_ci otp++; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (extp->MinorVersion >= '1') { 4458c2ecf20Sopenharmony_ci /* Burst Read info */ 4468c2ecf20Sopenharmony_ci extra_size += 2; 4478c2ecf20Sopenharmony_ci if (extp_size < sizeof(*extp) + extra_size) 4488c2ecf20Sopenharmony_ci goto need_more; 4498c2ecf20Sopenharmony_ci extra_size += extp->extra[extra_size - 1]; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (extp->MinorVersion >= '3') { 4538c2ecf20Sopenharmony_ci int nb_parts, i; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Number of hardware-partitions */ 4568c2ecf20Sopenharmony_ci extra_size += 1; 4578c2ecf20Sopenharmony_ci if (extp_size < sizeof(*extp) + extra_size) 4588c2ecf20Sopenharmony_ci goto need_more; 4598c2ecf20Sopenharmony_ci nb_parts = extp->extra[extra_size - 1]; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* skip the sizeof(partregion) field in CFI 1.4 */ 4628c2ecf20Sopenharmony_ci if (extp->MinorVersion >= '4') 4638c2ecf20Sopenharmony_ci extra_size += 2; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci for (i = 0; i < nb_parts; i++) { 4668c2ecf20Sopenharmony_ci struct cfi_intelext_regioninfo *rinfo; 4678c2ecf20Sopenharmony_ci rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[extra_size]; 4688c2ecf20Sopenharmony_ci extra_size += sizeof(*rinfo); 4698c2ecf20Sopenharmony_ci if (extp_size < sizeof(*extp) + extra_size) 4708c2ecf20Sopenharmony_ci goto need_more; 4718c2ecf20Sopenharmony_ci rinfo->NumIdentPartitions=le16_to_cpu(rinfo->NumIdentPartitions); 4728c2ecf20Sopenharmony_ci extra_size += (rinfo->NumBlockTypes - 1) 4738c2ecf20Sopenharmony_ci * sizeof(struct cfi_intelext_blockinfo); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (extp->MinorVersion >= '4') 4778c2ecf20Sopenharmony_ci extra_size += sizeof(struct cfi_intelext_programming_regioninfo); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (extp_size < sizeof(*extp) + extra_size) { 4808c2ecf20Sopenharmony_ci need_more: 4818c2ecf20Sopenharmony_ci extp_size = sizeof(*extp) + extra_size; 4828c2ecf20Sopenharmony_ci kfree(extp); 4838c2ecf20Sopenharmony_ci if (extp_size > 4096) { 4848c2ecf20Sopenharmony_ci printk(KERN_ERR 4858c2ecf20Sopenharmony_ci "%s: cfi_pri_intelext is too fat\n", 4868c2ecf20Sopenharmony_ci __func__); 4878c2ecf20Sopenharmony_ci return NULL; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci goto again; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return extp; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 4998c2ecf20Sopenharmony_ci struct mtd_info *mtd; 5008c2ecf20Sopenharmony_ci int i; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); 5038c2ecf20Sopenharmony_ci if (!mtd) 5048c2ecf20Sopenharmony_ci return NULL; 5058c2ecf20Sopenharmony_ci mtd->priv = map; 5068c2ecf20Sopenharmony_ci mtd->type = MTD_NORFLASH; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Fill in the default mtd operations */ 5098c2ecf20Sopenharmony_ci mtd->_erase = cfi_intelext_erase_varsize; 5108c2ecf20Sopenharmony_ci mtd->_read = cfi_intelext_read; 5118c2ecf20Sopenharmony_ci mtd->_write = cfi_intelext_write_words; 5128c2ecf20Sopenharmony_ci mtd->_sync = cfi_intelext_sync; 5138c2ecf20Sopenharmony_ci mtd->_lock = cfi_intelext_lock; 5148c2ecf20Sopenharmony_ci mtd->_unlock = cfi_intelext_unlock; 5158c2ecf20Sopenharmony_ci mtd->_is_locked = cfi_intelext_is_locked; 5168c2ecf20Sopenharmony_ci mtd->_suspend = cfi_intelext_suspend; 5178c2ecf20Sopenharmony_ci mtd->_resume = cfi_intelext_resume; 5188c2ecf20Sopenharmony_ci mtd->flags = MTD_CAP_NORFLASH; 5198c2ecf20Sopenharmony_ci mtd->name = map->name; 5208c2ecf20Sopenharmony_ci mtd->writesize = 1; 5218c2ecf20Sopenharmony_ci mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci mtd->reboot_notifier.notifier_call = cfi_intelext_reboot; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (cfi->cfi_mode == CFI_MODE_CFI) { 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * It's a real CFI chip, not one for which the probe 5288c2ecf20Sopenharmony_ci * routine faked a CFI structure. So we read the feature 5298c2ecf20Sopenharmony_ci * table from it. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; 5328c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci extp = read_pri_intelext(map, adr); 5358c2ecf20Sopenharmony_ci if (!extp) { 5368c2ecf20Sopenharmony_ci kfree(mtd); 5378c2ecf20Sopenharmony_ci return NULL; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* Install our own private info structure */ 5418c2ecf20Sopenharmony_ci cfi->cmdset_priv = extp; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci cfi_fixup(mtd, cfi_fixup_table); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 5468c2ecf20Sopenharmony_ci /* Tell the user about it in lots of lovely detail */ 5478c2ecf20Sopenharmony_ci cfi_tell_features(extp); 5488c2ecf20Sopenharmony_ci#endif 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if(extp->SuspendCmdSupport & 1) { 5518c2ecf20Sopenharmony_ci printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci else if (cfi->cfi_mode == CFI_MODE_JEDEC) { 5558c2ecf20Sopenharmony_ci /* Apply jedec specific fixups */ 5568c2ecf20Sopenharmony_ci cfi_fixup(mtd, jedec_fixup_table); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci /* Apply generic fixups */ 5598c2ecf20Sopenharmony_ci cfi_fixup(mtd, fixup_table); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci for (i=0; i< cfi->numchips; i++) { 5628c2ecf20Sopenharmony_ci if (cfi->cfiq->WordWriteTimeoutTyp) 5638c2ecf20Sopenharmony_ci cfi->chips[i].word_write_time = 5648c2ecf20Sopenharmony_ci 1<<cfi->cfiq->WordWriteTimeoutTyp; 5658c2ecf20Sopenharmony_ci else 5668c2ecf20Sopenharmony_ci cfi->chips[i].word_write_time = 50000; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (cfi->cfiq->BufWriteTimeoutTyp) 5698c2ecf20Sopenharmony_ci cfi->chips[i].buffer_write_time = 5708c2ecf20Sopenharmony_ci 1<<cfi->cfiq->BufWriteTimeoutTyp; 5718c2ecf20Sopenharmony_ci /* No default; if it isn't specified, we won't use it */ 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (cfi->cfiq->BlockEraseTimeoutTyp) 5748c2ecf20Sopenharmony_ci cfi->chips[i].erase_time = 5758c2ecf20Sopenharmony_ci 1000<<cfi->cfiq->BlockEraseTimeoutTyp; 5768c2ecf20Sopenharmony_ci else 5778c2ecf20Sopenharmony_ci cfi->chips[i].erase_time = 2000000; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (cfi->cfiq->WordWriteTimeoutTyp && 5808c2ecf20Sopenharmony_ci cfi->cfiq->WordWriteTimeoutMax) 5818c2ecf20Sopenharmony_ci cfi->chips[i].word_write_time_max = 5828c2ecf20Sopenharmony_ci 1<<(cfi->cfiq->WordWriteTimeoutTyp + 5838c2ecf20Sopenharmony_ci cfi->cfiq->WordWriteTimeoutMax); 5848c2ecf20Sopenharmony_ci else 5858c2ecf20Sopenharmony_ci cfi->chips[i].word_write_time_max = 50000 * 8; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (cfi->cfiq->BufWriteTimeoutTyp && 5888c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutMax) 5898c2ecf20Sopenharmony_ci cfi->chips[i].buffer_write_time_max = 5908c2ecf20Sopenharmony_ci 1<<(cfi->cfiq->BufWriteTimeoutTyp + 5918c2ecf20Sopenharmony_ci cfi->cfiq->BufWriteTimeoutMax); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (cfi->cfiq->BlockEraseTimeoutTyp && 5948c2ecf20Sopenharmony_ci cfi->cfiq->BlockEraseTimeoutMax) 5958c2ecf20Sopenharmony_ci cfi->chips[i].erase_time_max = 5968c2ecf20Sopenharmony_ci 1000<<(cfi->cfiq->BlockEraseTimeoutTyp + 5978c2ecf20Sopenharmony_ci cfi->cfiq->BlockEraseTimeoutMax); 5988c2ecf20Sopenharmony_ci else 5998c2ecf20Sopenharmony_ci cfi->chips[i].erase_time_max = 2000000 * 8; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci cfi->chips[i].ref_point_counter = 0; 6028c2ecf20Sopenharmony_ci init_waitqueue_head(&(cfi->chips[i].wq)); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci map->fldrv = &cfi_intelext_chipdrv; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return cfi_intelext_setup(mtd); 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0003(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001"))); 6108c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0200(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001"))); 6118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0001); 6128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0003); 6138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0200); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 6188c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 6198c2ecf20Sopenharmony_ci unsigned long offset = 0; 6208c2ecf20Sopenharmony_ci int i,j; 6218c2ecf20Sopenharmony_ci unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci mtd->size = devsize * cfi->numchips; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; 6288c2ecf20Sopenharmony_ci mtd->eraseregions = kcalloc(mtd->numeraseregions, 6298c2ecf20Sopenharmony_ci sizeof(struct mtd_erase_region_info), 6308c2ecf20Sopenharmony_ci GFP_KERNEL); 6318c2ecf20Sopenharmony_ci if (!mtd->eraseregions) 6328c2ecf20Sopenharmony_ci goto setup_err; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { 6358c2ecf20Sopenharmony_ci unsigned long ernum, ersize; 6368c2ecf20Sopenharmony_ci ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; 6378c2ecf20Sopenharmony_ci ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (mtd->erasesize < ersize) { 6408c2ecf20Sopenharmony_ci mtd->erasesize = ersize; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci for (j=0; j<cfi->numchips; j++) { 6438c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; 6448c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; 6458c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; 6468c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL); 6478c2ecf20Sopenharmony_ci if (!mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap) 6488c2ecf20Sopenharmony_ci goto setup_err; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci offset += (ersize * ernum); 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (offset != devsize) { 6548c2ecf20Sopenharmony_ci /* Argh */ 6558c2ecf20Sopenharmony_ci printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); 6568c2ecf20Sopenharmony_ci goto setup_err; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci for (i=0; i<mtd->numeraseregions;i++){ 6608c2ecf20Sopenharmony_ci printk(KERN_DEBUG "erase region %d: offset=0x%llx,size=0x%x,blocks=%d\n", 6618c2ecf20Sopenharmony_ci i,(unsigned long long)mtd->eraseregions[i].offset, 6628c2ecf20Sopenharmony_ci mtd->eraseregions[i].erasesize, 6638c2ecf20Sopenharmony_ci mtd->eraseregions[i].numblocks); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_OTP 6678c2ecf20Sopenharmony_ci mtd->_read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; 6688c2ecf20Sopenharmony_ci mtd->_read_user_prot_reg = cfi_intelext_read_user_prot_reg; 6698c2ecf20Sopenharmony_ci mtd->_write_user_prot_reg = cfi_intelext_write_user_prot_reg; 6708c2ecf20Sopenharmony_ci mtd->_lock_user_prot_reg = cfi_intelext_lock_user_prot_reg; 6718c2ecf20Sopenharmony_ci mtd->_get_fact_prot_info = cfi_intelext_get_fact_prot_info; 6728c2ecf20Sopenharmony_ci mtd->_get_user_prot_info = cfi_intelext_get_user_prot_info; 6738c2ecf20Sopenharmony_ci#endif 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* This function has the potential to distort the reality 6768c2ecf20Sopenharmony_ci a bit and therefore should be called last. */ 6778c2ecf20Sopenharmony_ci if (cfi_intelext_partition_fixup(mtd, &cfi) != 0) 6788c2ecf20Sopenharmony_ci goto setup_err; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 6818c2ecf20Sopenharmony_ci register_reboot_notifier(&mtd->reboot_notifier); 6828c2ecf20Sopenharmony_ci return mtd; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci setup_err: 6858c2ecf20Sopenharmony_ci if (mtd->eraseregions) 6868c2ecf20Sopenharmony_ci for (i=0; i<cfi->cfiq->NumEraseRegions; i++) 6878c2ecf20Sopenharmony_ci for (j=0; j<cfi->numchips; j++) 6888c2ecf20Sopenharmony_ci kfree(mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap); 6898c2ecf20Sopenharmony_ci kfree(mtd->eraseregions); 6908c2ecf20Sopenharmony_ci kfree(mtd); 6918c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 6928c2ecf20Sopenharmony_ci return NULL; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic int cfi_intelext_partition_fixup(struct mtd_info *mtd, 6968c2ecf20Sopenharmony_ci struct cfi_private **pcfi) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 6998c2ecf20Sopenharmony_ci struct cfi_private *cfi = *pcfi; 7008c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* 7038c2ecf20Sopenharmony_ci * Probing of multi-partition flash chips. 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * To support multiple partitions when available, we simply arrange 7068c2ecf20Sopenharmony_ci * for each of them to have their own flchip structure even if they 7078c2ecf20Sopenharmony_ci * are on the same physical chip. This means completely recreating 7088c2ecf20Sopenharmony_ci * a new cfi_private structure right here which is a blatent code 7098c2ecf20Sopenharmony_ci * layering violation, but this is still the least intrusive 7108c2ecf20Sopenharmony_ci * arrangement at this point. This can be rearranged in the future 7118c2ecf20Sopenharmony_ci * if someone feels motivated enough. --nico 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_ci if (extp && extp->MajorVersion == '1' && extp->MinorVersion >= '3' 7148c2ecf20Sopenharmony_ci && extp->FeatureSupport & (1 << 9)) { 7158c2ecf20Sopenharmony_ci int offs = 0; 7168c2ecf20Sopenharmony_ci struct cfi_private *newcfi; 7178c2ecf20Sopenharmony_ci struct flchip *chip; 7188c2ecf20Sopenharmony_ci struct flchip_shared *shared; 7198c2ecf20Sopenharmony_ci int numregions, numparts, partshift, numvirtchips, i, j; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* Protection Register info */ 7228c2ecf20Sopenharmony_ci if (extp->NumProtectionFields) 7238c2ecf20Sopenharmony_ci offs = (extp->NumProtectionFields - 1) * 7248c2ecf20Sopenharmony_ci sizeof(struct cfi_intelext_otpinfo); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* Burst Read info */ 7278c2ecf20Sopenharmony_ci offs += extp->extra[offs+1]+2; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Number of partition regions */ 7308c2ecf20Sopenharmony_ci numregions = extp->extra[offs]; 7318c2ecf20Sopenharmony_ci offs += 1; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* skip the sizeof(partregion) field in CFI 1.4 */ 7348c2ecf20Sopenharmony_ci if (extp->MinorVersion >= '4') 7358c2ecf20Sopenharmony_ci offs += 2; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* Number of hardware partitions */ 7388c2ecf20Sopenharmony_ci numparts = 0; 7398c2ecf20Sopenharmony_ci for (i = 0; i < numregions; i++) { 7408c2ecf20Sopenharmony_ci struct cfi_intelext_regioninfo *rinfo; 7418c2ecf20Sopenharmony_ci rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[offs]; 7428c2ecf20Sopenharmony_ci numparts += rinfo->NumIdentPartitions; 7438c2ecf20Sopenharmony_ci offs += sizeof(*rinfo) 7448c2ecf20Sopenharmony_ci + (rinfo->NumBlockTypes - 1) * 7458c2ecf20Sopenharmony_ci sizeof(struct cfi_intelext_blockinfo); 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci if (!numparts) 7498c2ecf20Sopenharmony_ci numparts = 1; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* Programming Region info */ 7528c2ecf20Sopenharmony_ci if (extp->MinorVersion >= '4') { 7538c2ecf20Sopenharmony_ci struct cfi_intelext_programming_regioninfo *prinfo; 7548c2ecf20Sopenharmony_ci prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; 7558c2ecf20Sopenharmony_ci mtd->writesize = cfi->interleave << prinfo->ProgRegShift; 7568c2ecf20Sopenharmony_ci mtd->flags &= ~MTD_BIT_WRITEABLE; 7578c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", 7588c2ecf20Sopenharmony_ci map->name, mtd->writesize, 7598c2ecf20Sopenharmony_ci cfi->interleave * prinfo->ControlValid, 7608c2ecf20Sopenharmony_ci cfi->interleave * prinfo->ControlInvalid); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* 7648c2ecf20Sopenharmony_ci * All functions below currently rely on all chips having 7658c2ecf20Sopenharmony_ci * the same geometry so we'll just assume that all hardware 7668c2ecf20Sopenharmony_ci * partitions are of the same size too. 7678c2ecf20Sopenharmony_ci */ 7688c2ecf20Sopenharmony_ci partshift = cfi->chipshift - __ffs(numparts); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if ((1 << partshift) < mtd->erasesize) { 7718c2ecf20Sopenharmony_ci printk( KERN_ERR 7728c2ecf20Sopenharmony_ci "%s: bad number of hw partitions (%d)\n", 7738c2ecf20Sopenharmony_ci __func__, numparts); 7748c2ecf20Sopenharmony_ci return -EINVAL; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci numvirtchips = cfi->numchips * numparts; 7788c2ecf20Sopenharmony_ci newcfi = kmalloc(struct_size(newcfi, chips, numvirtchips), 7798c2ecf20Sopenharmony_ci GFP_KERNEL); 7808c2ecf20Sopenharmony_ci if (!newcfi) 7818c2ecf20Sopenharmony_ci return -ENOMEM; 7828c2ecf20Sopenharmony_ci shared = kmalloc_array(cfi->numchips, 7838c2ecf20Sopenharmony_ci sizeof(struct flchip_shared), 7848c2ecf20Sopenharmony_ci GFP_KERNEL); 7858c2ecf20Sopenharmony_ci if (!shared) { 7868c2ecf20Sopenharmony_ci kfree(newcfi); 7878c2ecf20Sopenharmony_ci return -ENOMEM; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci memcpy(newcfi, cfi, sizeof(struct cfi_private)); 7908c2ecf20Sopenharmony_ci newcfi->numchips = numvirtchips; 7918c2ecf20Sopenharmony_ci newcfi->chipshift = partshift; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci chip = &newcfi->chips[0]; 7948c2ecf20Sopenharmony_ci for (i = 0; i < cfi->numchips; i++) { 7958c2ecf20Sopenharmony_ci shared[i].writing = shared[i].erasing = NULL; 7968c2ecf20Sopenharmony_ci mutex_init(&shared[i].lock); 7978c2ecf20Sopenharmony_ci for (j = 0; j < numparts; j++) { 7988c2ecf20Sopenharmony_ci *chip = cfi->chips[i]; 7998c2ecf20Sopenharmony_ci chip->start += j << partshift; 8008c2ecf20Sopenharmony_ci chip->priv = &shared[i]; 8018c2ecf20Sopenharmony_ci /* those should be reset too since 8028c2ecf20Sopenharmony_ci they create memory references. */ 8038c2ecf20Sopenharmony_ci init_waitqueue_head(&chip->wq); 8048c2ecf20Sopenharmony_ci mutex_init(&chip->mutex); 8058c2ecf20Sopenharmony_ci chip++; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: %d set(s) of %d interleaved chips " 8108c2ecf20Sopenharmony_ci "--> %d partitions of %d KiB\n", 8118c2ecf20Sopenharmony_ci map->name, cfi->numchips, cfi->interleave, 8128c2ecf20Sopenharmony_ci newcfi->numchips, 1<<(newcfi->chipshift-10)); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci map->fldrv_priv = newcfi; 8158c2ecf20Sopenharmony_ci *pcfi = newcfi; 8168c2ecf20Sopenharmony_ci kfree(cfi); 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/* 8238c2ecf20Sopenharmony_ci * *********** CHIP ACCESS FUNCTIONS *********** 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_cistatic int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 8288c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 8298c2ecf20Sopenharmony_ci map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01); 8308c2ecf20Sopenharmony_ci struct cfi_pri_intelext *cfip = cfi->cmdset_priv; 8318c2ecf20Sopenharmony_ci unsigned long timeo = jiffies + HZ; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* Prevent setting state FL_SYNCING for chip in suspended state. */ 8348c2ecf20Sopenharmony_ci if (mode == FL_SYNCING && chip->oldstate != FL_READY) 8358c2ecf20Sopenharmony_ci goto sleep; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci switch (chip->state) { 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci case FL_STATUS: 8408c2ecf20Sopenharmony_ci for (;;) { 8418c2ecf20Sopenharmony_ci status = map_read(map, adr); 8428c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* At this point we're fine with write operations 8468c2ecf20Sopenharmony_ci in other partitions as they don't conflict. */ 8478c2ecf20Sopenharmony_ci if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8518c2ecf20Sopenharmony_ci cfi_udelay(1); 8528c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 8538c2ecf20Sopenharmony_ci /* Someone else might have been playing with it. */ 8548c2ecf20Sopenharmony_ci return -EAGAIN; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci fallthrough; 8578c2ecf20Sopenharmony_ci case FL_READY: 8588c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 8598c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 8608c2ecf20Sopenharmony_ci return 0; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci case FL_ERASING: 8638c2ecf20Sopenharmony_ci if (!cfip || 8648c2ecf20Sopenharmony_ci !(cfip->FeatureSupport & 2) || 8658c2ecf20Sopenharmony_ci !(mode == FL_READY || mode == FL_POINT || 8668c2ecf20Sopenharmony_ci (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) 8678c2ecf20Sopenharmony_ci goto sleep; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* Do not allow suspend iff read/write to EB address */ 8708c2ecf20Sopenharmony_ci if ((adr & chip->in_progress_block_mask) == 8718c2ecf20Sopenharmony_ci chip->in_progress_block_addr) 8728c2ecf20Sopenharmony_ci goto sleep; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* do not suspend small EBs, buggy Micron Chips */ 8758c2ecf20Sopenharmony_ci if (cfi_is_micron_28F00AP30(cfi, chip) && 8768c2ecf20Sopenharmony_ci (chip->in_progress_block_mask == ~(0x8000-1))) 8778c2ecf20Sopenharmony_ci goto sleep; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* Erase suspend */ 8808c2ecf20Sopenharmony_ci map_write(map, CMD(0xB0), chip->in_progress_block_addr); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* If the flash has finished erasing, then 'erase suspend' 8838c2ecf20Sopenharmony_ci * appears to make some (28F320) flash devices switch to 8848c2ecf20Sopenharmony_ci * 'read' mode. Make sure that we switch to 'read status' 8858c2ecf20Sopenharmony_ci * mode so we get the right data. --rmk 8868c2ecf20Sopenharmony_ci */ 8878c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), chip->in_progress_block_addr); 8888c2ecf20Sopenharmony_ci chip->oldstate = FL_ERASING; 8898c2ecf20Sopenharmony_ci chip->state = FL_ERASE_SUSPENDING; 8908c2ecf20Sopenharmony_ci chip->erase_suspended = 1; 8918c2ecf20Sopenharmony_ci for (;;) { 8928c2ecf20Sopenharmony_ci status = map_read(map, chip->in_progress_block_addr); 8938c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 8948c2ecf20Sopenharmony_ci break; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 8978c2ecf20Sopenharmony_ci /* Urgh. Resume and pretend we weren't here. 8988c2ecf20Sopenharmony_ci * Make sure we're in 'read status' mode if it had finished */ 8998c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 9008c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Chip not ready after erase " 9018c2ecf20Sopenharmony_ci "suspended: status = 0x%lx\n", map->name, status.x[0]); 9028c2ecf20Sopenharmony_ci return -EIO; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 9068c2ecf20Sopenharmony_ci cfi_udelay(1); 9078c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9088c2ecf20Sopenharmony_ci /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. 9098c2ecf20Sopenharmony_ci So we can just loop here. */ 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci case FL_XIP_WHILE_ERASING: 9158c2ecf20Sopenharmony_ci if (mode != FL_READY && mode != FL_POINT && 9168c2ecf20Sopenharmony_ci (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1))) 9178c2ecf20Sopenharmony_ci goto sleep; 9188c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 9198c2ecf20Sopenharmony_ci chip->state = FL_READY; 9208c2ecf20Sopenharmony_ci return 0; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci case FL_SHUTDOWN: 9238c2ecf20Sopenharmony_ci /* The machine is rebooting now,so no one can get chip anymore */ 9248c2ecf20Sopenharmony_ci return -EIO; 9258c2ecf20Sopenharmony_ci case FL_POINT: 9268c2ecf20Sopenharmony_ci /* Only if there's no operation suspended... */ 9278c2ecf20Sopenharmony_ci if (mode == FL_READY && chip->oldstate == FL_READY) 9288c2ecf20Sopenharmony_ci return 0; 9298c2ecf20Sopenharmony_ci fallthrough; 9308c2ecf20Sopenharmony_ci default: 9318c2ecf20Sopenharmony_ci sleep: 9328c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 9338c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 9348c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 9358c2ecf20Sopenharmony_ci schedule(); 9368c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 9378c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9388c2ecf20Sopenharmony_ci return -EAGAIN; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci int ret; 9458c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci retry: 9488c2ecf20Sopenharmony_ci if (chip->priv && 9498c2ecf20Sopenharmony_ci (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE 9508c2ecf20Sopenharmony_ci || mode == FL_SHUTDOWN) && chip->state != FL_SYNCING) { 9518c2ecf20Sopenharmony_ci /* 9528c2ecf20Sopenharmony_ci * OK. We have possibility for contention on the write/erase 9538c2ecf20Sopenharmony_ci * operations which are global to the real chip and not per 9548c2ecf20Sopenharmony_ci * partition. So let's fight it over in the partition which 9558c2ecf20Sopenharmony_ci * currently has authority on the operation. 9568c2ecf20Sopenharmony_ci * 9578c2ecf20Sopenharmony_ci * The rules are as follows: 9588c2ecf20Sopenharmony_ci * 9598c2ecf20Sopenharmony_ci * - any write operation must own shared->writing. 9608c2ecf20Sopenharmony_ci * 9618c2ecf20Sopenharmony_ci * - any erase operation must own _both_ shared->writing and 9628c2ecf20Sopenharmony_ci * shared->erasing. 9638c2ecf20Sopenharmony_ci * 9648c2ecf20Sopenharmony_ci * - contention arbitration is handled in the owner's context. 9658c2ecf20Sopenharmony_ci * 9668c2ecf20Sopenharmony_ci * The 'shared' struct can be read and/or written only when 9678c2ecf20Sopenharmony_ci * its lock is taken. 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci struct flchip_shared *shared = chip->priv; 9708c2ecf20Sopenharmony_ci struct flchip *contender; 9718c2ecf20Sopenharmony_ci mutex_lock(&shared->lock); 9728c2ecf20Sopenharmony_ci contender = shared->writing; 9738c2ecf20Sopenharmony_ci if (contender && contender != chip) { 9748c2ecf20Sopenharmony_ci /* 9758c2ecf20Sopenharmony_ci * The engine to perform desired operation on this 9768c2ecf20Sopenharmony_ci * partition is already in use by someone else. 9778c2ecf20Sopenharmony_ci * Let's fight over it in the context of the chip 9788c2ecf20Sopenharmony_ci * currently using it. If it is possible to suspend, 9798c2ecf20Sopenharmony_ci * that other partition will do just that, otherwise 9808c2ecf20Sopenharmony_ci * it'll happily send us to sleep. In any case, when 9818c2ecf20Sopenharmony_ci * get_chip returns success we're clear to go ahead. 9828c2ecf20Sopenharmony_ci */ 9838c2ecf20Sopenharmony_ci ret = mutex_trylock(&contender->mutex); 9848c2ecf20Sopenharmony_ci mutex_unlock(&shared->lock); 9858c2ecf20Sopenharmony_ci if (!ret) 9868c2ecf20Sopenharmony_ci goto retry; 9878c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 9888c2ecf20Sopenharmony_ci ret = chip_ready(map, contender, contender->start, mode); 9898c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 9928c2ecf20Sopenharmony_ci mutex_unlock(&contender->mutex); 9938c2ecf20Sopenharmony_ci goto retry; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci if (ret) { 9968c2ecf20Sopenharmony_ci mutex_unlock(&contender->mutex); 9978c2ecf20Sopenharmony_ci return ret; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci mutex_lock(&shared->lock); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* We should not own chip if it is already 10028c2ecf20Sopenharmony_ci * in FL_SYNCING state. Put contender and retry. */ 10038c2ecf20Sopenharmony_ci if (chip->state == FL_SYNCING) { 10048c2ecf20Sopenharmony_ci put_chip(map, contender, contender->start); 10058c2ecf20Sopenharmony_ci mutex_unlock(&contender->mutex); 10068c2ecf20Sopenharmony_ci goto retry; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci mutex_unlock(&contender->mutex); 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* Check if we already have suspended erase 10128c2ecf20Sopenharmony_ci * on this chip. Sleep. */ 10138c2ecf20Sopenharmony_ci if (mode == FL_ERASING && shared->erasing 10148c2ecf20Sopenharmony_ci && shared->erasing->oldstate == FL_ERASING) { 10158c2ecf20Sopenharmony_ci mutex_unlock(&shared->lock); 10168c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 10178c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 10188c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10198c2ecf20Sopenharmony_ci schedule(); 10208c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 10218c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 10228c2ecf20Sopenharmony_ci goto retry; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* We now own it */ 10268c2ecf20Sopenharmony_ci shared->writing = chip; 10278c2ecf20Sopenharmony_ci if (mode == FL_ERASING) 10288c2ecf20Sopenharmony_ci shared->erasing = chip; 10298c2ecf20Sopenharmony_ci mutex_unlock(&shared->lock); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci ret = chip_ready(map, chip, adr, mode); 10328c2ecf20Sopenharmony_ci if (ret == -EAGAIN) 10338c2ecf20Sopenharmony_ci goto retry; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return ret; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (chip->priv) { 10438c2ecf20Sopenharmony_ci struct flchip_shared *shared = chip->priv; 10448c2ecf20Sopenharmony_ci mutex_lock(&shared->lock); 10458c2ecf20Sopenharmony_ci if (shared->writing == chip && chip->oldstate == FL_READY) { 10468c2ecf20Sopenharmony_ci /* We own the ability to write, but we're done */ 10478c2ecf20Sopenharmony_ci shared->writing = shared->erasing; 10488c2ecf20Sopenharmony_ci if (shared->writing && shared->writing != chip) { 10498c2ecf20Sopenharmony_ci /* give back ownership to who we loaned it from */ 10508c2ecf20Sopenharmony_ci struct flchip *loaner = shared->writing; 10518c2ecf20Sopenharmony_ci mutex_lock(&loaner->mutex); 10528c2ecf20Sopenharmony_ci mutex_unlock(&shared->lock); 10538c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10548c2ecf20Sopenharmony_ci put_chip(map, loaner, loaner->start); 10558c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 10568c2ecf20Sopenharmony_ci mutex_unlock(&loaner->mutex); 10578c2ecf20Sopenharmony_ci wake_up(&chip->wq); 10588c2ecf20Sopenharmony_ci return; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci shared->erasing = NULL; 10618c2ecf20Sopenharmony_ci shared->writing = NULL; 10628c2ecf20Sopenharmony_ci } else if (shared->erasing == chip && shared->writing != chip) { 10638c2ecf20Sopenharmony_ci /* 10648c2ecf20Sopenharmony_ci * We own the ability to erase without the ability 10658c2ecf20Sopenharmony_ci * to write, which means the erase was suspended 10668c2ecf20Sopenharmony_ci * and some other partition is currently writing. 10678c2ecf20Sopenharmony_ci * Don't let the switch below mess things up since 10688c2ecf20Sopenharmony_ci * we don't have ownership to resume anything. 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_ci mutex_unlock(&shared->lock); 10718c2ecf20Sopenharmony_ci wake_up(&chip->wq); 10728c2ecf20Sopenharmony_ci return; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci mutex_unlock(&shared->lock); 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci switch(chip->oldstate) { 10788c2ecf20Sopenharmony_ci case FL_ERASING: 10798c2ecf20Sopenharmony_ci /* What if one interleaved chip has finished and the 10808c2ecf20Sopenharmony_ci other hasn't? The old code would leave the finished 10818c2ecf20Sopenharmony_ci one in READY mode. That's bad, and caused -EROFS 10828c2ecf20Sopenharmony_ci errors to be returned from do_erase_oneblock because 10838c2ecf20Sopenharmony_ci that's the only bit it checked for at the time. 10848c2ecf20Sopenharmony_ci As the state machine appears to explicitly allow 10858c2ecf20Sopenharmony_ci sending the 0x70 (Read Status) command to an erasing 10868c2ecf20Sopenharmony_ci chip and expecting it to be ignored, that's what we 10878c2ecf20Sopenharmony_ci do. */ 10888c2ecf20Sopenharmony_ci map_write(map, CMD(0xd0), chip->in_progress_block_addr); 10898c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), chip->in_progress_block_addr); 10908c2ecf20Sopenharmony_ci chip->oldstate = FL_READY; 10918c2ecf20Sopenharmony_ci chip->state = FL_ERASING; 10928c2ecf20Sopenharmony_ci break; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci case FL_XIP_WHILE_ERASING: 10958c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 10968c2ecf20Sopenharmony_ci chip->oldstate = FL_READY; 10978c2ecf20Sopenharmony_ci break; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci case FL_READY: 11008c2ecf20Sopenharmony_ci case FL_STATUS: 11018c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 11028c2ecf20Sopenharmony_ci break; 11038c2ecf20Sopenharmony_ci default: 11048c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci wake_up(&chip->wq); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_XIP 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci/* 11128c2ecf20Sopenharmony_ci * No interrupt what so ever can be serviced while the flash isn't in array 11138c2ecf20Sopenharmony_ci * mode. This is ensured by the xip_disable() and xip_enable() functions 11148c2ecf20Sopenharmony_ci * enclosing any code path where the flash is known not to be in array mode. 11158c2ecf20Sopenharmony_ci * And within a XIP disabled code path, only functions marked with __xipram 11168c2ecf20Sopenharmony_ci * may be called and nothing else (it's a good thing to inspect generated 11178c2ecf20Sopenharmony_ci * assembly to make sure inline functions were actually inlined and that gcc 11188c2ecf20Sopenharmony_ci * didn't emit calls to its own support functions). Also configuring MTD CFI 11198c2ecf20Sopenharmony_ci * support to a single buswidth and a single interleave is also recommended. 11208c2ecf20Sopenharmony_ci */ 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cistatic void xip_disable(struct map_info *map, struct flchip *chip, 11238c2ecf20Sopenharmony_ci unsigned long adr) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci /* TODO: chips with no XIP use should ignore and return */ 11268c2ecf20Sopenharmony_ci (void) map_read(map, adr); /* ensure mmu mapping is up to date */ 11278c2ecf20Sopenharmony_ci local_irq_disable(); 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic void __xipram xip_enable(struct map_info *map, struct flchip *chip, 11318c2ecf20Sopenharmony_ci unsigned long adr) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 11348c2ecf20Sopenharmony_ci if (chip->state != FL_POINT && chip->state != FL_READY) { 11358c2ecf20Sopenharmony_ci map_write(map, CMD(0xff), adr); 11368c2ecf20Sopenharmony_ci chip->state = FL_READY; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci (void) map_read(map, adr); 11398c2ecf20Sopenharmony_ci xip_iprefetch(); 11408c2ecf20Sopenharmony_ci local_irq_enable(); 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci/* 11448c2ecf20Sopenharmony_ci * When a delay is required for the flash operation to complete, the 11458c2ecf20Sopenharmony_ci * xip_wait_for_operation() function is polling for both the given timeout 11468c2ecf20Sopenharmony_ci * and pending (but still masked) hardware interrupts. Whenever there is an 11478c2ecf20Sopenharmony_ci * interrupt pending then the flash erase or write operation is suspended, 11488c2ecf20Sopenharmony_ci * array mode restored and interrupts unmasked. Task scheduling might also 11498c2ecf20Sopenharmony_ci * happen at that point. The CPU eventually returns from the interrupt or 11508c2ecf20Sopenharmony_ci * the call to schedule() and the suspended flash operation is resumed for 11518c2ecf20Sopenharmony_ci * the remaining of the delay period. 11528c2ecf20Sopenharmony_ci * 11538c2ecf20Sopenharmony_ci * Warning: this function _will_ fool interrupt latency tracing tools. 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int __xipram xip_wait_for_operation( 11578c2ecf20Sopenharmony_ci struct map_info *map, struct flchip *chip, 11588c2ecf20Sopenharmony_ci unsigned long adr, unsigned int chip_op_time_max) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 11618c2ecf20Sopenharmony_ci struct cfi_pri_intelext *cfip = cfi->cmdset_priv; 11628c2ecf20Sopenharmony_ci map_word status, OK = CMD(0x80); 11638c2ecf20Sopenharmony_ci unsigned long usec, suspended, start, done; 11648c2ecf20Sopenharmony_ci flstate_t oldstate, newstate; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci start = xip_currtime(); 11678c2ecf20Sopenharmony_ci usec = chip_op_time_max; 11688c2ecf20Sopenharmony_ci if (usec == 0) 11698c2ecf20Sopenharmony_ci usec = 500000; 11708c2ecf20Sopenharmony_ci done = 0; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci do { 11738c2ecf20Sopenharmony_ci cpu_relax(); 11748c2ecf20Sopenharmony_ci if (xip_irqpending() && cfip && 11758c2ecf20Sopenharmony_ci ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) || 11768c2ecf20Sopenharmony_ci (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) && 11778c2ecf20Sopenharmony_ci (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { 11788c2ecf20Sopenharmony_ci /* 11798c2ecf20Sopenharmony_ci * Let's suspend the erase or write operation when 11808c2ecf20Sopenharmony_ci * supported. Note that we currently don't try to 11818c2ecf20Sopenharmony_ci * suspend interleaved chips if there is already 11828c2ecf20Sopenharmony_ci * another operation suspended (imagine what happens 11838c2ecf20Sopenharmony_ci * when one chip was already done with the current 11848c2ecf20Sopenharmony_ci * operation while another chip suspended it, then 11858c2ecf20Sopenharmony_ci * we resume the whole thing at once). Yes, it 11868c2ecf20Sopenharmony_ci * can happen! 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_ci usec -= done; 11898c2ecf20Sopenharmony_ci map_write(map, CMD(0xb0), adr); 11908c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 11918c2ecf20Sopenharmony_ci suspended = xip_currtime(); 11928c2ecf20Sopenharmony_ci do { 11938c2ecf20Sopenharmony_ci if (xip_elapsed_since(suspended) > 100000) { 11948c2ecf20Sopenharmony_ci /* 11958c2ecf20Sopenharmony_ci * The chip doesn't want to suspend 11968c2ecf20Sopenharmony_ci * after waiting for 100 msecs. 11978c2ecf20Sopenharmony_ci * This is a critical error but there 11988c2ecf20Sopenharmony_ci * is not much we can do here. 11998c2ecf20Sopenharmony_ci */ 12008c2ecf20Sopenharmony_ci return -EIO; 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci status = map_read(map, adr); 12038c2ecf20Sopenharmony_ci } while (!map_word_andequal(map, status, OK, OK)); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* Suspend succeeded */ 12068c2ecf20Sopenharmony_ci oldstate = chip->state; 12078c2ecf20Sopenharmony_ci if (oldstate == FL_ERASING) { 12088c2ecf20Sopenharmony_ci if (!map_word_bitsset(map, status, CMD(0x40))) 12098c2ecf20Sopenharmony_ci break; 12108c2ecf20Sopenharmony_ci newstate = FL_XIP_WHILE_ERASING; 12118c2ecf20Sopenharmony_ci chip->erase_suspended = 1; 12128c2ecf20Sopenharmony_ci } else { 12138c2ecf20Sopenharmony_ci if (!map_word_bitsset(map, status, CMD(0x04))) 12148c2ecf20Sopenharmony_ci break; 12158c2ecf20Sopenharmony_ci newstate = FL_XIP_WHILE_WRITING; 12168c2ecf20Sopenharmony_ci chip->write_suspended = 1; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci chip->state = newstate; 12198c2ecf20Sopenharmony_ci map_write(map, CMD(0xff), adr); 12208c2ecf20Sopenharmony_ci (void) map_read(map, adr); 12218c2ecf20Sopenharmony_ci xip_iprefetch(); 12228c2ecf20Sopenharmony_ci local_irq_enable(); 12238c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12248c2ecf20Sopenharmony_ci xip_iprefetch(); 12258c2ecf20Sopenharmony_ci cond_resched(); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* 12288c2ecf20Sopenharmony_ci * We're back. However someone else might have 12298c2ecf20Sopenharmony_ci * decided to go write to the chip if we are in 12308c2ecf20Sopenharmony_ci * a suspended erase state. If so let's wait 12318c2ecf20Sopenharmony_ci * until it's done. 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 12348c2ecf20Sopenharmony_ci while (chip->state != newstate) { 12358c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 12368c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 12378c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 12388c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12398c2ecf20Sopenharmony_ci schedule(); 12408c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 12418c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci /* Disallow XIP again */ 12448c2ecf20Sopenharmony_ci local_irq_disable(); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* Resume the write or erase operation */ 12478c2ecf20Sopenharmony_ci map_write(map, CMD(0xd0), adr); 12488c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 12498c2ecf20Sopenharmony_ci chip->state = oldstate; 12508c2ecf20Sopenharmony_ci start = xip_currtime(); 12518c2ecf20Sopenharmony_ci } else if (usec >= 1000000/HZ) { 12528c2ecf20Sopenharmony_ci /* 12538c2ecf20Sopenharmony_ci * Try to save on CPU power when waiting delay 12548c2ecf20Sopenharmony_ci * is at least a system timer tick period. 12558c2ecf20Sopenharmony_ci * No need to be extremely accurate here. 12568c2ecf20Sopenharmony_ci */ 12578c2ecf20Sopenharmony_ci xip_cpu_idle(); 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci status = map_read(map, adr); 12608c2ecf20Sopenharmony_ci done = xip_elapsed_since(start); 12618c2ecf20Sopenharmony_ci } while (!map_word_andequal(map, status, OK, OK) 12628c2ecf20Sopenharmony_ci && done < usec); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci return (done >= usec) ? -ETIME : 0; 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci/* 12688c2ecf20Sopenharmony_ci * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while 12698c2ecf20Sopenharmony_ci * the flash is actively programming or erasing since we have to poll for 12708c2ecf20Sopenharmony_ci * the operation to complete anyway. We can't do that in a generic way with 12718c2ecf20Sopenharmony_ci * a XIP setup so do it before the actual flash operation in this case 12728c2ecf20Sopenharmony_ci * and stub it out from INVAL_CACHE_AND_WAIT. 12738c2ecf20Sopenharmony_ci */ 12748c2ecf20Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(map, from, size) \ 12758c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, from, size) 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, usec, usec_max) \ 12788c2ecf20Sopenharmony_ci xip_wait_for_operation(map, chip, cmd_adr, usec_max) 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci#else 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci#define xip_disable(map, chip, adr) 12838c2ecf20Sopenharmony_ci#define xip_enable(map, chip, adr) 12848c2ecf20Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(x...) 12858c2ecf20Sopenharmony_ci#define INVAL_CACHE_AND_WAIT inval_cache_and_wait_for_operation 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic int inval_cache_and_wait_for_operation( 12888c2ecf20Sopenharmony_ci struct map_info *map, struct flchip *chip, 12898c2ecf20Sopenharmony_ci unsigned long cmd_adr, unsigned long inval_adr, int inval_len, 12908c2ecf20Sopenharmony_ci unsigned int chip_op_time, unsigned int chip_op_time_max) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 12938c2ecf20Sopenharmony_ci map_word status, status_OK = CMD(0x80); 12948c2ecf20Sopenharmony_ci int chip_state = chip->state; 12958c2ecf20Sopenharmony_ci unsigned int timeo, sleep_time, reset_timeo; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12988c2ecf20Sopenharmony_ci if (inval_len) 12998c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); 13008c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci timeo = chip_op_time_max; 13038c2ecf20Sopenharmony_ci if (!timeo) 13048c2ecf20Sopenharmony_ci timeo = 500000; 13058c2ecf20Sopenharmony_ci reset_timeo = timeo; 13068c2ecf20Sopenharmony_ci sleep_time = chip_op_time / 2; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci for (;;) { 13098c2ecf20Sopenharmony_ci if (chip->state != chip_state) { 13108c2ecf20Sopenharmony_ci /* Someone's suspended the operation: sleep */ 13118c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 13128c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 13138c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 13148c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13158c2ecf20Sopenharmony_ci schedule(); 13168c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 13178c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13188c2ecf20Sopenharmony_ci continue; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci status = map_read(map, cmd_adr); 13228c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 13238c2ecf20Sopenharmony_ci break; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (chip->erase_suspended && chip_state == FL_ERASING) { 13268c2ecf20Sopenharmony_ci /* Erase suspend occurred while sleep: reset timeout */ 13278c2ecf20Sopenharmony_ci timeo = reset_timeo; 13288c2ecf20Sopenharmony_ci chip->erase_suspended = 0; 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci if (chip->write_suspended && chip_state == FL_WRITING) { 13318c2ecf20Sopenharmony_ci /* Write suspend occurred while sleep: reset timeout */ 13328c2ecf20Sopenharmony_ci timeo = reset_timeo; 13338c2ecf20Sopenharmony_ci chip->write_suspended = 0; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci if (!timeo) { 13368c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 13378c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 13388c2ecf20Sopenharmony_ci return -ETIME; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* OK Still waiting. Drop the lock, wait a while and retry. */ 13428c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13438c2ecf20Sopenharmony_ci if (sleep_time >= 1000000/HZ) { 13448c2ecf20Sopenharmony_ci /* 13458c2ecf20Sopenharmony_ci * Half of the normal delay still remaining 13468c2ecf20Sopenharmony_ci * can be performed with a sleeping delay instead 13478c2ecf20Sopenharmony_ci * of busy waiting. 13488c2ecf20Sopenharmony_ci */ 13498c2ecf20Sopenharmony_ci msleep(sleep_time/1000); 13508c2ecf20Sopenharmony_ci timeo -= sleep_time; 13518c2ecf20Sopenharmony_ci sleep_time = 1000000/HZ; 13528c2ecf20Sopenharmony_ci } else { 13538c2ecf20Sopenharmony_ci udelay(1); 13548c2ecf20Sopenharmony_ci cond_resched(); 13558c2ecf20Sopenharmony_ci timeo--; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* Done and happy. */ 13618c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 13628c2ecf20Sopenharmony_ci return 0; 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci#endif 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci#define WAIT_TIMEOUT(map, chip, adr, udelay, udelay_max) \ 13688c2ecf20Sopenharmony_ci INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay, udelay_max); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_cistatic int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci unsigned long cmd_addr; 13748c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 13758c2ecf20Sopenharmony_ci int ret; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci adr += chip->start; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci /* Ensure cmd read/writes are aligned. */ 13808c2ecf20Sopenharmony_ci cmd_addr = adr & ~(map_bankwidth(map)-1); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci ret = get_chip(map, chip, cmd_addr, FL_POINT); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (!ret) { 13878c2ecf20Sopenharmony_ci if (chip->state != FL_POINT && chip->state != FL_READY) 13888c2ecf20Sopenharmony_ci map_write(map, CMD(0xff), cmd_addr); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci chip->state = FL_POINT; 13918c2ecf20Sopenharmony_ci chip->ref_point_counter++; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci return ret; 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len, 13998c2ecf20Sopenharmony_ci size_t *retlen, void **virt, resource_size_t *phys) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 14028c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 14038c2ecf20Sopenharmony_ci unsigned long ofs, last_end = 0; 14048c2ecf20Sopenharmony_ci int chipnum; 14058c2ecf20Sopenharmony_ci int ret; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci if (!map->virt) 14088c2ecf20Sopenharmony_ci return -EINVAL; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* Now lock the chip(s) to POINT state */ 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci /* ofs: offset within the first chip that the first read should start */ 14138c2ecf20Sopenharmony_ci chipnum = (from >> cfi->chipshift); 14148c2ecf20Sopenharmony_ci ofs = from - (chipnum << cfi->chipshift); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci *virt = map->virt + cfi->chips[chipnum].start + ofs; 14178c2ecf20Sopenharmony_ci if (phys) 14188c2ecf20Sopenharmony_ci *phys = map->phys + cfi->chips[chipnum].start + ofs; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci while (len) { 14218c2ecf20Sopenharmony_ci unsigned long thislen; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* We cannot point across chips that are virtually disjoint */ 14278c2ecf20Sopenharmony_ci if (!last_end) 14288c2ecf20Sopenharmony_ci last_end = cfi->chips[chipnum].start; 14298c2ecf20Sopenharmony_ci else if (cfi->chips[chipnum].start != last_end) 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if ((len + ofs -1) >> cfi->chipshift) 14338c2ecf20Sopenharmony_ci thislen = (1<<cfi->chipshift) - ofs; 14348c2ecf20Sopenharmony_ci else 14358c2ecf20Sopenharmony_ci thislen = len; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci ret = do_point_onechip(map, &cfi->chips[chipnum], ofs, thislen); 14388c2ecf20Sopenharmony_ci if (ret) 14398c2ecf20Sopenharmony_ci break; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci *retlen += thislen; 14428c2ecf20Sopenharmony_ci len -= thislen; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci ofs = 0; 14458c2ecf20Sopenharmony_ci last_end += 1 << cfi->chipshift; 14468c2ecf20Sopenharmony_ci chipnum++; 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci return 0; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 14548c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 14558c2ecf20Sopenharmony_ci unsigned long ofs; 14568c2ecf20Sopenharmony_ci int chipnum, err = 0; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* Now unlock the chip(s) POINT state */ 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* ofs: offset within the first chip that the first read should start */ 14618c2ecf20Sopenharmony_ci chipnum = (from >> cfi->chipshift); 14628c2ecf20Sopenharmony_ci ofs = from - (chipnum << cfi->chipshift); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci while (len && !err) { 14658c2ecf20Sopenharmony_ci unsigned long thislen; 14668c2ecf20Sopenharmony_ci struct flchip *chip; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci chip = &cfi->chips[chipnum]; 14698c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 14708c2ecf20Sopenharmony_ci break; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci if ((len + ofs -1) >> cfi->chipshift) 14738c2ecf20Sopenharmony_ci thislen = (1<<cfi->chipshift) - ofs; 14748c2ecf20Sopenharmony_ci else 14758c2ecf20Sopenharmony_ci thislen = len; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 14788c2ecf20Sopenharmony_ci if (chip->state == FL_POINT) { 14798c2ecf20Sopenharmony_ci chip->ref_point_counter--; 14808c2ecf20Sopenharmony_ci if(chip->ref_point_counter == 0) 14818c2ecf20Sopenharmony_ci chip->state = FL_READY; 14828c2ecf20Sopenharmony_ci } else { 14838c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Error: unpoint called on non pointed region\n", map->name); 14848c2ecf20Sopenharmony_ci err = -EINVAL; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci put_chip(map, chip, chip->start); 14888c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci len -= thislen; 14918c2ecf20Sopenharmony_ci ofs = 0; 14928c2ecf20Sopenharmony_ci chipnum++; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return err; 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci unsigned long cmd_addr; 15018c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 15028c2ecf20Sopenharmony_ci int ret; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci adr += chip->start; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci /* Ensure cmd read/writes are aligned. */ 15078c2ecf20Sopenharmony_ci cmd_addr = adr & ~(map_bankwidth(map)-1); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 15108c2ecf20Sopenharmony_ci ret = get_chip(map, chip, cmd_addr, FL_READY); 15118c2ecf20Sopenharmony_ci if (ret) { 15128c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 15138c2ecf20Sopenharmony_ci return ret; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (chip->state != FL_POINT && chip->state != FL_READY) { 15178c2ecf20Sopenharmony_ci map_write(map, CMD(0xff), cmd_addr); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci chip->state = FL_READY; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci map_copy_from(map, buf, adr, len); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci put_chip(map, chip, cmd_addr); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 15278c2ecf20Sopenharmony_ci return 0; 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_cistatic int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 15338c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 15348c2ecf20Sopenharmony_ci unsigned long ofs; 15358c2ecf20Sopenharmony_ci int chipnum; 15368c2ecf20Sopenharmony_ci int ret = 0; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* ofs: offset within the first chip that the first read should start */ 15398c2ecf20Sopenharmony_ci chipnum = (from >> cfi->chipshift); 15408c2ecf20Sopenharmony_ci ofs = from - (chipnum << cfi->chipshift); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci while (len) { 15438c2ecf20Sopenharmony_ci unsigned long thislen; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 15468c2ecf20Sopenharmony_ci break; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if ((len + ofs -1) >> cfi->chipshift) 15498c2ecf20Sopenharmony_ci thislen = (1<<cfi->chipshift) - ofs; 15508c2ecf20Sopenharmony_ci else 15518c2ecf20Sopenharmony_ci thislen = len; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); 15548c2ecf20Sopenharmony_ci if (ret) 15558c2ecf20Sopenharmony_ci break; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci *retlen += thislen; 15588c2ecf20Sopenharmony_ci len -= thislen; 15598c2ecf20Sopenharmony_ci buf += thislen; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci ofs = 0; 15628c2ecf20Sopenharmony_ci chipnum++; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci return ret; 15658c2ecf20Sopenharmony_ci} 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, 15688c2ecf20Sopenharmony_ci unsigned long adr, map_word datum, int mode) 15698c2ecf20Sopenharmony_ci{ 15708c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 15718c2ecf20Sopenharmony_ci map_word status, write_cmd; 15728c2ecf20Sopenharmony_ci int ret; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci adr += chip->start; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci switch (mode) { 15778c2ecf20Sopenharmony_ci case FL_WRITING: 15788c2ecf20Sopenharmony_ci write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0x40) : CMD(0x41); 15798c2ecf20Sopenharmony_ci break; 15808c2ecf20Sopenharmony_ci case FL_OTP_WRITE: 15818c2ecf20Sopenharmony_ci write_cmd = CMD(0xc0); 15828c2ecf20Sopenharmony_ci break; 15838c2ecf20Sopenharmony_ci default: 15848c2ecf20Sopenharmony_ci return -EINVAL; 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 15888c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, mode); 15898c2ecf20Sopenharmony_ci if (ret) { 15908c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 15918c2ecf20Sopenharmony_ci return ret; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); 15958c2ecf20Sopenharmony_ci ENABLE_VPP(map); 15968c2ecf20Sopenharmony_ci xip_disable(map, chip, adr); 15978c2ecf20Sopenharmony_ci map_write(map, write_cmd, adr); 15988c2ecf20Sopenharmony_ci map_write(map, datum, adr); 15998c2ecf20Sopenharmony_ci chip->state = mode; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci ret = INVAL_CACHE_AND_WAIT(map, chip, adr, 16028c2ecf20Sopenharmony_ci adr, map_bankwidth(map), 16038c2ecf20Sopenharmony_ci chip->word_write_time, 16048c2ecf20Sopenharmony_ci chip->word_write_time_max); 16058c2ecf20Sopenharmony_ci if (ret) { 16068c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 16078c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: word write error (status timeout)\n", map->name); 16088c2ecf20Sopenharmony_ci goto out; 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci /* check for errors */ 16128c2ecf20Sopenharmony_ci status = map_read(map, adr); 16138c2ecf20Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x1a))) { 16148c2ecf20Sopenharmony_ci unsigned long chipstatus = MERGESTATUS(status); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci /* reset status */ 16178c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), adr); 16188c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 16198c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci if (chipstatus & 0x02) { 16228c2ecf20Sopenharmony_ci ret = -EROFS; 16238c2ecf20Sopenharmony_ci } else if (chipstatus & 0x08) { 16248c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: word write error (bad VPP)\n", map->name); 16258c2ecf20Sopenharmony_ci ret = -EIO; 16268c2ecf20Sopenharmony_ci } else { 16278c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: word write error (status 0x%lx)\n", map->name, chipstatus); 16288c2ecf20Sopenharmony_ci ret = -EINVAL; 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci goto out; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 16358c2ecf20Sopenharmony_ci out: DISABLE_VPP(map); 16368c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 16378c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 16388c2ecf20Sopenharmony_ci return ret; 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cistatic int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 16458c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 16468c2ecf20Sopenharmony_ci int ret; 16478c2ecf20Sopenharmony_ci int chipnum; 16488c2ecf20Sopenharmony_ci unsigned long ofs; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci chipnum = to >> cfi->chipshift; 16518c2ecf20Sopenharmony_ci ofs = to - (chipnum << cfi->chipshift); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci /* If it's not bus-aligned, do the first byte write */ 16548c2ecf20Sopenharmony_ci if (ofs & (map_bankwidth(map)-1)) { 16558c2ecf20Sopenharmony_ci unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); 16568c2ecf20Sopenharmony_ci int gap = ofs - bus_ofs; 16578c2ecf20Sopenharmony_ci int n; 16588c2ecf20Sopenharmony_ci map_word datum; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci n = min_t(int, len, map_bankwidth(map)-gap); 16618c2ecf20Sopenharmony_ci datum = map_word_ff(map); 16628c2ecf20Sopenharmony_ci datum = map_word_load_partial(map, datum, buf, gap, n); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci ret = do_write_oneword(map, &cfi->chips[chipnum], 16658c2ecf20Sopenharmony_ci bus_ofs, datum, FL_WRITING); 16668c2ecf20Sopenharmony_ci if (ret) 16678c2ecf20Sopenharmony_ci return ret; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci len -= n; 16708c2ecf20Sopenharmony_ci ofs += n; 16718c2ecf20Sopenharmony_ci buf += n; 16728c2ecf20Sopenharmony_ci (*retlen) += n; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 16758c2ecf20Sopenharmony_ci chipnum ++; 16768c2ecf20Sopenharmony_ci ofs = 0; 16778c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 16788c2ecf20Sopenharmony_ci return 0; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci while(len >= map_bankwidth(map)) { 16838c2ecf20Sopenharmony_ci map_word datum = map_word_load(map, buf); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci ret = do_write_oneword(map, &cfi->chips[chipnum], 16868c2ecf20Sopenharmony_ci ofs, datum, FL_WRITING); 16878c2ecf20Sopenharmony_ci if (ret) 16888c2ecf20Sopenharmony_ci return ret; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci ofs += map_bankwidth(map); 16918c2ecf20Sopenharmony_ci buf += map_bankwidth(map); 16928c2ecf20Sopenharmony_ci (*retlen) += map_bankwidth(map); 16938c2ecf20Sopenharmony_ci len -= map_bankwidth(map); 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 16968c2ecf20Sopenharmony_ci chipnum ++; 16978c2ecf20Sopenharmony_ci ofs = 0; 16988c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 16998c2ecf20Sopenharmony_ci return 0; 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci if (len & (map_bankwidth(map)-1)) { 17048c2ecf20Sopenharmony_ci map_word datum; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci datum = map_word_ff(map); 17078c2ecf20Sopenharmony_ci datum = map_word_load_partial(map, datum, buf, 0, len); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci ret = do_write_oneword(map, &cfi->chips[chipnum], 17108c2ecf20Sopenharmony_ci ofs, datum, FL_WRITING); 17118c2ecf20Sopenharmony_ci if (ret) 17128c2ecf20Sopenharmony_ci return ret; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci (*retlen) += len; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci return 0; 17188c2ecf20Sopenharmony_ci} 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, 17228c2ecf20Sopenharmony_ci unsigned long adr, const struct kvec **pvec, 17238c2ecf20Sopenharmony_ci unsigned long *pvec_seek, int len) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 17268c2ecf20Sopenharmony_ci map_word status, write_cmd, datum; 17278c2ecf20Sopenharmony_ci unsigned long cmd_adr; 17288c2ecf20Sopenharmony_ci int ret, wbufsize, word_gap, words; 17298c2ecf20Sopenharmony_ci const struct kvec *vec; 17308c2ecf20Sopenharmony_ci unsigned long vec_seek; 17318c2ecf20Sopenharmony_ci unsigned long initial_adr; 17328c2ecf20Sopenharmony_ci int initial_len = len; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 17358c2ecf20Sopenharmony_ci adr += chip->start; 17368c2ecf20Sopenharmony_ci initial_adr = adr; 17378c2ecf20Sopenharmony_ci cmd_adr = adr & ~(wbufsize-1); 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci /* Sharp LH28F640BF chips need the first address for the 17408c2ecf20Sopenharmony_ci * Page Buffer Program command. See Table 5 of 17418c2ecf20Sopenharmony_ci * LH28F320BF, LH28F640BF, LH28F128BF Series (Appendix FUM00701) */ 17428c2ecf20Sopenharmony_ci if (is_LH28F640BF(cfi)) 17438c2ecf20Sopenharmony_ci cmd_adr = adr; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci /* Let's determine this according to the interleave only once */ 17468c2ecf20Sopenharmony_ci write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 17498c2ecf20Sopenharmony_ci ret = get_chip(map, chip, cmd_adr, FL_WRITING); 17508c2ecf20Sopenharmony_ci if (ret) { 17518c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 17528c2ecf20Sopenharmony_ci return ret; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci XIP_INVAL_CACHED_RANGE(map, initial_adr, initial_len); 17568c2ecf20Sopenharmony_ci ENABLE_VPP(map); 17578c2ecf20Sopenharmony_ci xip_disable(map, chip, cmd_adr); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set 17608c2ecf20Sopenharmony_ci [...], the device will not accept any more Write to Buffer commands". 17618c2ecf20Sopenharmony_ci So we must check here and reset those bits if they're set. Otherwise 17628c2ecf20Sopenharmony_ci we're just pissing in the wind */ 17638c2ecf20Sopenharmony_ci if (chip->state != FL_STATUS) { 17648c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 17658c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci status = map_read(map, cmd_adr); 17688c2ecf20Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x30))) { 17698c2ecf20Sopenharmony_ci xip_enable(map, chip, cmd_adr); 17708c2ecf20Sopenharmony_ci printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); 17718c2ecf20Sopenharmony_ci xip_disable(map, chip, cmd_adr); 17728c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), cmd_adr); 17738c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci chip->state = FL_WRITING_TO_BUFFER; 17778c2ecf20Sopenharmony_ci map_write(map, write_cmd, cmd_adr); 17788c2ecf20Sopenharmony_ci ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0, 0); 17798c2ecf20Sopenharmony_ci if (ret) { 17808c2ecf20Sopenharmony_ci /* Argh. Not ready for write to buffer */ 17818c2ecf20Sopenharmony_ci map_word Xstatus = map_read(map, cmd_adr); 17828c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 17838c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 17848c2ecf20Sopenharmony_ci status = map_read(map, cmd_adr); 17858c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), cmd_adr); 17868c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 17878c2ecf20Sopenharmony_ci xip_enable(map, chip, cmd_adr); 17888c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n", 17898c2ecf20Sopenharmony_ci map->name, Xstatus.x[0], status.x[0]); 17908c2ecf20Sopenharmony_ci goto out; 17918c2ecf20Sopenharmony_ci } 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* Figure out the number of words to write */ 17948c2ecf20Sopenharmony_ci word_gap = (-adr & (map_bankwidth(map)-1)); 17958c2ecf20Sopenharmony_ci words = DIV_ROUND_UP(len - word_gap, map_bankwidth(map)); 17968c2ecf20Sopenharmony_ci if (!word_gap) { 17978c2ecf20Sopenharmony_ci words--; 17988c2ecf20Sopenharmony_ci } else { 17998c2ecf20Sopenharmony_ci word_gap = map_bankwidth(map) - word_gap; 18008c2ecf20Sopenharmony_ci adr -= word_gap; 18018c2ecf20Sopenharmony_ci datum = map_word_ff(map); 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci /* Write length of data to come */ 18058c2ecf20Sopenharmony_ci map_write(map, CMD(words), cmd_adr ); 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci /* Write data */ 18088c2ecf20Sopenharmony_ci vec = *pvec; 18098c2ecf20Sopenharmony_ci vec_seek = *pvec_seek; 18108c2ecf20Sopenharmony_ci do { 18118c2ecf20Sopenharmony_ci int n = map_bankwidth(map) - word_gap; 18128c2ecf20Sopenharmony_ci if (n > vec->iov_len - vec_seek) 18138c2ecf20Sopenharmony_ci n = vec->iov_len - vec_seek; 18148c2ecf20Sopenharmony_ci if (n > len) 18158c2ecf20Sopenharmony_ci n = len; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci if (!word_gap && len < map_bankwidth(map)) 18188c2ecf20Sopenharmony_ci datum = map_word_ff(map); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci datum = map_word_load_partial(map, datum, 18218c2ecf20Sopenharmony_ci vec->iov_base + vec_seek, 18228c2ecf20Sopenharmony_ci word_gap, n); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci len -= n; 18258c2ecf20Sopenharmony_ci word_gap += n; 18268c2ecf20Sopenharmony_ci if (!len || word_gap == map_bankwidth(map)) { 18278c2ecf20Sopenharmony_ci map_write(map, datum, adr); 18288c2ecf20Sopenharmony_ci adr += map_bankwidth(map); 18298c2ecf20Sopenharmony_ci word_gap = 0; 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci vec_seek += n; 18338c2ecf20Sopenharmony_ci if (vec_seek == vec->iov_len) { 18348c2ecf20Sopenharmony_ci vec++; 18358c2ecf20Sopenharmony_ci vec_seek = 0; 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci } while (len); 18388c2ecf20Sopenharmony_ci *pvec = vec; 18398c2ecf20Sopenharmony_ci *pvec_seek = vec_seek; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci /* GO GO GO */ 18428c2ecf20Sopenharmony_ci map_write(map, CMD(0xd0), cmd_adr); 18438c2ecf20Sopenharmony_ci chip->state = FL_WRITING; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, 18468c2ecf20Sopenharmony_ci initial_adr, initial_len, 18478c2ecf20Sopenharmony_ci chip->buffer_write_time, 18488c2ecf20Sopenharmony_ci chip->buffer_write_time_max); 18498c2ecf20Sopenharmony_ci if (ret) { 18508c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 18518c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 18528c2ecf20Sopenharmony_ci xip_enable(map, chip, cmd_adr); 18538c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name); 18548c2ecf20Sopenharmony_ci goto out; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci /* check for errors */ 18588c2ecf20Sopenharmony_ci status = map_read(map, cmd_adr); 18598c2ecf20Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x1a))) { 18608c2ecf20Sopenharmony_ci unsigned long chipstatus = MERGESTATUS(status); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci /* reset status */ 18638c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), cmd_adr); 18648c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 18658c2ecf20Sopenharmony_ci xip_enable(map, chip, cmd_adr); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci if (chipstatus & 0x02) { 18688c2ecf20Sopenharmony_ci ret = -EROFS; 18698c2ecf20Sopenharmony_ci } else if (chipstatus & 0x08) { 18708c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: buffer write error (bad VPP)\n", map->name); 18718c2ecf20Sopenharmony_ci ret = -EIO; 18728c2ecf20Sopenharmony_ci } else { 18738c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: buffer write error (status 0x%lx)\n", map->name, chipstatus); 18748c2ecf20Sopenharmony_ci ret = -EINVAL; 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci goto out; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci xip_enable(map, chip, cmd_adr); 18818c2ecf20Sopenharmony_ci out: DISABLE_VPP(map); 18828c2ecf20Sopenharmony_ci put_chip(map, chip, cmd_adr); 18838c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 18848c2ecf20Sopenharmony_ci return ret; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs, 18888c2ecf20Sopenharmony_ci unsigned long count, loff_t to, size_t *retlen) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 18918c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 18928c2ecf20Sopenharmony_ci int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 18938c2ecf20Sopenharmony_ci int ret; 18948c2ecf20Sopenharmony_ci int chipnum; 18958c2ecf20Sopenharmony_ci unsigned long ofs, vec_seek, i; 18968c2ecf20Sopenharmony_ci size_t len = 0; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 18998c2ecf20Sopenharmony_ci len += vecs[i].iov_len; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci if (!len) 19028c2ecf20Sopenharmony_ci return 0; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci chipnum = to >> cfi->chipshift; 19058c2ecf20Sopenharmony_ci ofs = to - (chipnum << cfi->chipshift); 19068c2ecf20Sopenharmony_ci vec_seek = 0; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci do { 19098c2ecf20Sopenharmony_ci /* We must not cross write block boundaries */ 19108c2ecf20Sopenharmony_ci int size = wbufsize - (ofs & (wbufsize-1)); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci if (size > len) 19138c2ecf20Sopenharmony_ci size = len; 19148c2ecf20Sopenharmony_ci ret = do_write_buffer(map, &cfi->chips[chipnum], 19158c2ecf20Sopenharmony_ci ofs, &vecs, &vec_seek, size); 19168c2ecf20Sopenharmony_ci if (ret) 19178c2ecf20Sopenharmony_ci return ret; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci ofs += size; 19208c2ecf20Sopenharmony_ci (*retlen) += size; 19218c2ecf20Sopenharmony_ci len -= size; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 19248c2ecf20Sopenharmony_ci chipnum ++; 19258c2ecf20Sopenharmony_ci ofs = 0; 19268c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 19278c2ecf20Sopenharmony_ci return 0; 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci /* Be nice and reschedule with the chip in a usable state for other 19318c2ecf20Sopenharmony_ci processes. */ 19328c2ecf20Sopenharmony_ci cond_resched(); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci } while (len); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci return 0; 19378c2ecf20Sopenharmony_ci} 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_cistatic int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, 19408c2ecf20Sopenharmony_ci size_t len, size_t *retlen, const u_char *buf) 19418c2ecf20Sopenharmony_ci{ 19428c2ecf20Sopenharmony_ci struct kvec vec; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci vec.iov_base = (void *) buf; 19458c2ecf20Sopenharmony_ci vec.iov_len = len; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci return cfi_intelext_writev(mtd, &vec, 1, to, retlen); 19488c2ecf20Sopenharmony_ci} 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_cistatic int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, 19518c2ecf20Sopenharmony_ci unsigned long adr, int len, void *thunk) 19528c2ecf20Sopenharmony_ci{ 19538c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 19548c2ecf20Sopenharmony_ci map_word status; 19558c2ecf20Sopenharmony_ci int retries = 3; 19568c2ecf20Sopenharmony_ci int ret; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci adr += chip->start; 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci retry: 19618c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 19628c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, FL_ERASING); 19638c2ecf20Sopenharmony_ci if (ret) { 19648c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 19658c2ecf20Sopenharmony_ci return ret; 19668c2ecf20Sopenharmony_ci } 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci XIP_INVAL_CACHED_RANGE(map, adr, len); 19698c2ecf20Sopenharmony_ci ENABLE_VPP(map); 19708c2ecf20Sopenharmony_ci xip_disable(map, chip, adr); 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci /* Clear the status register first */ 19738c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), adr); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* Now erase */ 19768c2ecf20Sopenharmony_ci map_write(map, CMD(0x20), adr); 19778c2ecf20Sopenharmony_ci map_write(map, CMD(0xD0), adr); 19788c2ecf20Sopenharmony_ci chip->state = FL_ERASING; 19798c2ecf20Sopenharmony_ci chip->erase_suspended = 0; 19808c2ecf20Sopenharmony_ci chip->in_progress_block_addr = adr; 19818c2ecf20Sopenharmony_ci chip->in_progress_block_mask = ~(len - 1); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci ret = INVAL_CACHE_AND_WAIT(map, chip, adr, 19848c2ecf20Sopenharmony_ci adr, len, 19858c2ecf20Sopenharmony_ci chip->erase_time, 19868c2ecf20Sopenharmony_ci chip->erase_time_max); 19878c2ecf20Sopenharmony_ci if (ret) { 19888c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 19898c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 19908c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 19918c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name); 19928c2ecf20Sopenharmony_ci goto out; 19938c2ecf20Sopenharmony_ci } 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci /* We've broken this before. It doesn't hurt to be safe */ 19968c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 19978c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 19988c2ecf20Sopenharmony_ci status = map_read(map, adr); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci /* check for errors */ 20018c2ecf20Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x3a))) { 20028c2ecf20Sopenharmony_ci unsigned long chipstatus = MERGESTATUS(status); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci /* Reset the error bits */ 20058c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), adr); 20068c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 20078c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci if ((chipstatus & 0x30) == 0x30) { 20108c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: block erase error: (bad command sequence, status 0x%lx)\n", map->name, chipstatus); 20118c2ecf20Sopenharmony_ci ret = -EINVAL; 20128c2ecf20Sopenharmony_ci } else if (chipstatus & 0x02) { 20138c2ecf20Sopenharmony_ci /* Protection bit set */ 20148c2ecf20Sopenharmony_ci ret = -EROFS; 20158c2ecf20Sopenharmony_ci } else if (chipstatus & 0x8) { 20168c2ecf20Sopenharmony_ci /* Voltage */ 20178c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: block erase error: (bad VPP)\n", map->name); 20188c2ecf20Sopenharmony_ci ret = -EIO; 20198c2ecf20Sopenharmony_ci } else if (chipstatus & 0x20 && retries--) { 20208c2ecf20Sopenharmony_ci printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus); 20218c2ecf20Sopenharmony_ci DISABLE_VPP(map); 20228c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 20238c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 20248c2ecf20Sopenharmony_ci goto retry; 20258c2ecf20Sopenharmony_ci } else { 20268c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: block erase failed at 0x%08lx (status 0x%lx)\n", map->name, adr, chipstatus); 20278c2ecf20Sopenharmony_ci ret = -EIO; 20288c2ecf20Sopenharmony_ci } 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci goto out; 20318c2ecf20Sopenharmony_ci } 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 20348c2ecf20Sopenharmony_ci out: DISABLE_VPP(map); 20358c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 20368c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 20378c2ecf20Sopenharmony_ci return ret; 20388c2ecf20Sopenharmony_ci} 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_cistatic int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) 20418c2ecf20Sopenharmony_ci{ 20428c2ecf20Sopenharmony_ci return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr, 20438c2ecf20Sopenharmony_ci instr->len, NULL); 20448c2ecf20Sopenharmony_ci} 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_cistatic void cfi_intelext_sync (struct mtd_info *mtd) 20478c2ecf20Sopenharmony_ci{ 20488c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 20498c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 20508c2ecf20Sopenharmony_ci int i; 20518c2ecf20Sopenharmony_ci struct flchip *chip; 20528c2ecf20Sopenharmony_ci int ret = 0; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 20558c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 20588c2ecf20Sopenharmony_ci ret = get_chip(map, chip, chip->start, FL_SYNCING); 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (!ret) { 20618c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 20628c2ecf20Sopenharmony_ci chip->state = FL_SYNCING; 20638c2ecf20Sopenharmony_ci /* No need to wake_up() on this state change - 20648c2ecf20Sopenharmony_ci * as the whole point is that nobody can do anything 20658c2ecf20Sopenharmony_ci * with the chip now anyway. 20668c2ecf20Sopenharmony_ci */ 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci /* Unlock the chips again */ 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci for (i--; i >=0; i--) { 20748c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci if (chip->state == FL_SYNCING) { 20798c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 20808c2ecf20Sopenharmony_ci chip->oldstate = FL_READY; 20818c2ecf20Sopenharmony_ci wake_up(&chip->wq); 20828c2ecf20Sopenharmony_ci } 20838c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 20848c2ecf20Sopenharmony_ci } 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_cistatic int __xipram do_getlockstatus_oneblock(struct map_info *map, 20888c2ecf20Sopenharmony_ci struct flchip *chip, 20898c2ecf20Sopenharmony_ci unsigned long adr, 20908c2ecf20Sopenharmony_ci int len, void *thunk) 20918c2ecf20Sopenharmony_ci{ 20928c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 20938c2ecf20Sopenharmony_ci int status, ofs_factor = cfi->interleave * cfi->device_type; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci adr += chip->start; 20968c2ecf20Sopenharmony_ci xip_disable(map, chip, adr+(2*ofs_factor)); 20978c2ecf20Sopenharmony_ci map_write(map, CMD(0x90), adr+(2*ofs_factor)); 20988c2ecf20Sopenharmony_ci chip->state = FL_JEDEC_QUERY; 20998c2ecf20Sopenharmony_ci status = cfi_read_query(map, adr+(2*ofs_factor)); 21008c2ecf20Sopenharmony_ci xip_enable(map, chip, 0); 21018c2ecf20Sopenharmony_ci return status; 21028c2ecf20Sopenharmony_ci} 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 21058c2ecf20Sopenharmony_cistatic int __xipram do_printlockstatus_oneblock(struct map_info *map, 21068c2ecf20Sopenharmony_ci struct flchip *chip, 21078c2ecf20Sopenharmony_ci unsigned long adr, 21088c2ecf20Sopenharmony_ci int len, void *thunk) 21098c2ecf20Sopenharmony_ci{ 21108c2ecf20Sopenharmony_ci printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", 21118c2ecf20Sopenharmony_ci adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk)); 21128c2ecf20Sopenharmony_ci return 0; 21138c2ecf20Sopenharmony_ci} 21148c2ecf20Sopenharmony_ci#endif 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) 21178c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_cistatic int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip, 21208c2ecf20Sopenharmony_ci unsigned long adr, int len, void *thunk) 21218c2ecf20Sopenharmony_ci{ 21228c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 21238c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 21248c2ecf20Sopenharmony_ci int mdelay; 21258c2ecf20Sopenharmony_ci int ret; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci adr += chip->start; 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 21308c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, FL_LOCKING); 21318c2ecf20Sopenharmony_ci if (ret) { 21328c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 21338c2ecf20Sopenharmony_ci return ret; 21348c2ecf20Sopenharmony_ci } 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci ENABLE_VPP(map); 21378c2ecf20Sopenharmony_ci xip_disable(map, chip, adr); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci map_write(map, CMD(0x60), adr); 21408c2ecf20Sopenharmony_ci if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { 21418c2ecf20Sopenharmony_ci map_write(map, CMD(0x01), adr); 21428c2ecf20Sopenharmony_ci chip->state = FL_LOCKING; 21438c2ecf20Sopenharmony_ci } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { 21448c2ecf20Sopenharmony_ci map_write(map, CMD(0xD0), adr); 21458c2ecf20Sopenharmony_ci chip->state = FL_UNLOCKING; 21468c2ecf20Sopenharmony_ci } else 21478c2ecf20Sopenharmony_ci BUG(); 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci /* 21508c2ecf20Sopenharmony_ci * If Instant Individual Block Locking supported then no need 21518c2ecf20Sopenharmony_ci * to delay. 21528c2ecf20Sopenharmony_ci */ 21538c2ecf20Sopenharmony_ci /* 21548c2ecf20Sopenharmony_ci * Unlocking may take up to 1.4 seconds on some Intel flashes. So 21558c2ecf20Sopenharmony_ci * lets use a max of 1.5 seconds (1500ms) as timeout. 21568c2ecf20Sopenharmony_ci * 21578c2ecf20Sopenharmony_ci * See "Clear Block Lock-Bits Time" on page 40 in 21588c2ecf20Sopenharmony_ci * "3 Volt Intel StrataFlash Memory" 28F128J3,28F640J3,28F320J3 manual 21598c2ecf20Sopenharmony_ci * from February 2003 21608c2ecf20Sopenharmony_ci */ 21618c2ecf20Sopenharmony_ci mdelay = (!extp || !(extp->FeatureSupport & (1 << 5))) ? 1500 : 0; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci ret = WAIT_TIMEOUT(map, chip, adr, mdelay, mdelay * 1000); 21648c2ecf20Sopenharmony_ci if (ret) { 21658c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 21668c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 21678c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 21688c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: block unlock error: (status timeout)\n", map->name); 21698c2ecf20Sopenharmony_ci goto out; 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci xip_enable(map, chip, adr); 21738c2ecf20Sopenharmony_ci out: DISABLE_VPP(map); 21748c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 21758c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 21768c2ecf20Sopenharmony_ci return ret; 21778c2ecf20Sopenharmony_ci} 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_cistatic int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 21808c2ecf20Sopenharmony_ci{ 21818c2ecf20Sopenharmony_ci int ret; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 21848c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", 21858c2ecf20Sopenharmony_ci __func__, ofs, len); 21868c2ecf20Sopenharmony_ci cfi_varsize_frob(mtd, do_printlockstatus_oneblock, 21878c2ecf20Sopenharmony_ci ofs, len, NULL); 21888c2ecf20Sopenharmony_ci#endif 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, 21918c2ecf20Sopenharmony_ci ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 21948c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: lock status after, ret=%d\n", 21958c2ecf20Sopenharmony_ci __func__, ret); 21968c2ecf20Sopenharmony_ci cfi_varsize_frob(mtd, do_printlockstatus_oneblock, 21978c2ecf20Sopenharmony_ci ofs, len, NULL); 21988c2ecf20Sopenharmony_ci#endif 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci return ret; 22018c2ecf20Sopenharmony_ci} 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_cistatic int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 22048c2ecf20Sopenharmony_ci{ 22058c2ecf20Sopenharmony_ci int ret; 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 22088c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", 22098c2ecf20Sopenharmony_ci __func__, ofs, len); 22108c2ecf20Sopenharmony_ci cfi_varsize_frob(mtd, do_printlockstatus_oneblock, 22118c2ecf20Sopenharmony_ci ofs, len, NULL); 22128c2ecf20Sopenharmony_ci#endif 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, 22158c2ecf20Sopenharmony_ci ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 22188c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: lock status after, ret=%d\n", 22198c2ecf20Sopenharmony_ci __func__, ret); 22208c2ecf20Sopenharmony_ci cfi_varsize_frob(mtd, do_printlockstatus_oneblock, 22218c2ecf20Sopenharmony_ci ofs, len, NULL); 22228c2ecf20Sopenharmony_ci#endif 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci return ret; 22258c2ecf20Sopenharmony_ci} 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_cistatic int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs, 22288c2ecf20Sopenharmony_ci uint64_t len) 22298c2ecf20Sopenharmony_ci{ 22308c2ecf20Sopenharmony_ci return cfi_varsize_frob(mtd, do_getlockstatus_oneblock, 22318c2ecf20Sopenharmony_ci ofs, len, NULL) ? 1 : 0; 22328c2ecf20Sopenharmony_ci} 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_OTP 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_citypedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, 22378c2ecf20Sopenharmony_ci u_long data_offset, u_char *buf, u_int size, 22388c2ecf20Sopenharmony_ci u_long prot_offset, u_int groupno, u_int groupsize); 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_cistatic int __xipram 22418c2ecf20Sopenharmony_cido_otp_read(struct map_info *map, struct flchip *chip, u_long offset, 22428c2ecf20Sopenharmony_ci u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) 22438c2ecf20Sopenharmony_ci{ 22448c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 22458c2ecf20Sopenharmony_ci int ret; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 22488c2ecf20Sopenharmony_ci ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); 22498c2ecf20Sopenharmony_ci if (ret) { 22508c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 22518c2ecf20Sopenharmony_ci return ret; 22528c2ecf20Sopenharmony_ci } 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci /* let's ensure we're not reading back cached data from array mode */ 22558c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, chip->start + offset, size); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci xip_disable(map, chip, chip->start); 22588c2ecf20Sopenharmony_ci if (chip->state != FL_JEDEC_QUERY) { 22598c2ecf20Sopenharmony_ci map_write(map, CMD(0x90), chip->start); 22608c2ecf20Sopenharmony_ci chip->state = FL_JEDEC_QUERY; 22618c2ecf20Sopenharmony_ci } 22628c2ecf20Sopenharmony_ci map_copy_from(map, buf, chip->start + offset, size); 22638c2ecf20Sopenharmony_ci xip_enable(map, chip, chip->start); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci /* then ensure we don't keep OTP data in the cache */ 22668c2ecf20Sopenharmony_ci INVALIDATE_CACHED_RANGE(map, chip->start + offset, size); 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci put_chip(map, chip, chip->start); 22698c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 22708c2ecf20Sopenharmony_ci return 0; 22718c2ecf20Sopenharmony_ci} 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_cistatic int 22748c2ecf20Sopenharmony_cido_otp_write(struct map_info *map, struct flchip *chip, u_long offset, 22758c2ecf20Sopenharmony_ci u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) 22768c2ecf20Sopenharmony_ci{ 22778c2ecf20Sopenharmony_ci int ret; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci while (size) { 22808c2ecf20Sopenharmony_ci unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1); 22818c2ecf20Sopenharmony_ci int gap = offset - bus_ofs; 22828c2ecf20Sopenharmony_ci int n = min_t(int, size, map_bankwidth(map)-gap); 22838c2ecf20Sopenharmony_ci map_word datum = map_word_ff(map); 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci datum = map_word_load_partial(map, datum, buf, gap, n); 22868c2ecf20Sopenharmony_ci ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE); 22878c2ecf20Sopenharmony_ci if (ret) 22888c2ecf20Sopenharmony_ci return ret; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci offset += n; 22918c2ecf20Sopenharmony_ci buf += n; 22928c2ecf20Sopenharmony_ci size -= n; 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci return 0; 22968c2ecf20Sopenharmony_ci} 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_cistatic int 22998c2ecf20Sopenharmony_cido_otp_lock(struct map_info *map, struct flchip *chip, u_long offset, 23008c2ecf20Sopenharmony_ci u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) 23018c2ecf20Sopenharmony_ci{ 23028c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 23038c2ecf20Sopenharmony_ci map_word datum; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci /* make sure area matches group boundaries */ 23068c2ecf20Sopenharmony_ci if (size != grpsz) 23078c2ecf20Sopenharmony_ci return -EXDEV; 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci datum = map_word_ff(map); 23108c2ecf20Sopenharmony_ci datum = map_word_clr(map, datum, CMD(1 << grpno)); 23118c2ecf20Sopenharmony_ci return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE); 23128c2ecf20Sopenharmony_ci} 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_cistatic int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, 23158c2ecf20Sopenharmony_ci size_t *retlen, u_char *buf, 23168c2ecf20Sopenharmony_ci otp_op_t action, int user_regs) 23178c2ecf20Sopenharmony_ci{ 23188c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 23198c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 23208c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 23218c2ecf20Sopenharmony_ci struct flchip *chip; 23228c2ecf20Sopenharmony_ci struct cfi_intelext_otpinfo *otp; 23238c2ecf20Sopenharmony_ci u_long devsize, reg_prot_offset, data_offset; 23248c2ecf20Sopenharmony_ci u_int chip_num, chip_step, field, reg_fact_size, reg_user_size; 23258c2ecf20Sopenharmony_ci u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups; 23268c2ecf20Sopenharmony_ci int ret; 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci *retlen = 0; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci /* Check that we actually have some OTP registers */ 23318c2ecf20Sopenharmony_ci if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields) 23328c2ecf20Sopenharmony_ci return -ENODATA; 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci /* we need real chips here not virtual ones */ 23358c2ecf20Sopenharmony_ci devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave; 23368c2ecf20Sopenharmony_ci chip_step = devsize >> cfi->chipshift; 23378c2ecf20Sopenharmony_ci chip_num = 0; 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci /* Some chips have OTP located in the _top_ partition only. 23408c2ecf20Sopenharmony_ci For example: Intel 28F256L18T (T means top-parameter device) */ 23418c2ecf20Sopenharmony_ci if (cfi->mfr == CFI_MFR_INTEL) { 23428c2ecf20Sopenharmony_ci switch (cfi->id) { 23438c2ecf20Sopenharmony_ci case 0x880b: 23448c2ecf20Sopenharmony_ci case 0x880c: 23458c2ecf20Sopenharmony_ci case 0x880d: 23468c2ecf20Sopenharmony_ci chip_num = chip_step - 1; 23478c2ecf20Sopenharmony_ci } 23488c2ecf20Sopenharmony_ci } 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci for ( ; chip_num < cfi->numchips; chip_num += chip_step) { 23518c2ecf20Sopenharmony_ci chip = &cfi->chips[chip_num]; 23528c2ecf20Sopenharmony_ci otp = (struct cfi_intelext_otpinfo *)&extp->extra[0]; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci /* first OTP region */ 23558c2ecf20Sopenharmony_ci field = 0; 23568c2ecf20Sopenharmony_ci reg_prot_offset = extp->ProtRegAddr; 23578c2ecf20Sopenharmony_ci reg_fact_groups = 1; 23588c2ecf20Sopenharmony_ci reg_fact_size = 1 << extp->FactProtRegSize; 23598c2ecf20Sopenharmony_ci reg_user_groups = 1; 23608c2ecf20Sopenharmony_ci reg_user_size = 1 << extp->UserProtRegSize; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci while (len > 0) { 23638c2ecf20Sopenharmony_ci /* flash geometry fixup */ 23648c2ecf20Sopenharmony_ci data_offset = reg_prot_offset + 1; 23658c2ecf20Sopenharmony_ci data_offset *= cfi->interleave * cfi->device_type; 23668c2ecf20Sopenharmony_ci reg_prot_offset *= cfi->interleave * cfi->device_type; 23678c2ecf20Sopenharmony_ci reg_fact_size *= cfi->interleave; 23688c2ecf20Sopenharmony_ci reg_user_size *= cfi->interleave; 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci if (user_regs) { 23718c2ecf20Sopenharmony_ci groups = reg_user_groups; 23728c2ecf20Sopenharmony_ci groupsize = reg_user_size; 23738c2ecf20Sopenharmony_ci /* skip over factory reg area */ 23748c2ecf20Sopenharmony_ci groupno = reg_fact_groups; 23758c2ecf20Sopenharmony_ci data_offset += reg_fact_groups * reg_fact_size; 23768c2ecf20Sopenharmony_ci } else { 23778c2ecf20Sopenharmony_ci groups = reg_fact_groups; 23788c2ecf20Sopenharmony_ci groupsize = reg_fact_size; 23798c2ecf20Sopenharmony_ci groupno = 0; 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci while (len > 0 && groups > 0) { 23838c2ecf20Sopenharmony_ci if (!action) { 23848c2ecf20Sopenharmony_ci /* 23858c2ecf20Sopenharmony_ci * Special case: if action is NULL 23868c2ecf20Sopenharmony_ci * we fill buf with otp_info records. 23878c2ecf20Sopenharmony_ci */ 23888c2ecf20Sopenharmony_ci struct otp_info *otpinfo; 23898c2ecf20Sopenharmony_ci map_word lockword; 23908c2ecf20Sopenharmony_ci len -= sizeof(struct otp_info); 23918c2ecf20Sopenharmony_ci if (len <= 0) 23928c2ecf20Sopenharmony_ci return -ENOSPC; 23938c2ecf20Sopenharmony_ci ret = do_otp_read(map, chip, 23948c2ecf20Sopenharmony_ci reg_prot_offset, 23958c2ecf20Sopenharmony_ci (u_char *)&lockword, 23968c2ecf20Sopenharmony_ci map_bankwidth(map), 23978c2ecf20Sopenharmony_ci 0, 0, 0); 23988c2ecf20Sopenharmony_ci if (ret) 23998c2ecf20Sopenharmony_ci return ret; 24008c2ecf20Sopenharmony_ci otpinfo = (struct otp_info *)buf; 24018c2ecf20Sopenharmony_ci otpinfo->start = from; 24028c2ecf20Sopenharmony_ci otpinfo->length = groupsize; 24038c2ecf20Sopenharmony_ci otpinfo->locked = 24048c2ecf20Sopenharmony_ci !map_word_bitsset(map, lockword, 24058c2ecf20Sopenharmony_ci CMD(1 << groupno)); 24068c2ecf20Sopenharmony_ci from += groupsize; 24078c2ecf20Sopenharmony_ci buf += sizeof(*otpinfo); 24088c2ecf20Sopenharmony_ci *retlen += sizeof(*otpinfo); 24098c2ecf20Sopenharmony_ci } else if (from >= groupsize) { 24108c2ecf20Sopenharmony_ci from -= groupsize; 24118c2ecf20Sopenharmony_ci data_offset += groupsize; 24128c2ecf20Sopenharmony_ci } else { 24138c2ecf20Sopenharmony_ci int size = groupsize; 24148c2ecf20Sopenharmony_ci data_offset += from; 24158c2ecf20Sopenharmony_ci size -= from; 24168c2ecf20Sopenharmony_ci from = 0; 24178c2ecf20Sopenharmony_ci if (size > len) 24188c2ecf20Sopenharmony_ci size = len; 24198c2ecf20Sopenharmony_ci ret = action(map, chip, data_offset, 24208c2ecf20Sopenharmony_ci buf, size, reg_prot_offset, 24218c2ecf20Sopenharmony_ci groupno, groupsize); 24228c2ecf20Sopenharmony_ci if (ret < 0) 24238c2ecf20Sopenharmony_ci return ret; 24248c2ecf20Sopenharmony_ci buf += size; 24258c2ecf20Sopenharmony_ci len -= size; 24268c2ecf20Sopenharmony_ci *retlen += size; 24278c2ecf20Sopenharmony_ci data_offset += size; 24288c2ecf20Sopenharmony_ci } 24298c2ecf20Sopenharmony_ci groupno++; 24308c2ecf20Sopenharmony_ci groups--; 24318c2ecf20Sopenharmony_ci } 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci /* next OTP region */ 24348c2ecf20Sopenharmony_ci if (++field == extp->NumProtectionFields) 24358c2ecf20Sopenharmony_ci break; 24368c2ecf20Sopenharmony_ci reg_prot_offset = otp->ProtRegAddr; 24378c2ecf20Sopenharmony_ci reg_fact_groups = otp->FactGroups; 24388c2ecf20Sopenharmony_ci reg_fact_size = 1 << otp->FactProtRegSize; 24398c2ecf20Sopenharmony_ci reg_user_groups = otp->UserGroups; 24408c2ecf20Sopenharmony_ci reg_user_size = 1 << otp->UserProtRegSize; 24418c2ecf20Sopenharmony_ci otp++; 24428c2ecf20Sopenharmony_ci } 24438c2ecf20Sopenharmony_ci } 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci return 0; 24468c2ecf20Sopenharmony_ci} 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_cistatic int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, 24498c2ecf20Sopenharmony_ci size_t len, size_t *retlen, 24508c2ecf20Sopenharmony_ci u_char *buf) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci return cfi_intelext_otp_walk(mtd, from, len, retlen, 24538c2ecf20Sopenharmony_ci buf, do_otp_read, 0); 24548c2ecf20Sopenharmony_ci} 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_cistatic int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, 24578c2ecf20Sopenharmony_ci size_t len, size_t *retlen, 24588c2ecf20Sopenharmony_ci u_char *buf) 24598c2ecf20Sopenharmony_ci{ 24608c2ecf20Sopenharmony_ci return cfi_intelext_otp_walk(mtd, from, len, retlen, 24618c2ecf20Sopenharmony_ci buf, do_otp_read, 1); 24628c2ecf20Sopenharmony_ci} 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_cistatic int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, 24658c2ecf20Sopenharmony_ci size_t len, size_t *retlen, 24668c2ecf20Sopenharmony_ci u_char *buf) 24678c2ecf20Sopenharmony_ci{ 24688c2ecf20Sopenharmony_ci return cfi_intelext_otp_walk(mtd, from, len, retlen, 24698c2ecf20Sopenharmony_ci buf, do_otp_write, 1); 24708c2ecf20Sopenharmony_ci} 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_cistatic int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, 24738c2ecf20Sopenharmony_ci loff_t from, size_t len) 24748c2ecf20Sopenharmony_ci{ 24758c2ecf20Sopenharmony_ci size_t retlen; 24768c2ecf20Sopenharmony_ci return cfi_intelext_otp_walk(mtd, from, len, &retlen, 24778c2ecf20Sopenharmony_ci NULL, do_otp_lock, 1); 24788c2ecf20Sopenharmony_ci} 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_cistatic int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len, 24818c2ecf20Sopenharmony_ci size_t *retlen, struct otp_info *buf) 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci{ 24848c2ecf20Sopenharmony_ci return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf, 24858c2ecf20Sopenharmony_ci NULL, 0); 24868c2ecf20Sopenharmony_ci} 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_cistatic int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len, 24898c2ecf20Sopenharmony_ci size_t *retlen, struct otp_info *buf) 24908c2ecf20Sopenharmony_ci{ 24918c2ecf20Sopenharmony_ci return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf, 24928c2ecf20Sopenharmony_ci NULL, 1); 24938c2ecf20Sopenharmony_ci} 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci#endif 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_cistatic void cfi_intelext_save_locks(struct mtd_info *mtd) 24988c2ecf20Sopenharmony_ci{ 24998c2ecf20Sopenharmony_ci struct mtd_erase_region_info *region; 25008c2ecf20Sopenharmony_ci int block, status, i; 25018c2ecf20Sopenharmony_ci unsigned long adr; 25028c2ecf20Sopenharmony_ci size_t len; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci for (i = 0; i < mtd->numeraseregions; i++) { 25058c2ecf20Sopenharmony_ci region = &mtd->eraseregions[i]; 25068c2ecf20Sopenharmony_ci if (!region->lockmap) 25078c2ecf20Sopenharmony_ci continue; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci for (block = 0; block < region->numblocks; block++){ 25108c2ecf20Sopenharmony_ci len = region->erasesize; 25118c2ecf20Sopenharmony_ci adr = region->offset + block * len; 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci status = cfi_varsize_frob(mtd, 25148c2ecf20Sopenharmony_ci do_getlockstatus_oneblock, adr, len, NULL); 25158c2ecf20Sopenharmony_ci if (status) 25168c2ecf20Sopenharmony_ci set_bit(block, region->lockmap); 25178c2ecf20Sopenharmony_ci else 25188c2ecf20Sopenharmony_ci clear_bit(block, region->lockmap); 25198c2ecf20Sopenharmony_ci } 25208c2ecf20Sopenharmony_ci } 25218c2ecf20Sopenharmony_ci} 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_cistatic int cfi_intelext_suspend(struct mtd_info *mtd) 25248c2ecf20Sopenharmony_ci{ 25258c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 25268c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 25278c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 25288c2ecf20Sopenharmony_ci int i; 25298c2ecf20Sopenharmony_ci struct flchip *chip; 25308c2ecf20Sopenharmony_ci int ret = 0; 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci if ((mtd->flags & MTD_POWERUP_LOCK) 25338c2ecf20Sopenharmony_ci && extp && (extp->FeatureSupport & (1 << 5))) 25348c2ecf20Sopenharmony_ci cfi_intelext_save_locks(mtd); 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 25378c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci switch (chip->state) { 25428c2ecf20Sopenharmony_ci case FL_READY: 25438c2ecf20Sopenharmony_ci case FL_STATUS: 25448c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 25458c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 25468c2ecf20Sopenharmony_ci if (chip->oldstate == FL_READY) { 25478c2ecf20Sopenharmony_ci /* place the chip in a known state before suspend */ 25488c2ecf20Sopenharmony_ci map_write(map, CMD(0xFF), cfi->chips[i].start); 25498c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 25508c2ecf20Sopenharmony_ci chip->state = FL_PM_SUSPENDED; 25518c2ecf20Sopenharmony_ci /* No need to wake_up() on this state change - 25528c2ecf20Sopenharmony_ci * as the whole point is that nobody can do anything 25538c2ecf20Sopenharmony_ci * with the chip now anyway. 25548c2ecf20Sopenharmony_ci */ 25558c2ecf20Sopenharmony_ci } else { 25568c2ecf20Sopenharmony_ci /* There seems to be an operation pending. We must wait for it. */ 25578c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Flash device refused suspend due to pending operation (oldstate %d)\n", chip->oldstate); 25588c2ecf20Sopenharmony_ci ret = -EAGAIN; 25598c2ecf20Sopenharmony_ci } 25608c2ecf20Sopenharmony_ci break; 25618c2ecf20Sopenharmony_ci default: 25628c2ecf20Sopenharmony_ci /* Should we actually wait? Once upon a time these routines weren't 25638c2ecf20Sopenharmony_ci allowed to. Or should we return -EAGAIN, because the upper layers 25648c2ecf20Sopenharmony_ci ought to have already shut down anything which was using the device 25658c2ecf20Sopenharmony_ci anyway? The latter for now. */ 25668c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->state); 25678c2ecf20Sopenharmony_ci ret = -EAGAIN; 25688c2ecf20Sopenharmony_ci case FL_PM_SUSPENDED: 25698c2ecf20Sopenharmony_ci break; 25708c2ecf20Sopenharmony_ci } 25718c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci /* Unlock the chips again */ 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci if (ret) { 25778c2ecf20Sopenharmony_ci for (i--; i >=0; i--) { 25788c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 25838c2ecf20Sopenharmony_ci /* No need to force it into a known state here, 25848c2ecf20Sopenharmony_ci because we're returning failure, and it didn't 25858c2ecf20Sopenharmony_ci get power cycled */ 25868c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 25878c2ecf20Sopenharmony_ci chip->oldstate = FL_READY; 25888c2ecf20Sopenharmony_ci wake_up(&chip->wq); 25898c2ecf20Sopenharmony_ci } 25908c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 25918c2ecf20Sopenharmony_ci } 25928c2ecf20Sopenharmony_ci } 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci return ret; 25958c2ecf20Sopenharmony_ci} 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_cistatic void cfi_intelext_restore_locks(struct mtd_info *mtd) 25988c2ecf20Sopenharmony_ci{ 25998c2ecf20Sopenharmony_ci struct mtd_erase_region_info *region; 26008c2ecf20Sopenharmony_ci int block, i; 26018c2ecf20Sopenharmony_ci unsigned long adr; 26028c2ecf20Sopenharmony_ci size_t len; 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci for (i = 0; i < mtd->numeraseregions; i++) { 26058c2ecf20Sopenharmony_ci region = &mtd->eraseregions[i]; 26068c2ecf20Sopenharmony_ci if (!region->lockmap) 26078c2ecf20Sopenharmony_ci continue; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci for_each_clear_bit(block, region->lockmap, region->numblocks) { 26108c2ecf20Sopenharmony_ci len = region->erasesize; 26118c2ecf20Sopenharmony_ci adr = region->offset + block * len; 26128c2ecf20Sopenharmony_ci cfi_intelext_unlock(mtd, adr, len); 26138c2ecf20Sopenharmony_ci } 26148c2ecf20Sopenharmony_ci } 26158c2ecf20Sopenharmony_ci} 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_cistatic void cfi_intelext_resume(struct mtd_info *mtd) 26188c2ecf20Sopenharmony_ci{ 26198c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 26208c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 26218c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp = cfi->cmdset_priv; 26228c2ecf20Sopenharmony_ci int i; 26238c2ecf20Sopenharmony_ci struct flchip *chip; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci for (i=0; i<cfi->numchips; i++) { 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci /* Go to known state. Chip may have been power cycled */ 26328c2ecf20Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 26338c2ecf20Sopenharmony_ci /* Refresh LH28F640BF Partition Config. Register */ 26348c2ecf20Sopenharmony_ci fixup_LH28F640BF(mtd); 26358c2ecf20Sopenharmony_ci map_write(map, CMD(0xFF), cfi->chips[i].start); 26368c2ecf20Sopenharmony_ci chip->oldstate = chip->state = FL_READY; 26378c2ecf20Sopenharmony_ci wake_up(&chip->wq); 26388c2ecf20Sopenharmony_ci } 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 26418c2ecf20Sopenharmony_ci } 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci if ((mtd->flags & MTD_POWERUP_LOCK) 26448c2ecf20Sopenharmony_ci && extp && (extp->FeatureSupport & (1 << 5))) 26458c2ecf20Sopenharmony_ci cfi_intelext_restore_locks(mtd); 26468c2ecf20Sopenharmony_ci} 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_cistatic int cfi_intelext_reset(struct mtd_info *mtd) 26498c2ecf20Sopenharmony_ci{ 26508c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 26518c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 26528c2ecf20Sopenharmony_ci int i, ret; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci for (i=0; i < cfi->numchips; i++) { 26558c2ecf20Sopenharmony_ci struct flchip *chip = &cfi->chips[i]; 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci /* force the completion of any ongoing operation 26588c2ecf20Sopenharmony_ci and switch to array mode so any bootloader in 26598c2ecf20Sopenharmony_ci flash is accessible for soft reboot. */ 26608c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 26618c2ecf20Sopenharmony_ci ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); 26628c2ecf20Sopenharmony_ci if (!ret) { 26638c2ecf20Sopenharmony_ci map_write(map, CMD(0xff), chip->start); 26648c2ecf20Sopenharmony_ci chip->state = FL_SHUTDOWN; 26658c2ecf20Sopenharmony_ci put_chip(map, chip, chip->start); 26668c2ecf20Sopenharmony_ci } 26678c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 26688c2ecf20Sopenharmony_ci } 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci return 0; 26718c2ecf20Sopenharmony_ci} 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_cistatic int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val, 26748c2ecf20Sopenharmony_ci void *v) 26758c2ecf20Sopenharmony_ci{ 26768c2ecf20Sopenharmony_ci struct mtd_info *mtd; 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci mtd = container_of(nb, struct mtd_info, reboot_notifier); 26798c2ecf20Sopenharmony_ci cfi_intelext_reset(mtd); 26808c2ecf20Sopenharmony_ci return NOTIFY_DONE; 26818c2ecf20Sopenharmony_ci} 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_cistatic void cfi_intelext_destroy(struct mtd_info *mtd) 26848c2ecf20Sopenharmony_ci{ 26858c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 26868c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 26878c2ecf20Sopenharmony_ci struct mtd_erase_region_info *region; 26888c2ecf20Sopenharmony_ci int i; 26898c2ecf20Sopenharmony_ci cfi_intelext_reset(mtd); 26908c2ecf20Sopenharmony_ci unregister_reboot_notifier(&mtd->reboot_notifier); 26918c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 26928c2ecf20Sopenharmony_ci kfree(cfi->cfiq); 26938c2ecf20Sopenharmony_ci kfree(cfi->chips[0].priv); 26948c2ecf20Sopenharmony_ci kfree(cfi); 26958c2ecf20Sopenharmony_ci for (i = 0; i < mtd->numeraseregions; i++) { 26968c2ecf20Sopenharmony_ci region = &mtd->eraseregions[i]; 26978c2ecf20Sopenharmony_ci kfree(region->lockmap); 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci kfree(mtd->eraseregions); 27008c2ecf20Sopenharmony_ci} 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 27038c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al."); 27048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD chip driver for Intel/Sharp flash chips"); 27058c2ecf20Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0003"); 27068c2ecf20Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0200"); 2707