diff options
Diffstat (limited to 'drivers/mtd/chips/cfi_cmdset_0001.c')
| -rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0001.c | 209 |
1 files changed, 143 insertions, 66 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 77514430f1fe..c10693ba265b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1,8 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common Flash Interface support: * Intel Extended Vendor Command Set (ID 0x0001) * - * (C) 2000 Red Hat. GPL'd + * (C) 2000 Red Hat. * * * 10/10/2000 Nicolas Pitre <nico@fluxnic.net> @@ -21,7 +22,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/init.h> #include <asm/io.h> #include <asm/byteorder.h> @@ -46,6 +46,7 @@ #define I82802AB 0x00ad #define I82802AC 0x00ac #define PF38F4476 0x881c +#define M28F00AP30 0x8963 /* STMicroelectronics chips */ #define M50LPW080 0x002F #define M50FLW080A 0x0080 @@ -53,6 +54,11 @@ /* Atmel chips */ #define AT49BV640D 0x02de #define AT49BV640DT 0x02db +/* Sharp chips */ +#define LH28F640BFHE_PTTL90 0x00b0 +#define LH28F640BFHE_PBTL90 0x00b1 +#define LH28F640BFHE_PTTL70A 0x00b2 +#define LH28F640BFHE_PBTL70A 0x00b3 static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -67,12 +73,13 @@ static int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs, #ifdef CONFIG_MTD_OTP static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_write_user_prot_reg(struct mtd_info *, loff_t, size_t, + size_t *, const u_char *); static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); -static int cfi_intelext_get_fact_prot_info (struct mtd_info *, - struct otp_info *, size_t); -static int cfi_intelext_get_user_prot_info (struct mtd_info *, - struct otp_info *, size_t); +static int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t, + size_t *, struct otp_info *); +static int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t, + size_t *, struct otp_info *); #endif static int cfi_intelext_suspend (struct mtd_info *); static void cfi_intelext_resume (struct mtd_info *); @@ -259,6 +266,36 @@ static void fixup_st_m28w320cb(struct mtd_info *mtd) (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; }; +static int is_LH28F640BF(struct cfi_private *cfi) +{ + /* Sharp LH28F640BF Family */ + if (cfi->mfr == CFI_MFR_SHARP && ( + cfi->id == LH28F640BFHE_PTTL90 || cfi->id == LH28F640BFHE_PBTL90 || + cfi->id == LH28F640BFHE_PTTL70A || cfi->id == LH28F640BFHE_PBTL70A)) + return 1; + return 0; +} + +static void fixup_LH28F640BF(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + + /* Reset the Partition Configuration Register on LH28F640BF + * to a single partition (PCR = 0x000): PCR is embedded into A0-A15. */ + if (is_LH28F640BF(cfi)) { + printk(KERN_INFO "Reset Partition Config. Register: 1 Partition of 4 planes\n"); + map_write(map, CMD(0x60), 0); + map_write(map, CMD(0x04), 0); + + /* We have set one single partition thus + * Simultaneous Operations are not allowed */ + printk(KERN_INFO "cfi_cmdset_0001: Simultaneous Operations disabled\n"); + extp->FeatureSupport &= ~512; + } +} + static void fixup_use_point(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -310,6 +347,8 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct }, { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb }, { CFI_MFR_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock }, + { CFI_MFR_SHARP, CFI_ID_ANY, fixup_unlock_powerup_lock }, + { CFI_MFR_SHARP, CFI_ID_ANY, fixup_LH28F640BF }, { 0, 0, NULL } }; @@ -339,6 +378,17 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi, extp->MinorVersion = '1'; } +static int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip) +{ + /* + * Micron(was Numonyx) 1Gbit bottom boot are buggy w.r.t + * Erase Supend for their small Erase Blocks(0x8000) + */ + if (cfi->mfr == CFI_MFR_INTEL && cfi->id == M28F00AP30) + return 1; + return 0; +} + static inline struct cfi_pri_intelext * read_pri_intelext(struct map_info *map, __u16 adr) { @@ -372,8 +422,25 @@ read_pri_intelext(struct map_info *map, __u16 adr) extra_size = 0; /* Protection Register info */ - extra_size += (extp->NumProtectionFields - 1) * - sizeof(struct cfi_intelext_otpinfo); + if (extp->NumProtectionFields) { + struct cfi_intelext_otpinfo *otp = + (struct cfi_intelext_otpinfo *)&extp->extra[0]; + + extra_size += (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); + + if (extp_size >= sizeof(*extp) + extra_size) { + int i; + + /* Do some byteswapping if necessary */ + for (i = 0; i < extp->NumProtectionFields - 1; i++) { + otp->ProtRegAddr = le32_to_cpu(otp->ProtRegAddr); + otp->FactGroups = le16_to_cpu(otp->FactGroups); + otp->UserGroups = le16_to_cpu(otp->UserGroups); + otp++; + } + } + } } if (extp->MinorVersion >= '1') { @@ -435,10 +502,8 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) int i; mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) { - printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + if (!mtd) return NULL; - } mtd->priv = map; mtd->type = MTD_NORFLASH; @@ -562,12 +627,11 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; - mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) - * mtd->numeraseregions, GFP_KERNEL); - if (!mtd->eraseregions) { - printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); + mtd->eraseregions = kcalloc(mtd->numeraseregions, + sizeof(struct mtd_erase_region_info), + GFP_KERNEL); + if (!mtd->eraseregions) goto setup_err; - } for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { unsigned long ernum, ersize; @@ -582,6 +646,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL); + if (!mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap) + goto setup_err; } offset += (ersize * ernum); } @@ -618,6 +684,10 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) return mtd; setup_err: + if (mtd->eraseregions) + for (i=0; i<cfi->cfiq->NumEraseRegions; i++) + for (j=0; j<cfi->numchips; j++) + kfree(mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap); kfree(mtd->eraseregions); kfree(mtd); kfree(cfi->cmdset_priv); @@ -644,14 +714,16 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, */ if (extp && extp->MajorVersion == '1' && extp->MinorVersion >= '3' && extp->FeatureSupport & (1 << 9)) { + int offs = 0; struct cfi_private *newcfi; struct flchip *chip; struct flchip_shared *shared; - int offs, numregions, numparts, partshift, numvirtchips, i, j; + int numregions, numparts, partshift, numvirtchips, i, j; /* Protection Register info */ - offs = (extp->NumProtectionFields - 1) * - sizeof(struct cfi_intelext_otpinfo); + if (extp->NumProtectionFields) + offs = (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); /* Burst Read info */ offs += extp->extra[offs+1]+2; @@ -705,10 +777,13 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, } numvirtchips = cfi->numchips * numparts; - newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); + newcfi = kmalloc(struct_size(newcfi, chips, numvirtchips), + GFP_KERNEL); if (!newcfi) return -ENOMEM; - shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL); + shared = kmalloc_array(cfi->numchips, + sizeof(struct flchip_shared), + GFP_KERNEL); if (!shared) { kfree(newcfi); return -ENOMEM; @@ -780,7 +855,7 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long /* Someone else might have been playing with it. */ return -EAGAIN; } - /* Fall through */ + fallthrough; case FL_READY: case FL_CFI_QUERY: case FL_JEDEC_QUERY: @@ -793,21 +868,30 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) goto sleep; + /* Do not allow suspend iff read/write to EB address */ + if ((adr & chip->in_progress_block_mask) == + chip->in_progress_block_addr) + goto sleep; + + /* do not suspend small EBs, buggy Micron Chips */ + if (cfi_is_micron_28F00AP30(cfi, chip) && + (chip->in_progress_block_mask == ~(0x8000-1))) + goto sleep; /* Erase suspend */ - map_write(map, CMD(0xB0), adr); + map_write(map, CMD(0xB0), chip->in_progress_block_addr); /* If the flash has finished erasing, then 'erase suspend' * appears to make some (28F320) flash devices switch to * 'read' mode. Make sure that we switch to 'read status' * mode so we get the right data. --rmk */ - map_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), chip->in_progress_block_addr); chip->oldstate = FL_ERASING; chip->state = FL_ERASE_SUSPENDING; chip->erase_suspended = 1; for (;;) { - status = map_read(map, adr); + status = map_read(map, chip->in_progress_block_addr); if (map_word_andequal(map, status, status_OK, status_OK)) break; @@ -844,7 +928,7 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) return 0; - /* Fall through */ + fallthrough; default: sleep: set_current_state(TASK_UNINTERRUPTIBLE); @@ -1003,8 +1087,8 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad sending the 0x70 (Read Status) command to an erasing chip and expecting it to be ignored, that's what we do. */ - map_write(map, CMD(0xd0), adr); - map_write(map, CMD(0x70), adr); + map_write(map, CMD(0xd0), chip->in_progress_block_addr); + map_write(map, CMD(0x70), chip->in_progress_block_addr); chip->oldstate = FL_READY; chip->state = FL_ERASING; break; @@ -1290,7 +1374,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a { unsigned long cmd_addr; struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; + int ret; adr += chip->start; @@ -1320,7 +1404,7 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len, struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs, last_end = 0; int chipnum; - int ret = 0; + int ret; if (!map->virt) return -EINVAL; @@ -1487,7 +1571,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, { struct cfi_private *cfi = map->fldrv_priv; map_word status, write_cmd; - int ret=0; + int ret; adr += chip->start; @@ -1561,7 +1645,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; + int ret; int chipnum; unsigned long ofs; @@ -1654,6 +1738,12 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, initial_adr = adr; cmd_adr = adr & ~(wbufsize-1); + /* Sharp LH28F640BF chips need the first address for the + * Page Buffer Program command. See Table 5 of + * LH28F320BF, LH28F640BF, LH28F128BF Series (Appendix FUM00701) */ + if (is_LH28F640BF(cfi)) + cmd_adr = adr; + /* Let's determine this according to the interleave only once */ write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9); @@ -1802,7 +1892,7 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs, struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; - int ret = 0; + int ret; int chipnum; unsigned long ofs, vec_seek, i; size_t len = 0; @@ -1889,6 +1979,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; chip->erase_suspended = 0; + chip->in_progress_block_addr = adr; + chip->in_progress_block_mask = ~(len - 1); ret = INVAL_CACHE_AND_WAIT(map, chip, adr, adr, len, @@ -1949,20 +2041,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) { - unsigned long ofs, len; - int ret; - - ofs = instr->addr; - len = instr->len; - - ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); - if (ret) - return ret; - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; + return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr, + instr->len, NULL); } static void cfi_intelext_sync (struct mtd_info *mtd) @@ -2385,10 +2465,10 @@ static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, - u_char *buf) + const u_char *buf) { return cfi_intelext_otp_walk(mtd, from, len, retlen, - buf, do_otp_write, 1); + (u_char *)buf, do_otp_write, 1); } static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, @@ -2399,24 +2479,19 @@ static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, NULL, do_otp_lock, 1); } -static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) -{ - size_t retlen; - int ret; +static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) - ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0); - return ret ? : retlen; +{ + return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf, + NULL, 0); } -static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { - size_t retlen; - int ret; - - ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1); - return ret ? : retlen; + return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf, + NULL, 1); } #endif @@ -2492,6 +2567,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) anyway? The latter for now. */ printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->state); ret = -EAGAIN; + break; case FL_PM_SUSPENDED: break; } @@ -2557,6 +2633,8 @@ static void cfi_intelext_resume(struct mtd_info *mtd) /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { + /* Refresh LH28F640BF Partition Config. Register */ + fixup_LH28F640BF(mtd); map_write(map, CMD(0xFF), cfi->chips[i].start); chip->oldstate = chip->state = FL_READY; wake_up(&chip->wq); @@ -2619,8 +2697,7 @@ static void cfi_intelext_destroy(struct mtd_info *mtd) kfree(cfi); for (i = 0; i < mtd->numeraseregions; i++) { region = &mtd->eraseregions[i]; - if (region->lockmap) - kfree(region->lockmap); + kfree(region->lockmap); } kfree(mtd->eraseregions); } |
