📄 cfi_cmdset_0001.c
字号:
/*
* vivi/drivers/mtd/cfi-intelext.c: Intel Extended Vendor Command Set
*
* Based on linux/drivers/mtd/chips/cfi_cmdset_0001.c
*
* $Id: cfi_cmdset_0001.c,v 1.1.1.1 2004/02/04 06:22:25 laputa Exp $
*
* History
*
* 2002-01-16: Nandy Lyu <nandy@mizi.com>
* - Initial code
*
*/
#include "config.h"
#include "mtd/mtd.h"
#include "mtd/cfi.h"
#include "heap.h"
#include "printk.h"
#ifdef CONFIG_MSG_PROGRESS
#include "vivi_lib.h"
#endif
#include <types.h>
#include <errno.h>
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_erase_varsize(struct mtd_info *, struct erase_info *);
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
struct mtd_info *cfi_cmdset_0001(struct map_info *, int);
static struct mtd_info *cfi_intelext_setup (struct map_info *);
#ifdef CONFIG_DEBUG_CFI
static void cfi_tell_features(struct cfi_pri_intelext *extp)
{
int i;
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");
for (i=9; 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(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no");
for (i=2; 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 >> 8, extp->VccOptimal & 0xf);
if (extp->VppOptimal)
printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n",
extp->VppOptimal >> 8, extp->VppOptimal & 0xf);
}
#endif
/* This routine is made available to other mtd code via
* inter_module_register. It must only be accessed through
* inter_module_get which will bump the use count of this module. The
* addresses passed back in cfi are valid as long as the use count of
* this module is non-zero, i.e. between inter_module_get and
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
*/
struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
int i;
__u32 base = cfi->chips[0].start;
if (cfi->cfi_mode == CFI_MODE_CFI) {
/*
* It's a real CFI chip, not one for which the probe
* routine faked a CFI structure. So we read the feature
* table from it.
*/
__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
struct cfi_pri_intelext *extp;
int ofs_factor = cfi->interleave * cfi->device_type;
//printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr);
if (!adr)
return NULL;
/* Switch it into Query Mode */
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
extp = mmalloc(sizeof(*extp));
if (!extp) {
printk("Failed to allocate memory\n");
return NULL;
}
/* Read in the Extended Query Table */
for (i=0; i<sizeof(*extp); i++) {
((unsigned char *)extp)[i] =
cfi_read_query(map, (base+((adr+i)*ofs_factor)));
}
if (extp->MajorVersion != '1' ||
(extp->MinorVersion < '0' || extp->MinorVersion > '2')) {
printk(" Unknown IntelExt Extended Query "
"version %c.%c.\n", extp->MajorVersion,
extp->MinorVersion);
mfree(extp);
return NULL;
}
/* Do some byteswapping if necessary */
extp->FeatureSupport = extp->FeatureSupport;
extp->BlkStatusRegMask = extp->BlkStatusRegMask;
extp->ProtRegAddr = extp->ProtRegAddr;
#ifdef CONFIG_DEBUG_CFI
/* Tell the user about it in lots of lovely detail */
cfi_tell_features(extp);
#endif
/* Install our own private info structure */
cfi->cmdset_priv = extp;
}
for (i=0; i< cfi->numchips; i++) {
cfi->chips[i].word_write_time = 128;
cfi->chips[i].buffer_write_time = 128;
cfi->chips[i].erase_time = 1024;
}
/* Make sure it's in read mode */
cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL);
return cfi_intelext_setup(map);
}
static struct mtd_info *cfi_intelext_setup(struct map_info *map)
{
struct cfi_private *cfi = map->fldrv_priv;
struct mtd_info *mtd;
unsigned long offset = 0;
int i, j;
unsigned long devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave;
mtd = mmalloc(sizeof(*mtd));
if (!mtd) {
printk("Failed to allocate memory for MTD device\n");
mfree(cfi->cmdset_priv);
return NULL;
}
memset(mtd, 0, sizeof(*mtd));
mtd->priv = map;
mtd->type = MTD_NORFLASH;
mtd->size = devsize * cfi->numchips;
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
mtd->eraseregions = mmalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions);
if (!mtd->eraseregions) {
printk("Failed to allocate memory for MTD erase region info\n");
mfree(cfi->cmdset_priv);
return NULL;
}
for (i = 0; i < cfi->cfiq->NumEraseRegions; i++) {
unsigned long ernum, ersize;
ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
if (mtd->erasesize < ersize) {
mtd->erasesize = ersize;
}
for (j = 0; j < cfi->numchips; j++) {
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
}
offset += (ersize * ernum);
}
if (offset != devsize) {
/* Argh */
printk("Sum of region (%lx) != total size of set of interleave chips (%lx)\n", offset, devsize);
mfree(mtd->eraseregions);
mfree(cfi->cmdset_priv);
return NULL;
}
for (i = 0; i < mtd->numeraseregions; i++) {
printk("%d: offset = 0x%x, size = 0x%x, blocks = %d\n",
i, mtd->eraseregions[i].offset,
mtd->eraseregions[i].erasesize,
mtd->eraseregions[i].numblocks);
}
/* Also select the correct geometry setup too */
mtd->erase = cfi_intelext_erase_varsize;
#ifndef FORCE_WORD_WRTE
if (cfi->cfiq->BufWriteTimeoutTyp) {
printk("Using buffer write method\n");
mtd->write = cfi_intelext_write_buffers;
} else {
#else
}
#endif
printk("Using word write method\n");
mtd->write = cfi_intelext_write_words;
}
mtd->lock = cfi_intelext_lock;
mtd->unlock = cfi_intelext_unlock;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
return mtd;
}
int do_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, cfi_word datum)
{
struct cfi_private *cfi = map->fldrv_priv;
cfi_word status = 0, status_OK;
unsigned long timeo;
adr += chip->start;
/* Let's determine this according to the interleave only once */
status_OK = CMD(0x80);
/* 弊成 荤侩 沁澜 - 2002-06-25 nandy */
timeo = cfi->cfiq->WordWriteTimeoutMax * 1000;
ENABLE_VPP(map);
cfi_write(map, CMD(0x40), adr);
cfi_write(map, datum, adr);
/* wait for it to be programmed */
while (timeo > 0) {
status = cfi_read(map, adr);
if ((status & status_OK) == status_OK)
break;
timeo--;
}
/* done */
DISABLE_VPP(map);
/* clear status */
cfi_write(map, CMD(0x50), adr);
/* report errors */
if (timeo <= 0) {
printk("\nFailed do_write_oneword() operation\n");
printk(" Offset = 0x%08lx, datum = 0x%08lx, status = 0x%08lx\n",
adr, datum, status);
cfi_write(map, CMD(0xff), adr);
return -EIO;
}
cfi_write(map, CMD(0xff), adr);
return 0;
}
static int cfi_intelext_write_words(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
int ret = 0;
int chipnum;
unsigned long ofs;
*retlen = 0;
if (!len)
return 0;
chipnum = to >> cfi->chipshift;
ofs = to - (chipnum << cfi->chipshift);
/* It it's not bus-aligned, do the first byte write */
if (ofs & (CFIDEV_BUSWIDTH-1)) {
unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
int gap = ofs - bus_ofs;
int i = 0, n = 0;
u_char tmp_buf[8];
cfi_word datum;
while (gap--)
tmp_buf[i++] = 0xff;
while (len && i < CFIDEV_BUSWIDTH)
tmp_buf[i++] = buf[n++], len--;
while (i < CFIDEV_BUSWIDTH)
tmp_buf[i++] = 0xff;
if (cfi_buswidth_is_2()) {
datum = *(__u16*)tmp_buf;
} else if (cfi_buswidth_is_4()) {
datum = *(__u32*)tmp_buf;
} else if (cfi_buswidth_is_8()) {
datum = *(__u64*)tmp_buf;
} else {
return -EINVAL; /* should never happen, but be safe */
}
ret = do_write_oneword(map, &cfi->chips[chipnum], bus_ofs, datum);
if (ret) return ret;
ofs += n;
buf += n;
(*retlen) += n;
if (ofs >> cfi->chipshift) {
chipnum++;
ofs = 0;
if (chipnum == cfi->numchips)
return 0;
}
}
while (len >= CFIDEV_BUSWIDTH) {
cfi_word datum;
if (cfi_buswidth_is_1()) {
datum = *(__u8*)buf;
} else if (cfi_buswidth_is_2()) {
datum = *(__u16*)buf;
} else if (cfi_buswidth_is_4()) {
datum = *(__u32*)buf;
} else if (cfi_buswidth_is_8()) {
datum = *(__u64*)buf;
} else {
return -EINVAL;
}
ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum);
if (ret) return ret;
ofs += CFIDEV_BUSWIDTH;
buf += CFIDEV_BUSWIDTH;
(*retlen) += CFIDEV_BUSWIDTH;
len -= CFIDEV_BUSWIDTH;
if (ofs >> cfi->chipshift) {
chipnum++;
ofs = 0;
if (chipnum == cfi->numchips)
return 0;
}
}
if (len & (CFIDEV_BUSWIDTH-1)) {
int i = 0, n = 0;
u_char tmp_buf[8];
cfi_word datum;
while (len--)
tmp_buf[i++] = buf[n++];
while (i < CFIDEV_BUSWIDTH)
tmp_buf[i++] = 0xff;
if (cfi_buswidth_is_2()) {
datum = *(__u16*)tmp_buf;
} else if (cfi_buswidth_is_4()) {
datum = *(__u32*)tmp_buf;
} else if (cfi_buswidth_is_8()) {
datum = *(__u64*)tmp_buf;
} else {
return -EINVAL; /* should never happen, but be safe */
}
ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum);
if (ret)
return ret;
(*retlen) += n;
}
return 0;
}
static int do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const u_char *buf, int len)
{
struct cfi_private *cfi = map->fldrv_priv;
cfi_word status, status_OK;
unsigned long cmd_adr, timeo;
int wbufsize, z;
wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
adr += chip->start;
cmd_adr = adr & ~(wbufsize - 1);
/* Let's determinc this according to the interleave only once */
status_OK = CMD(0x80);
timeo = cfi->cfiq->BufWriteTimeoutMax * 1000;
ENABLE_VPP(map);
cfi_write(map, CMD(0xe8), cmd_adr);
for (;;) {
status = cfi_read(map, cmd_adr);
if ((status & status_OK) == status_OK)
break;
if (timeo < 0) {
/* Argh. Not ready for write to buffer */
cfi_write(map, CMD(0x70), cmd_adr);
DISABLE_VPP(map);
printk("Chip not ready for buffer write. Xstatus = 0x%llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr));
/* Odd. Clear status bits */
cfi_write(map, CMD(0x50), cmd_adr);
cfi_write(map, CMD(0x70), cmd_adr);
cfi_write(map, CMD(0xff), adr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -