#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/bitmap.h>
#include <linux/mtd/xip.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h>
#define FORCE_WORD_WRITE …
#define I82802AB …
#define I82802AC …
#define PF38F4476 …
#define M28F00AP30 …
#define M50LPW080 …
#define M50FLW080A …
#define M50FLW080B …
#define AT49BV640D …
#define AT49BV640DT …
#define LH28F640BFHE_PTTL90 …
#define LH28F640BFHE_PBTL90 …
#define LH28F640BFHE_PTTL70A …
#define LH28F640BFHE_PBTL70A …
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 *);
static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *);
static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
static void cfi_intelext_sync (struct mtd_info *);
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs,
uint64_t len);
#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 *, 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 *, 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 *);
static int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *);
static void cfi_intelext_destroy(struct mtd_info *);
struct mtd_info *cfi_cmdset_0001(struct map_info *, int);
static struct mtd_info *cfi_intelext_setup (struct mtd_info *);
static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **);
static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);
static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
#include "fwh_lock.h"
static struct mtd_chip_driver cfi_intelext_chipdrv = …;
#ifdef DEBUG_CFI_FEATURES
static void cfi_tell_features(struct cfi_pri_intelext *extp)
{
int i;
printk(" Extended Query version %c.%c\n", extp->MajorVersion, extp->MinorVersion);
printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport);
printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported");
printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported");
printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported");
printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported");
printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported");
printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported");
printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported");
printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported");
printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported");
printk(" - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported");
printk(" - Extended Flash Array: %s\n", extp->FeatureSupport&1024?"supported":"unsupported");
for (i=11; i<32; i++) {
if (extp->FeatureSupport & (1<<i))
printk(" - Unknown Bit %X: supported\n", i);
}
printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport);
printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported");
for (i=1; i<8; i++) {
if (extp->SuspendCmdSupport & (1<<i))
printk(" - Unknown Bit %X: supported\n", i);
}
printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask);
printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no");
printk(" - Lock-Down Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no");
for (i=2; i<3; i++) {
if (extp->BlkStatusRegMask & (1<<i))
printk(" - Unknown Bit %X Active: yes\n",i);
}
printk(" - EFA Lock Bit: %s\n", extp->BlkStatusRegMask&16?"yes":"no");
printk(" - EFA Lock-Down Bit: %s\n", extp->BlkStatusRegMask&32?"yes":"no");
for (i=6; i<16; i++) {
if (extp->BlkStatusRegMask & (1<<i))
printk(" - Unknown Bit %X Active: yes\n",i);
}
printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n",
extp->VccOptimal >> 4, extp->VccOptimal & 0xf);
if (extp->VppOptimal)
printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n",
extp->VppOptimal >> 4, extp->VppOptimal & 0xf);
}
#endif
static void fixup_convert_atmel_pri(struct mtd_info *mtd)
{ … }
static void fixup_at49bv640dx_lock(struct mtd_info *mtd)
{ … }
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
static void fixup_intel_strataflash(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;
printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
"erase on write disabled.\n");
extp->SuspendCmdSupport &= ~1;
}
#endif
#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
static void fixup_no_write_suspend(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
if (cfip && (cfip->FeatureSupport&4)) {
cfip->FeatureSupport &= ~4;
printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n");
}
}
#endif
static void fixup_st_m28w320ct(struct mtd_info *mtd)
{ … }
static void fixup_st_m28w320cb(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
cfi->cfiq->EraseRegionInfo[1] =
(cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
};
static int is_LH28F640BF(struct cfi_private *cfi)
{ … }
static void fixup_LH28F640BF(struct mtd_info *mtd)
{ … }
static void fixup_use_point(struct mtd_info *mtd)
{ … }
static void fixup_use_write_buffers(struct mtd_info *mtd)
{ … }
static void fixup_unlock_powerup_lock(struct mtd_info *mtd)
{ … }
static struct cfi_fixup cfi_fixup_table[] = …;
static struct cfi_fixup jedec_fixup_table[] = …;
static struct cfi_fixup fixup_table[] = …;
static void cfi_fixup_major_minor(struct cfi_private *cfi,
struct cfi_pri_intelext *extp)
{ … }
static int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip)
{ … }
static inline struct cfi_pri_intelext *
read_pri_intelext(struct map_info *map, __u16 adr)
{ … }
struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
{ … }
struct mtd_info *cfi_cmdset_0003(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
struct mtd_info *cfi_cmdset_0200(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
EXPORT_SYMBOL_GPL(…);
EXPORT_SYMBOL_GPL(…);
EXPORT_SYMBOL_GPL(…);
static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
{ … }
static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
struct cfi_private **pcfi)
{ … }
static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
{ … }
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
{ … }
static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
{ … }
#ifdef CONFIG_MTD_XIP
static void xip_disable(struct map_info *map, struct flchip *chip,
unsigned long adr)
{
(void) map_read(map, adr);
local_irq_disable();
}
static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
unsigned long adr)
{
struct cfi_private *cfi = map->fldrv_priv;
if (chip->state != FL_POINT && chip->state != FL_READY) {
map_write(map, CMD(0xff), adr);
chip->state = FL_READY;
}
(void) map_read(map, adr);
xip_iprefetch();
local_irq_enable();
}
static int __xipram xip_wait_for_operation(
struct map_info *map, struct flchip *chip,
unsigned long adr, unsigned int chip_op_time_max)
{
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
map_word status, OK = CMD(0x80);
unsigned long usec, suspended, start, done;
flstate_t oldstate, newstate;
start = xip_currtime();
usec = chip_op_time_max;
if (usec == 0)
usec = 500000;
done = 0;
do {
cpu_relax();
if (xip_irqpending() && cfip &&
((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) ||
(chip->state == FL_WRITING && (cfip->FeatureSupport&4))) &&
(cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
usec -= done;
map_write(map, CMD(0xb0), adr);
map_write(map, CMD(0x70), adr);
suspended = xip_currtime();
do {
if (xip_elapsed_since(suspended) > 100000) {
return -EIO;
}
status = map_read(map, adr);
} while (!map_word_andequal(map, status, OK, OK));
oldstate = chip->state;
if (oldstate == FL_ERASING) {
if (!map_word_bitsset(map, status, CMD(0x40)))
break;
newstate = FL_XIP_WHILE_ERASING;
chip->erase_suspended = 1;
} else {
if (!map_word_bitsset(map, status, CMD(0x04)))
break;
newstate = FL_XIP_WHILE_WRITING;
chip->write_suspended = 1;
}
chip->state = newstate;
map_write(map, CMD(0xff), adr);
(void) map_read(map, adr);
xip_iprefetch();
local_irq_enable();
mutex_unlock(&chip->mutex);
xip_iprefetch();
cond_resched();
mutex_lock(&chip->mutex);
while (chip->state != newstate) {
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
mutex_lock(&chip->mutex);
}
local_irq_disable();
map_write(map, CMD(0xd0), adr);
map_write(map, CMD(0x70), adr);
chip->state = oldstate;
start = xip_currtime();
} else if (usec >= 1000000/HZ) {
xip_cpu_idle();
}
status = map_read(map, adr);
done = xip_elapsed_since(start);
} while (!map_word_andequal(map, status, OK, OK)
&& done < usec);
return (done >= usec) ? -ETIME : 0;
}
#define XIP_INVAL_CACHED_RANGE …
#define INVAL_CACHE_AND_WAIT …
#else
#define xip_disable(map, chip, adr) …
#define xip_enable(map, chip, adr) …
#define XIP_INVAL_CACHED_RANGE(x...) …
#define INVAL_CACHE_AND_WAIT …
static int inval_cache_and_wait_for_operation(
struct map_info *map, struct flchip *chip,
unsigned long cmd_adr, unsigned long inval_adr, int inval_len,
unsigned int chip_op_time, unsigned int chip_op_time_max)
{ … }
#endif
#define WAIT_TIMEOUT(map, chip, adr, udelay, udelay_max) …
static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
{ … }
static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{ … }
static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{ … }
static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
{ … }
static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{ … }
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, map_word datum, int mode)
{ … }
static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf)
{ … }
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const struct kvec **pvec,
unsigned long *pvec_seek, int len)
{ … }
static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{ … }
static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to,
size_t len, size_t *retlen, const u_char *buf)
{ … }
static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{ … }
static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
{ … }
static void cfi_intelext_sync (struct mtd_info *mtd)
{ … }
static int __xipram do_getlockstatus_oneblock(struct map_info *map,
struct flchip *chip,
unsigned long adr,
int len, void *thunk)
{ … }
#ifdef DEBUG_LOCK_BITS
static int __xipram do_printlockstatus_oneblock(struct map_info *map,
struct flchip *chip,
unsigned long adr,
int len, void *thunk)
{
printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk));
return 0;
}
#endif
#define DO_XXLOCK_ONEBLOCK_LOCK …
#define DO_XXLOCK_ONEBLOCK_UNLOCK …
static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{ … }
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{ … }
static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{ … }
static int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs,
uint64_t len)
{ … }
#ifdef CONFIG_MTD_OTP
otp_op_t;
static int __xipram
do_otp_read(struct map_info *map, struct flchip *chip, u_long offset,
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
{ … }
static int
do_otp_write(struct map_info *map, struct flchip *chip, u_long offset,
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
{ … }
static int
do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset,
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
{ … }
static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf,
otp_op_t action, int user_regs)
{ … }
static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
u_char *buf)
{ … }
static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
u_char *buf)
{ … }
static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
const u_char *buf)
{ … }
static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
loff_t from, size_t len)
{ … }
static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{ … }
static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{ … }
#endif
static void cfi_intelext_save_locks(struct mtd_info *mtd)
{ … }
static int cfi_intelext_suspend(struct mtd_info *mtd)
{ … }
static void cfi_intelext_restore_locks(struct mtd_info *mtd)
{ … }
static void cfi_intelext_resume(struct mtd_info *mtd)
{ … }
static int cfi_intelext_reset(struct mtd_info *mtd)
{ … }
static int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val,
void *v)
{ … }
static void cfi_intelext_destroy(struct mtd_info *mtd)
{ … }
MODULE_LICENSE(…) …;
MODULE_AUTHOR(…) …;
MODULE_DESCRIPTION(…) …;
MODULE_ALIAS(…) …;
MODULE_ALIAS(…) …;