#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/of.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/xip.h>
#define AMD_BOOTLOC_BUG
#define FORCE_WORD_WRITE …
#define MAX_RETRIES …
#define SST49LF004B …
#define SST49LF040B …
#define SST49LF008A …
#define AT49BV6416 …
#define S29GL064N_MN12 …
#define CFI_SR_DRB …
#define CFI_SR_ESB …
#define CFI_SR_PSB …
#define CFI_SR_WBASB …
#define CFI_SR_SLSB …
enum cfi_quirks { … };
static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
#if !FORCE_WORD_WRITE
static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
#endif
static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
static void cfi_amdstd_sync (struct mtd_info *);
static int cfi_amdstd_suspend (struct mtd_info *);
static void cfi_amdstd_resume (struct mtd_info *);
static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
static int cfi_amdstd_get_fact_prot_info(struct mtd_info *, size_t,
size_t *, struct otp_info *);
static int cfi_amdstd_get_user_prot_info(struct mtd_info *, size_t,
size_t *, struct otp_info *);
static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t,
size_t *, u_char *);
static int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t,
size_t *, u_char *);
static int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t,
size_t *, const u_char *);
static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *, loff_t, size_t);
static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
static void cfi_amdstd_destroy(struct mtd_info *);
struct mtd_info *cfi_cmdset_0002(struct map_info *, int);
static struct mtd_info *cfi_amdstd_setup (struct mtd_info *);
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 int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static struct mtd_chip_driver cfi_amdstd_chipdrv = …;
static int cfi_use_status_reg(struct cfi_private *cfi)
{ … }
static int cfi_check_err_status(struct map_info *map, struct flchip *chip,
unsigned long adr)
{ … }
#ifdef DEBUG_CFI_FEATURES
static void cfi_tell_features(struct cfi_pri_amdstd *extp)
{
const char* erase_suspend[3] = {
"Not supported", "Read only", "Read/write"
};
const char* top_bottom[6] = {
"No WP", "8x8KiB sectors at top & bottom, no WP",
"Bottom boot", "Top boot",
"Uniform, Bottom WP", "Uniform, Top WP"
};
printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1);
printk(" Address sensitive unlock: %s\n",
(extp->SiliconRevision & 1) ? "Not required" : "Required");
if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))
printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]);
else
printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend);
if (extp->BlkProt == 0)
printk(" Block protection: Not supported\n");
else
printk(" Block protection: %d sectors per group\n", extp->BlkProt);
printk(" Temporary block unprotect: %s\n",
extp->TmpBlkUnprotect ? "Supported" : "Not supported");
printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot);
printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps);
printk(" Burst mode: %s\n",
extp->BurstMode ? "Supported" : "Not supported");
if (extp->PageMode == 0)
printk(" Page mode: Not supported\n");
else
printk(" Page mode: %d word page\n", extp->PageMode << 2);
printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n",
extp->VppMin >> 4, extp->VppMin & 0xf);
printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n",
extp->VppMax >> 4, extp->VppMax & 0xf);
if (extp->TopBottom < ARRAY_SIZE(top_bottom))
printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]);
else
printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom);
}
#endif
#ifdef AMD_BOOTLOC_BUG
static void fixup_amd_bootblock(struct mtd_info *mtd)
{ … }
#endif
#if !FORCE_WORD_WRITE
static void fixup_use_write_buffers(struct mtd_info *mtd)
{ … }
#endif
static void fixup_convert_atmel_pri(struct mtd_info *mtd)
{ … }
static void fixup_use_secsi(struct mtd_info *mtd)
{ … }
static void fixup_use_erase_chip(struct mtd_info *mtd)
{ … }
static void fixup_use_atmel_lock(struct mtd_info *mtd)
{ … }
static void fixup_old_sst_eraseregion(struct mtd_info *mtd)
{ … }
static void fixup_sst39vf(struct mtd_info *mtd)
{ … }
static void fixup_sst39vf_rev_b(struct mtd_info *mtd)
{ … }
static void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd)
{ … }
static void fixup_s29gl064n_sectors(struct mtd_info *mtd)
{ … }
static void fixup_s29gl032n_sectors(struct mtd_info *mtd)
{ … }
static void fixup_s29ns512p_sectors(struct mtd_info *mtd)
{ … }
static void fixup_quirks(struct mtd_info *mtd)
{ … }
static struct cfi_fixup cfi_nopri_fixup_table[] = …;
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_amdstd *extp)
{ … }
static int is_m29ew(struct cfi_private *cfi)
{ … }
static void cfi_fixup_m29ew_erase_suspend(struct map_info *map,
unsigned long adr)
{ … }
static void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi)
{ … }
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
{ … }
struct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
EXPORT_SYMBOL_GPL(…);
EXPORT_SYMBOL_GPL(…);
EXPORT_SYMBOL_GPL(…);
static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
{ … }
static int __xipram chip_ready(struct map_info *map, struct flchip *chip,
unsigned long addr, map_word *expected)
{ … }
static int __xipram chip_good(struct map_info *map, struct flchip *chip,
unsigned long addr, map_word *expected)
{ … }
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(0xf0), adr);
chip->state = FL_READY;
}
(void) map_read(map, adr);
xip_iprefetch();
local_irq_enable();
}
static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
unsigned long adr, int usec)
{
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
map_word status, OK = CMD(0x80);
unsigned long suspended, start = xip_currtime();
flstate_t oldstate;
do {
cpu_relax();
if (xip_irqpending() && extp &&
((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&
(cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
map_write(map, CMD(0xb0), adr);
usec -= xip_elapsed_since(start);
suspended = xip_currtime();
do {
if (xip_elapsed_since(suspended) > 100000) {
return;
}
status = map_read(map, adr);
} while (!map_word_andequal(map, status, OK, OK));
oldstate = chip->state;
if (!map_word_bitsset(map, status, CMD(0x40)))
break;
chip->state = FL_XIP_WHILE_ERASING;
chip->erase_suspended = 1;
map_write(map, CMD(0xf0), 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 != FL_XIP_WHILE_ERASING) {
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();
cfi_fixup_m29ew_erase_suspend(map, adr);
map_write(map, cfi->sector_erase_cmd, adr);
chip->state = oldstate;
start = xip_currtime();
} else if (usec >= 1000000/HZ) {
xip_cpu_idle();
}
status = map_read(map, adr);
} while (!map_word_andequal(map, status, OK, OK)
&& xip_elapsed_since(start) < usec);
}
#define UDELAY …
#define XIP_INVAL_CACHED_RANGE …
#define INVALIDATE_CACHE_UDELAY …
#else
#define xip_disable(map, chip, adr) …
#define xip_enable(map, chip, adr) …
#define XIP_INVAL_CACHED_RANGE(x...) …
#define UDELAY(map, chip, adr, usec) …
#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) …
#endif
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_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{ … }
otp_op_t;
static inline void otp_enter(struct map_info *map, struct flchip *chip,
loff_t adr, size_t len)
{ … }
static inline void otp_exit(struct map_info *map, struct flchip *chip,
loff_t adr, size_t len)
{ … }
static inline int do_read_secsi_onechip(struct map_info *map,
struct flchip *chip, loff_t adr,
size_t len, u_char *buf,
size_t grouplen)
{ … }
static int cfi_amdstd_secsi_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 do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
size_t len, u_char *buf, size_t grouplen)
{ … }
static int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr,
size_t len, u_char *buf, size_t grouplen)
{ … }
static int cfi_amdstd_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_amdstd_get_fact_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{ … }
static int cfi_amdstd_get_user_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{ … }
static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
u_char *buf)
{ … }
static int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
u_char *buf)
{ … }
static int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
const u_char *buf)
{ … }
static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{ … }
static int __xipram do_write_oneword_once(struct map_info *map,
struct flchip *chip,
unsigned long adr, map_word datum,
int mode, struct cfi_private *cfi)
{ … }
static int __xipram do_write_oneword_start(struct map_info *map,
struct flchip *chip,
unsigned long adr, int mode)
{ … }
static void __xipram do_write_oneword_done(struct map_info *map,
struct flchip *chip,
unsigned long adr, int mode)
{ … }
static int __xipram do_write_oneword_retry(struct map_info *map,
struct flchip *chip,
unsigned long adr, map_word datum,
int mode)
{ … }
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, map_word datum,
int mode)
{ … }
static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{ … }
#if !FORCE_WORD_WRITE
static int __xipram do_write_buffer_wait(struct map_info *map,
struct flchip *chip, unsigned long adr,
map_word datum)
{ … }
static void __xipram do_write_buffer_reset(struct map_info *map,
struct flchip *chip,
struct cfi_private *cfi)
{ … }
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const u_char *buf,
int len)
{ … }
static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{ … }
#endif
static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
unsigned long adr)
{ … }
static int do_panic_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, map_word datum)
{ … }
static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{ … }
static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
{ … }
static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
{ … }
static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
{ … }
static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
{ … }
static int do_atmel_lock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{ … }
static int do_atmel_unlock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{ … }
static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{ … }
static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{ … }
struct ppb_lock { … };
#define DO_XXLOCK_ONEBLOCK_LOCK …
#define DO_XXLOCK_ONEBLOCK_UNLOCK …
#define DO_XXLOCK_ONEBLOCK_GETLOCK …
static int __maybe_unused do_ppb_xxlock(struct map_info *map,
struct flchip *chip,
unsigned long adr, int len, void *thunk)
{ … }
static int __maybe_unused cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs,
uint64_t len)
{ … }
static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs,
uint64_t len)
{ … }
static int __maybe_unused cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs,
uint64_t len)
{ … }
static void cfi_amdstd_sync (struct mtd_info *mtd)
{ … }
static int cfi_amdstd_suspend(struct mtd_info *mtd)
{ … }
static void cfi_amdstd_resume(struct mtd_info *mtd)
{ … }
static int cfi_amdstd_reset(struct mtd_info *mtd)
{ … }
static int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val,
void *v)
{ … }
static void cfi_amdstd_destroy(struct mtd_info *mtd)
{ … }
MODULE_LICENSE(…) …;
MODULE_AUTHOR(…) …;
MODULE_DESCRIPTION(…) …;
MODULE_ALIAS(…) …;
MODULE_ALIAS(…) …;