Commit 637465e0 authored by David Woodhouse's avatar David Woodhouse Committed by David Woodhouse

MTD: NOR flash chip driver updates

Mostly from Eric Biederman for supporting BIOS flash.

    - Move support firmware hub style lock and unlock into fhw_lock.h (from cfi_cmdset_0002)
    - Move cfi_varsize_frob into cfi_util from cfi_cmdset_0001.c and cfi_cmdset_0002.c
    - reduce gen_probe probe failuers to a debug level message
    - Modify cfi_fixup to take a struct mtd_info instead of a struct map_info
      So that the fixup routines can modify the mtd functions.
    - Modify cfi_cmdset_0001() to allocate and initialize the mtd structure
      before calling cfi_fixup.
    - Modify cfi_cmdset_0002() to allocate and initialize the mtd structure
      before calling cfi_fixup.
    - Refactor the hard coded fixups in cfi_cmdset_0001 and cfi_cmdset_0002
      so the improved cfi_fixup infrastructure.
    - Rewrote amd76xrom and ichxrom.
      They now report their starting physical address in their name.
      They now both handle multiple bankwidth configurations
      They both can create multipe mtd devices.
      They both now assume the rom windows are properly opened by the BIOS
       or whatever runs previous to them.
      Their code is now synchromized so it is almost identical,
         and could be a starting point for a x86_rom_probe.
Signed-Off-By: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 8ea99926
......@@ -4,7 +4,7 @@
*
* (C) 2000 Red Hat. GPL'd
*
* $Id: cfi_cmdset_0001.c,v 1.154 2004/08/09 13:19:43 dwmw2 Exp $
* $Id: cfi_cmdset_0001.c,v 1.157 2004/10/15 20:00:26 nico Exp $
*
*
* 10/10/2000 Nicolas Pitre <nico@cam.org>
......@@ -39,6 +39,12 @@
// debugging, turns off buffer write mode if set to 1
#define FORCE_WORD_WRITE 0
#define MANUFACTURER_INTEL 0x0089
#define I82802AB 0x00ad
#define I82802AC 0x00ac
#define MANUFACTURER_ST 0x0020
#define M50LPW080 0x002F
static int cfi_intelext_read (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_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
......@@ -55,7 +61,7 @@ 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 map_info *);
static struct mtd_info *cfi_intelext_setup (struct mtd_info *);
static int cfi_intelext_partition_fixup(struct map_info *, struct cfi_private **);
static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
......@@ -63,6 +69,11 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
size_t len);
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"
/*
* *********** SETUP AND PROBE BITS ***********
......@@ -123,8 +134,9 @@ static void cfi_tell_features(struct cfi_pri_intelext *extp)
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
static void fixup_intel_strataflash(struct map_info *map, void* param)
static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
......@@ -134,16 +146,18 @@ static void fixup_intel_strataflash(struct map_info *map, void* param)
}
#endif
static void fixup_st_m28w320ct(struct map_info *map, void* param)
static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */
cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */
}
static void fixup_st_m28w320cb(struct map_info *map, void* param)
static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
/* Note this is done after the region info is endian swapped */
......@@ -151,24 +165,51 @@ static void fixup_st_m28w320cb(struct map_info *map, void* param)
(cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
};
static struct cfi_fixup fixup_table[] = {
static void fixup_use_point(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
if (!mtd->point && map_is_linear(map)) {
mtd->point = cfi_intelext_point;
mtd->unpoint = cfi_intelext_unpoint;
}
}
static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->cfiq->BufWriteTimeoutTyp) {
printk(KERN_INFO "Using buffer write method\n" );
mtd->write = cfi_intelext_write_buffers;
}
}
static struct cfi_fixup cfi_fixup_table[] = {
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
{
CFI_MFR_ANY, CFI_ID_ANY,
fixup_intel_strataflash, NULL
},
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
#endif
{
0x0020, /* STMicroelectronics */
0x00ba, /* M28W320CT */
fixup_st_m28w320ct, NULL
}, {
0x0020, /* STMicroelectronics */
0x00bb, /* M28W320CB */
fixup_st_m28w320cb, NULL
}, {
0, 0, NULL, NULL
}
#if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL },
#endif
{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
{ 0, 0, NULL, NULL }
};
static struct cfi_fixup jedec_fixup_table[] = {
{ MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, },
{ MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, },
{ MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, },
{ 0, 0, NULL, NULL }
};
static struct cfi_fixup fixup_table[] = {
/* The CFI vendor ids and the JEDEC vendor IDs appear
* to be common. It is like the devices id's are as
* well. This table is to pick all cases where
* we know that is the case.
*/
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL },
{ 0, 0, NULL, NULL }
};
/* This routine is made available to other mtd code via
......@@ -181,8 +222,30 @@ static struct cfi_fixup fixup_table[] = {
struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
struct mtd_info *mtd;
int i;
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd) {
printk(KERN_ERR "Failed to allocate memory for MTD device\n");
return NULL;
}
memset(mtd, 0, sizeof(*mtd));
mtd->priv = map;
mtd->type = MTD_NORFLASH;
/* Fill in the default mtd operations */
mtd->erase = cfi_intelext_erase_varsize;
mtd->read = cfi_intelext_read;
mtd->write = cfi_intelext_write_words;
mtd->sync = cfi_intelext_sync;
mtd->lock = cfi_intelext_lock;
mtd->unlock = cfi_intelext_unlock;
mtd->suspend = cfi_intelext_suspend;
mtd->resume = cfi_intelext_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
if (cfi->cfi_mode == CFI_MODE_CFI) {
/*
* It's a real CFI chip, not one for which the probe
......@@ -193,8 +256,10 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
struct cfi_pri_intelext *extp;
extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp");
if (!extp)
if (!extp) {
kfree(mtd);
return NULL;
}
/* Do some byteswapping if necessary */
extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport);
......@@ -204,7 +269,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
/* Install our own private info structure */
cfi->cmdset_priv = extp;
cfi_fixup(map, fixup_table);
cfi_fixup(mtd, cfi_fixup_table);
#ifdef DEBUG_CFI_FEATURES
/* Tell the user about it in lots of lovely detail */
......@@ -215,6 +280,12 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n");
}
}
else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
/* Apply jedec specific fixups */
cfi_fixup(mtd, jedec_fixup_table);
}
/* Apply generic fixups */
cfi_fixup(mtd, fixup_table);
for (i=0; i< cfi->numchips; i++) {
cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
......@@ -225,28 +296,19 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
map->fldrv = &cfi_intelext_chipdrv;
return cfi_intelext_setup(map);
return cfi_intelext_setup(mtd);
}
static struct mtd_info *cfi_intelext_setup(struct map_info *map)
static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
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 = kmalloc(sizeof(*mtd), GFP_KERNEL);
//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
if (!mtd) {
printk(KERN_ERR "Failed to allocate memory for MTD device\n");
goto setup_err;
}
memset(mtd, 0, sizeof(*mtd));
mtd->priv = map;
mtd->type = MTD_NORFLASH;
mtd->size = devsize * cfi->numchips;
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
......@@ -286,34 +348,10 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map)
mtd->eraseregions[i].numblocks);
}
/* Also select the correct geometry setup too */
mtd->erase = cfi_intelext_erase_varsize;
mtd->read = cfi_intelext_read;
if (map_is_linear(map)) {
mtd->point = cfi_intelext_point;
mtd->unpoint = cfi_intelext_unpoint;
}
if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
printk(KERN_INFO "Using buffer write method\n" );
mtd->write = cfi_intelext_write_buffers;
} else {
printk(KERN_INFO "Using word write method\n" );
mtd->write = cfi_intelext_write_words;
}
#if 0
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
#endif
mtd->sync = cfi_intelext_sync;
mtd->lock = cfi_intelext_lock;
mtd->unlock = cfi_intelext_unlock;
mtd->suspend = cfi_intelext_suspend;
mtd->resume = cfi_intelext_resume;
mtd->flags = MTD_CAP_NORFLASH;
map->fldrv = &cfi_intelext_chipdrv;
mtd->name = map->name;
/* This function has the potential to distort the reality
a bit and therefore should be called last. */
......@@ -515,7 +553,8 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
return 0;
case FL_ERASING:
if (!(cfip->FeatureSupport & 2) ||
if (!cfip ||
!(cfip->FeatureSupport & 2) ||
!(mode == FL_READY || mode == FL_POINT ||
(mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
goto sleep;
......@@ -884,7 +923,7 @@ static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, s
int base_offst,reg_sz;
/* Check that we actually have some protection registers */
if(!(extp->FeatureSupport&64)){
if(!extp || !(extp->FeatureSupport&64)){
printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
return 0;
}
......@@ -903,7 +942,7 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, s
int base_offst,reg_sz;
/* Check that we actually have some protection registers */
if(!(extp->FeatureSupport&64)){
if(!extp || !(extp->FeatureSupport&64)){
printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
return 0;
}
......@@ -1308,102 +1347,6 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to,
return 0;
}
typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk);
static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
loff_t ofs, size_t len, void *thunk)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
unsigned long adr;
int chipnum, ret = 0;
int i, first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
if (ofs > mtd->size)
return -EINVAL;
if ((len + ofs) > mtd->size)
return -EINVAL;
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
i = 0;
/* Skip all erase regions which are ended before the start of
the requested erase. Actually, to save on the calculations,
we skip to the first erase region which starts after the
start of the requested erase, and then go back one.
*/
while (i < mtd->numeraseregions && ofs >= regions[i].offset)
i++;
i--;
/* OK, now i is pointing at the erase region in which this
erase request starts. Check the start of the requested
erase range is aligned with the erase size which is in
effect here.
*/
if (ofs & (regions[i].erasesize-1))
return -EINVAL;
/* Remember the erase region we start on */
first = i;
/* Next, check that the end of the requested erase is aligned
* with the erase region at that address.
*/
while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
i++;
/* As before, drop back one to point at the region in which
the address actually falls
*/
i--;
if ((ofs + len) & (regions[i].erasesize-1))
return -EINVAL;
chipnum = ofs >> cfi->chipshift;
adr = ofs - (chipnum << cfi->chipshift);
i=first;
while(len) {
unsigned long chipmask;
int size = regions[i].erasesize;
ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
if (ret)
return ret;
adr += size;
len -= size;
chipmask = (1 << cfi->chipshift) - 1;
if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask))
i++;
if (adr >> cfi->chipshift) {
adr = 0;
chipnum++;
if (chipnum >= cfi->numchips)
break;
}
}
return 0;
}
static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{
......@@ -1550,7 +1493,7 @@ int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
ofs = instr->addr;
len = instr->len;
ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
if (ret)
return ret;
......@@ -1695,17 +1638,17 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
#ifdef DEBUG_LOCK_BITS
printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
__FUNCTION__, ofs, len);
cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif
ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock,
ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
#ifdef DEBUG_LOCK_BITS
printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
__FUNCTION__, ret);
cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif
......@@ -1719,17 +1662,17 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
#ifdef DEBUG_LOCK_BITS
printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
__FUNCTION__, ofs, len);
cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif
ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock,
ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);
#ifdef DEBUG_LOCK_BITS
printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
__FUNCTION__, ret);
cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif
......
......@@ -13,7 +13,7 @@
*
* This code is GPL
*
* $Id: cfi_cmdset_0002.c,v 1.106 2004/08/09 14:02:32 dwmw2 Exp $
* $Id: cfi_cmdset_0002.c,v 1.110 2004/09/24 04:26:04 eric Exp $
*
*/
......@@ -40,13 +40,15 @@
#define MAX_WORD_RETRIES 3
#define MANUFACTURER_AMD 0x0001
#define MANUFACTURER_SST 0x00BF
#define SST49LF004B 0x0060
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 *);
static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
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 int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t);
static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t);
static void cfi_amdstd_sync (struct mtd_info *);
static int cfi_amdstd_suspend (struct mtd_info *);
static void cfi_amdstd_resume (struct mtd_info *);
......@@ -55,8 +57,11 @@ static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u
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 map_info *);
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 struct mtd_chip_driver cfi_amdstd_chipdrv = {
.probe = NULL, /* Not usable directly */
......@@ -66,7 +71,6 @@ static struct mtd_chip_driver cfi_amdstd_chipdrv = {
};
/* #define DEBUG_LOCK_BITS */
/* #define DEBUG_CFI_FEATURES */
......@@ -122,8 +126,9 @@ static void cfi_tell_features(struct cfi_pri_amdstd *extp)
#ifdef AMD_BOOTLOC_BUG
/* Wheee. Bring me the head of someone at AMD. */
static void fixup_amd_bootblock(struct map_info *map, void* param)
static void fixup_amd_bootblock(struct mtd_info *mtd, void* param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
__u8 major = extp->MajorVersion;
......@@ -141,14 +146,61 @@ static void fixup_amd_bootblock(struct map_info *map, void* param)
}
#endif
static struct cfi_fixup fixup_table[] = {
static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->cfiq->BufWriteTimeoutTyp) {
DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
mtd->write = cfi_amdstd_write_buffers;
}
}
static void fixup_use_secsi(struct mtd_info *mtd, void *param)
{
/* Setup for chips with a secsi area */
mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
}
static void fixup_use_erase_chip(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
if ((cfi->cfiq->NumEraseRegions == 1) &&
((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
mtd->erase = cfi_amdstd_erase_chip;
}
}
static struct cfi_fixup cfi_fixup_table[] = {
#ifdef AMD_BOOTLOC_BUG
{
0x0001, /* AMD */
CFI_ID_ANY,
fixup_amd_bootblock, NULL
},
{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL },
#endif
{ CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, },
{ CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, },
{ CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, },
{ CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, },
{ CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, },
{ CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, },
#if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
#endif
{ 0, 0, NULL, NULL }
};
static struct cfi_fixup jedec_fixup_table[] = {
{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
{ 0, 0, NULL, NULL }
};
static struct cfi_fixup fixup_table[] = {
/* The CFI vendor ids and the JEDEC vendor IDs appear
* to be common. It is like the devices id's are as
* well. This table is to pick all cases where
* we know that is the case.
*/
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL },
{ 0, 0, NULL, NULL }
};
......@@ -156,10 +208,30 @@ static struct cfi_fixup fixup_table[] = {
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned char bootloc;
struct mtd_info *mtd;
int i;
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd) {
printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
return NULL;
}
memset(mtd, 0, sizeof(*mtd));
mtd->priv = map;
mtd->type = MTD_NORFLASH;
/* Fill in the default mtd operations */
mtd->erase = cfi_amdstd_erase_varsize;
mtd->write = cfi_amdstd_write_words;
mtd->read = cfi_amdstd_read;
mtd->sync = cfi_amdstd_sync;
mtd->suspend = cfi_amdstd_suspend;
mtd->resume = cfi_amdstd_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
if (cfi->cfi_mode==CFI_MODE_CFI){
unsigned char bootloc;
/*
* It's a real CFI chip, not one for which the probe
* routine faked a CFI structure. So we read the feature
......@@ -169,13 +241,16 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
struct cfi_pri_amdstd *extp;
extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
if (!extp)
if (!extp) {
kfree(mtd);
return NULL;
}
/* Install our own private info structure */
cfi->cmdset_priv = extp;
cfi_fixup(map, fixup_table);
/* Apply cfi device specific fixups */
cfi_fixup(mtd, cfi_fixup_table);
#ifdef DEBUG_CFI_FEATURES
/* Tell the user about it in lots of lovely detail */
......@@ -201,43 +276,28 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
cfi->cfiq->EraseRegionInfo[j] = swap;
}
}
/*
* These might already be setup (more correctly) by
* jedec_probe.c - still need it for cfi_probe.c path.
*/
if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) {
switch (cfi->device_type) {
case CFI_DEVICETYPE_X8:
/* Set the default CFI lock/unlock addresses */
cfi->addr_unlock1 = 0x555;
cfi->addr_unlock2 = 0x2aa;
break;
case CFI_DEVICETYPE_X16:
/* Modify the unlock address if we are in compatibility mode */
if ( /* x16 in x8 mode */
((cfi->device_type == CFI_DEVICETYPE_X8) &&
(cfi->cfiq->InterfaceDesc == 2)) ||
/* x32 in x16 mode */
((cfi->device_type == CFI_DEVICETYPE_X16) &&
(cfi->cfiq->InterfaceDesc == 4)))
{
cfi->addr_unlock1 = 0xaaa;
if (map_bankwidth(map) == cfi_interleave(cfi)) {
/* X16 chip(s) in X8 mode */
cfi->addr_unlock2 = 0x555;
} else {
cfi->addr_unlock2 = 0x554;
}
break;
case CFI_DEVICETYPE_X32:
cfi->addr_unlock1 = 0x1554;
if (map_bankwidth(map) == cfi_interleave(cfi)*2) {
/* X32 chip(s) in X16 mode */
cfi->addr_unlock1 = 0xaaa;
} else {
cfi->addr_unlock2 = 0xaa8;
}
break;
default:
printk(KERN_WARNING
"MTD %s(): Unsupported device type %d\n",
__func__, cfi->device_type);
return NULL;
}
}
} /* CFI mode */
else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
/* Apply jedec specific fixups */
cfi_fixup(mtd, jedec_fixup_table);
}
/* Apply generic fixups */
cfi_fixup(mtd, fixup_table);
for (i=0; i< cfi->numchips; i++) {
cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
......@@ -247,31 +307,21 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
map->fldrv = &cfi_amdstd_chipdrv;
return cfi_amdstd_setup(map);
return cfi_amdstd_setup(mtd);
}
static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct mtd_info *mtd;
unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
unsigned long offset = 0;
int i,j;
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
printk(KERN_NOTICE "number of %s chips: %d\n",
(cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips);
if (!mtd) {
printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
goto setup_err;
}
memset(mtd, 0, sizeof(*mtd));
mtd->priv = map;
mtd->type = MTD_NORFLASH;
/* Also select the correct geometry setup too */
/* Select the correct geometry setup */
mtd->size = devsize * cfi->numchips;
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
......@@ -312,54 +362,10 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
}
#endif
if (mtd->numeraseregions == 1
&& ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) {
mtd->erase = cfi_amdstd_erase_chip;
} else {
mtd->erase = cfi_amdstd_erase_varsize;
mtd->lock = cfi_amdstd_lock_varsize;
mtd->unlock = cfi_amdstd_unlock_varsize;
}
if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
mtd->write = cfi_amdstd_write_buffers;
} else {
DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" );
mtd->write = cfi_amdstd_write_words;
}
mtd->read = cfi_amdstd_read;
/* FIXME: erase-suspend-program is broken. See
http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */
printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n");
/* does this chip have a secsi area? */
if(cfi->mfr==1){
switch(cfi->id){
case 0x50:
case 0x53:
case 0x55:
case 0x56:
case 0x5C:
case 0x5F:
/* Yes */
mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
default:
;
}
}
mtd->sync = cfi_amdstd_sync;
mtd->suspend = cfi_amdstd_suspend;
mtd->resume = cfi_amdstd_resume;
mtd->flags = MTD_CAP_NORFLASH;
map->fldrv = &cfi_amdstd_chipdrv;
mtd->name = map->name;
__module_get(THIS_MODULE);
return mtd;
......@@ -434,6 +440,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
goto sleep;
if (!(mode == FL_READY || mode == FL_POINT
|| !cfip
|| (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
|| (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
goto sleep;
......@@ -624,14 +631,12 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
chip->state = FL_READY;
/* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
map_copy_from(map, buf, adr, len);
/* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
......@@ -732,17 +737,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
ENABLE_VPP(map);
retry:
/*
* The CFI_DEVICETYPE_X8 argument is needed even when
* cfi->device_type != CFI_DEVICETYPE_X8. The addresses for
* command sequences don't scale even when the device is
* wider. This is the case for many of the cfi_send_gen_cmd()
* below. I'm not sure, however, why some use
* cfi->device_type.
*/
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
map_write(map, datum, adr);
chip->state = FL_WRITING;
......@@ -958,7 +955,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
/* see comments in do_write_oneword() regarding uWriteTimeo. */
static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
int ret = -EIO;
unsigned long cmd_adr;
int z, words;
......@@ -980,9 +977,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
__func__, adr, datum.x[0] );
ENABLE_VPP(map);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
/* Write Buffer Load */
map_write(map, CMD(0x25), cmd_adr);
......@@ -1081,8 +1078,8 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
size_t local_len = (-ofs)&(map_bankwidth(map)-1);
if (local_len > len)
local_len = len;
ret = cfi_amdstd_write_words(mtd, to, local_len,
retlen, buf);
ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
local_len, retlen, buf);
if (ret)
return ret;
ofs += local_len;
......@@ -1128,7 +1125,8 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
if (len) {
size_t retlen_dregs = 0;
ret = cfi_amdstd_write_words(mtd, to, len, &retlen_dregs, buf);
ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
len, &retlen_dregs, buf);
*retlen += retlen_dregs;
return ret;
......@@ -1163,12 +1161,12 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
__func__, chip->start );
ENABLE_VPP(map);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
chip->state = FL_ERASING;
chip->erase_suspended = 0;
......@@ -1229,100 +1227,7 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
}
typedef int (*frob_t)(struct map_info *map, struct flchip *chip,
unsigned long adr, void *thunk);
static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob,
loff_t ofs, size_t len, void *thunk)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
unsigned long adr;
int chipnum, ret = 0;
int i, first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
if (ofs > mtd->size)
return -EINVAL;
if ((len + ofs) > mtd->size)
return -EINVAL;
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
i = 0;
/* Skip all erase regions which are ended before the start of
the requested erase. Actually, to save on the calculations,
we skip to the first erase region which starts after the
start of the requested erase, and then go back one.
*/
while (i < mtd->numeraseregions && ofs >= regions[i].offset)
i++;
i--;
/* OK, now i is pointing at the erase region in which this
erase request starts. Check the start of the requested
erase range is aligned with the erase size which is in
effect here.
*/
if (ofs & (regions[i].erasesize-1))
return -EINVAL;
/* Remember the erase region we start on */
first = i;
/* Next, check that the end of the requested erase is aligned
* with the erase region at that address.
*/
while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
i++;
/* As before, drop back one to point at the region in which
the address actually falls
*/
i--;
if ((ofs + len) & (regions[i].erasesize-1))
return -EINVAL;
chipnum = ofs >> cfi->chipshift;
adr = ofs - (chipnum << cfi->chipshift);
i=first;
while (len) {
ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
if (ret)
return ret;
adr += regions[i].erasesize;
len -= regions[i].erasesize;
if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
i++;
if (adr >> cfi->chipshift) {
adr = 0;
chipnum++;
if (chipnum >= cfi->numchips)
break;
}
}
return 0;
}
static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
......@@ -1342,11 +1247,11 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
__func__, adr );
ENABLE_VPP(map);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
map_write(map, CMD(0x30), adr);
chip->state = FL_ERASING;
......@@ -1415,7 +1320,7 @@ int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
ofs = instr->addr;
len = instr->len;
ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
if (ret)
return ret;
......@@ -1588,137 +1493,6 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
}
}
#ifdef DEBUG_LOCK_BITS
static int do_printlockstatus_oneblock(struct map_info *map,
struct flchip *chip,
unsigned long adr,
void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
int ofs_factor = cfi->interleave * cfi->device_type;
cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
adr, cfi_read_query(map, adr+(2*ofs_factor)));
cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL);
return 0;
}
#define debug_dump_locks(mtd, frob, ofs, len, thunk) \
cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk))
#else
#define debug_dump_locks(...)
#endif /* DEBUG_LOCK_BITS */
struct xxlock_thunk {
uint8_t val;
flstate_t state;
};
#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING})
#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING})
/*
* FIXME - this is *very* specific to a particular chip. It likely won't
* work for all chips that require unlock. It also hasn't been tested
* with interleaved chips.
*/
static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk;
int ret;
/*
* This is easy because these are writes to registers and not writes
* to flash memory - that means that we don't have to check status
* and timeout.
*/
adr += chip->start;
/*
* lock block registers:
* - on 64k boundariesand
* - bit 1 set high
* - block lock registers are 4MiB lower - overflow subtract (danger)
*/
adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff;
cfi_spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_LOCKING);
if (ret) {
cfi_spin_unlock(chip->mutex);
return ret;
}
chip->state = xxlt->state;
map_write(map, CMD(xxlt->val), adr);
/* Done and happy. */
chip->state = FL_READY;
put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex);
return 0;
}
static int cfi_amdstd_lock_varsize(struct mtd_info *mtd,
loff_t ofs,
size_t len)
{
int ret;
DEBUG(MTD_DEBUG_LEVEL3,
"%s: lock status before, ofs=0x%08llx, len=0x%08zX\n",
__func__, ofs, len);
debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
(void *)&DO_XXLOCK_ONEBLOCK_LOCK);
DEBUG(MTD_DEBUG_LEVEL3,
"%s: lock status after, ret=%d\n",
__func__, ret);
debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
return ret;
}
static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd,
loff_t ofs,
size_t len)
{
int ret;
DEBUG(MTD_DEBUG_LEVEL3,
"%s: lock status before, ofs=0x%08llx, len=0x%08zX\n",
__func__, ofs, len);
debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
(void *)&DO_XXLOCK_ONEBLOCK_UNLOCK);
DEBUG(MTD_DEBUG_LEVEL3,
"%s: lock status after, ret=%d\n",
__func__, ret);
debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
return ret;
}
static void cfi_amdstd_destroy(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
......
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
$Id: cfi_probe.c,v 1.77 2004/07/14 08:38:44 dwmw2 Exp $
$Id: cfi_probe.c,v 1.79 2004/10/20 23:04:01 dwmw2 Exp $
*/
#include <linux/config.h>
......@@ -244,12 +244,27 @@ static char *vendorname(__u16 vendor)
case P_ID_AMD_EXT:
return "AMD/Fujitsu Extended";
case P_ID_WINBOND:
return "Winbond Standard";
case P_ID_ST_ADV:
return "ST Advanced";
case P_ID_MITSUBISHI_STD:
return "Mitsubishi Standard";
case P_ID_MITSUBISHI_EXT:
return "Mitsubishi Extended";
case P_ID_SST_PAGE:
return "SST Page Write";
case P_ID_INTEL_PERFORMANCE:
return "Intel Performance Code";
case P_ID_INTEL_DATA:
return "Intel Data";
case P_ID_RESERVED:
return "Not Allowed / Reserved for Future Use";
......@@ -327,6 +342,10 @@ static void print_cfi_ident(struct cfi_ident *cfip)
printk(" - x32-only asynchronous interface\n");
break;
case 4:
printk(" - supports x16 and x32 via Word# with asynchronous interface\n");
break;
case 65535:
printk(" - Not Allowed / Reserved\n");
break;
......
......@@ -7,7 +7,7 @@
*
* This code is covered by the GPL.
*
* $Id: cfi_util.c,v 1.4 2004/07/14 08:38:44 dwmw2 Exp $
* $Id: cfi_util.c,v 1.5 2004/08/12 06:40:23 eric Exp $
*
*/
......@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/compatmac.h>
......@@ -74,19 +75,114 @@ cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
EXPORT_SYMBOL(cfi_read_pri);
void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups)
void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_fixup *f;
for (f=fixups; f->fixup; f++) {
if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
((f->id == CFI_ID_ANY) || (f->id == cfi->id))) {
f->fixup(map, f->param);
f->fixup(mtd, f->param);
}
}
}
EXPORT_SYMBOL(cfi_fixup);
int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
loff_t ofs, size_t len, void *thunk)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
unsigned long adr;
int chipnum, ret = 0;
int i, first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
if (ofs > mtd->size)
return -EINVAL;
if ((len + ofs) > mtd->size)
return -EINVAL;
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
i = 0;
/* Skip all erase regions which are ended before the start of
the requested erase. Actually, to save on the calculations,
we skip to the first erase region which starts after the
start of the requested erase, and then go back one.
*/
while (i < mtd->numeraseregions && ofs >= regions[i].offset)
i++;
i--;
/* OK, now i is pointing at the erase region in which this
erase request starts. Check the start of the requested
erase range is aligned with the erase size which is in
effect here.
*/
if (ofs & (regions[i].erasesize-1))
return -EINVAL;
/* Remember the erase region we start on */
first = i;
/* Next, check that the end of the requested erase is aligned
* with the erase region at that address.
*/
while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
i++;
/* As before, drop back one to point at the region in which
the address actually falls
*/
i--;
if ((ofs + len) & (regions[i].erasesize-1))
return -EINVAL;
chipnum = ofs >> cfi->chipshift;
adr = ofs - (chipnum << cfi->chipshift);
i=first;
while(len) {
unsigned long chipmask;
int size = regions[i].erasesize;
ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
if (ret)
return ret;
adr += size;
len -= size;
chipmask = (1 << cfi->chipshift) - 1;
if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask))
i++;
if (adr >> cfi->chipshift) {
adr = 0;
chipnum++;
if (chipnum >= cfi->numchips)
break;
}
}
return 0;
}
EXPORT_SYMBOL(cfi_varsize_frob);
MODULE_LICENSE("GPL");
#ifndef FWH_LOCK_H
#define FWH_LOCK_H
enum fwh_lock_state {
FWH_UNLOCKED = 0,
FWH_DENY_WRITE = 1,
FWH_IMMUTABLE = 2,
FWH_DENY_READ = 4,
};
struct fwh_xxlock_thunk {
enum fwh_lock_state val;
flstate_t state;
};
#define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING})
#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING})
/*
* This locking/unlock is specific to firmware hub parts. Only one
* is known that supports the Intel command set. Firmware
* hub parts cannot be interleaved as they are on the LPC bus
* so this code has not been tested with interleaved chips,
* and will likely fail in that context.
*/
static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk;
int ret;
/* Refuse the operation if the we cannot look behind the chip */
if (chip->start < 0x400000) {
DEBUG( MTD_DEBUG_LEVEL3,
"MTD %s(): chip->start: %lx wanted >= 0x400000\n",
__func__, chip->start );
return -EIO;
}
/*
* lock block registers:
* - on 64k boundariesand
* - bit 1 set high
* - block lock registers are 4MiB lower - overflow subtract (danger)
*
* The address manipulation is first done on the logical address
* which is 0 at the start of the chip, and then the offset of
* the individual chip is addted to it. Any other order a weird
* map offset could cause problems.
*/
adr = (adr & ~0xffffUL) | 0x2;
adr += chip->start - 0x400000;
/*
* This is easy because these are writes to registers and not writes
* to flash memory - that means that we don't have to check status
* and timeout.
*/
cfi_spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_LOCKING);
if (ret) {
cfi_spin_unlock(chip->mutex);
return ret;
}
chip->state = xxlt->state;
map_write(map, CMD(xxlt->val), adr);
/* Done and happy. */
chip->state = FL_READY;
put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex);
return 0;
}
static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
{
int ret;
ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
(void *)&FWH_XXLOCK_ONEBLOCK_LOCK);
return ret;
}
static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
{
int ret;
ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
(void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK);
return ret;
}
static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param)
{
printk(KERN_NOTICE "using fwh lock/unlock method\n");
/* Setup for the chips with the fwh lock method */
mtd->lock = fwh_lock_varsize;
mtd->unlock = fwh_unlock_varsize;
}
#endif /* FWH_LOCK_H */
......@@ -2,7 +2,7 @@
* Routines common to all CFI-type probes.
* (C) 2001-2003 Red Hat, Inc.
* GPL'd
* $Id: gen_probe.c,v 1.19 2004/07/13 22:33:32 dwmw2 Exp $
* $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $
*/
#include <linux/kernel.h>
......@@ -64,7 +64,7 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
interleave and device type, etc. */
if (!genprobe_new_chip(map, cp, &cfi)) {
/* The probe didn't like it */
printk(KERN_WARNING "%s: Found no %s device at location zero\n",
printk(KERN_DEBUG "%s: Found no %s device at location zero\n",
cp->name, map->name);
return NULL;
}
......@@ -169,8 +169,12 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
cfi->interleave = nr_chips;
for (type = 0; type < 3; type++) {
cfi->device_type = 1<<type;
/* Minimum device size. Don't look for one 8-bit device
in a 16-bit bus, etc. */
type = map_bankwidth(map) / nr_chips;
for (; type <= CFI_DEVICETYPE_X32; type<<=1) {
cfi->device_type = type;
if (cp->probe_chip(map, 0, NULL, cfi))
return 1;
......
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
$Id: jedec_probe.c,v 1.51 2004/07/14 14:44:30 thayne Exp $
$Id: jedec_probe.c,v 1.57 2004/09/17 11:45:05 eric Exp $
See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
for the standard this probe goes back to.
......@@ -107,6 +107,7 @@
#define I82802AC 0x00ac
/* Macronix */
#define MX29LV040C 0x004F
#define MX29LV160T 0x22C4
#define MX29LV160B 0x2249
#define MX29F016 0x00AD
......@@ -128,6 +129,7 @@
#define M50FW040 0x002C
#define M50FW080 0x002D
#define M50FW016 0x002E
#define M50LPW080 0x002F
/* SST */
#define SST29EE020 0x0010
......@@ -181,8 +183,8 @@ enum uaddr {
struct unlock_addr {
int addr1;
int addr2;
u32 addr1;
u32 addr2;
};
......@@ -512,12 +514,12 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x10000,8),
}
}, {
.mfr_id = MANUFACTURER_AMD,
.dev_id = AM29F002T,
.name = "AMD AM29F002T",
.DevSize = SIZE_256KiB,
.NumEraseRegions = 4,
.regions = {ERASEINFO(0x10000,3),
mfr_id: MANUFACTURER_AMD,
dev_id: AM29F002T,
name: "AMD AM29F002T",
DevSize: SIZE_256KiB,
NumEraseRegions: 4,
regions: {ERASEINFO(0x10000,3),
ERASEINFO(0x08000,1),
ERASEINFO(0x02000,2),
ERASEINFO(0x04000,1)
......@@ -768,12 +770,12 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x04000,1)
}
}, {
.mfr_id = MANUFACTURER_HYUNDAI,
.dev_id = HY29F002T,
.name = "Hyundai HY29F002T",
.DevSize = SIZE_256KiB,
.NumEraseRegions = 4,
.regions = {ERASEINFO(0x10000,3),
mfr_id: MANUFACTURER_HYUNDAI,
dev_id: HY29F002T,
name: "Hyundai HY29F002T",
DevSize: SIZE_256KiB,
NumEraseRegions: 4,
regions: {ERASEINFO(0x10000,3),
ERASEINFO(0x08000,1),
ERASEINFO(0x02000,2),
ERASEINFO(0x04000,1)
......@@ -1082,6 +1084,19 @@ static const struct amd_flash_info jedec_table[] = {
.regions = {
ERASEINFO(0x10000,16),
}
}, {
.mfr_id = MANUFACTURER_MACRONIX,
.dev_id = MX29LV040C,
.name = "Macronix MX29LV040C",
.uaddr = {
[0] = MTD_UADDR_0x0555_0x02AA, /* x8 */
},
.DevSize = SIZE_512KiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 1,
.regions = {
ERASEINFO(0x10000,8),
}
}, {
.mfr_id = MANUFACTURER_MACRONIX,
.dev_id = MX29LV160T,
......@@ -1162,12 +1177,12 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x10000,7),
}
}, {
.mfr_id = MANUFACTURER_MACRONIX,
.dev_id = MX29F002T,
.name = "Macronix MX29F002T",
.DevSize = SIZE_256KiB,
.NumEraseRegions = 4,
.regions = {ERASEINFO(0x10000,3),
mfr_id: MANUFACTURER_MACRONIX,
dev_id: MX29F002T,
name: "Macronix MX29F002T",
DevSize: SIZE_256KiB,
NumEraseRegions: 4,
regions: {ERASEINFO(0x10000,3),
ERASEINFO(0x08000,1),
ERASEINFO(0x02000,2),
ERASEINFO(0x04000,1)
......@@ -1247,7 +1262,7 @@ static const struct amd_flash_info jedec_table[] = {
.DevSize = SIZE_256KiB,
.CmdSet = P_ID_SST_PAGE,
.NumEraseRegions= 1,
.regions = {ERASEINFO(0x01000,64),
regions: {ERASEINFO(0x01000,64),
}
}, {
.mfr_id = MANUFACTURER_SST,
......@@ -1259,7 +1274,7 @@ static const struct amd_flash_info jedec_table[] = {
.DevSize = SIZE_256KiB,
.CmdSet = P_ID_SST_PAGE,
.NumEraseRegions= 1,
.regions = {ERASEINFO(0x01000,64),
regions: {ERASEINFO(0x01000,64),
}
}, {
.mfr_id = MANUFACTURER_SST,
......@@ -1378,6 +1393,22 @@ static const struct amd_flash_info jedec_table[] = {
.regions = {
ERASEINFO(0x01000,256),
}
}, {
.mfr_id = MANUFACTURER_SST, /* should be CFI */
.dev_id = SST39LF160,
.name = "SST 39LF160",
.uaddr = {
[0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */
[1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 2,
.regions = {
ERASEINFO(0x1000,256),
ERASEINFO(0x1000,256)
}
}, {
.mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
.dev_id = M29W800DT,
......@@ -1498,6 +1529,19 @@ static const struct amd_flash_info jedec_table[] = {
.regions = {
ERASEINFO(0x10000,32),
}
}, {
.mfr_id = MANUFACTURER_ST,
.dev_id = M50LPW080,
.name = "ST M50LPW080",
.uaddr = {
[0] = MTD_UADDR_UNNECESSARY, /* x8 */
},
.DevSize = SIZE_1MiB,
.CmdSet = P_ID_INTEL_EXT,
.NumEraseRegions= 1,
.regions = {
ERASEINFO(0x10000,16),
}
}, {
.mfr_id = MANUFACTURER_TOSHIBA,
.dev_id = TC58FVT160,
......@@ -1624,20 +1668,20 @@ static inline u32 jedec_read_mfr(struct map_info *map, __u32 base,
{
map_word result;
unsigned long mask;
u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type);
mask = (1 << (cfi->device_type * 8)) -1;
result = map_read(map, base);
result = map_read(map, base + ofs);
return result.x[0] & mask;
}
static inline u32 jedec_read_id(struct map_info *map, __u32 base,
struct cfi_private *cfi)
{
int osf;
map_word result;
unsigned long mask;
osf = cfi->interleave *cfi->device_type;
u32 ofs = cfi_build_cmd_addr(1, cfi_interleave(cfi), cfi->device_type);
mask = (1 << (cfi->device_type * 8)) -1;
result = map_read(map, base + osf);
result = map_read(map, base + ofs);
return result.x[0] & mask;
}
......@@ -1653,9 +1697,11 @@ static inline void jedec_reset(u32 base, struct map_info *map,
* as they will ignore the writes and dont care what address
* the F0 is written to */
if(cfi->addr_unlock1) {
/*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
DEBUG( MTD_DEBUG_LEVEL3,
"reset unlock called %x %x \n",
cfi->addr_unlock1,cfi->addr_unlock2);
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
}
cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
......@@ -1700,7 +1746,6 @@ static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_ty
static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
{
int i,num_erase_regions;
unsigned long mask;
__u8 uaddr;
printk("Found: %s\n",jedec_table[index].name);
......@@ -1736,9 +1781,8 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
}
/* Mask out address bits which are smaller than the device type */
mask = ~(p_cfi->device_type-1);
p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask;
p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask;
p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1;
p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2;
return 1; /* ok */
}
......@@ -1759,7 +1803,6 @@ static inline int jedec_match( __u32 base,
int rc = 0; /* failure until all tests pass */
u32 mfr, id;
__u8 uaddr;
unsigned long mask;
/*
* The IDs must match. For X16 and X32 devices operating in
......@@ -1797,7 +1840,7 @@ static inline int jedec_match( __u32 base,
DEBUG( MTD_DEBUG_LEVEL3,
"MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n",
__func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) );
if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) {
if ( base + cfi_interleave(cfi) * ( 1 << finfo->DevSize ) > map->size ) {
DEBUG( MTD_DEBUG_LEVEL3,
"MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n",
__func__, finfo->mfr_id, finfo->dev_id,
......@@ -1810,18 +1853,16 @@ static inline int jedec_match( __u32 base,
goto match_done;
}
mask = ~(cfi->device_type-1);
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n",
__func__, cfi->addr_unlock1, cfi->addr_unlock2 );
if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr
&& ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 ||
(unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) {
&& ( unlock_addrs[uaddr].addr1 != cfi->addr_unlock1 ||
unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) {
DEBUG( MTD_DEBUG_LEVEL3,
"MTD %s(): 0x%.4lx 0x%.4lx did not match\n",
"MTD %s(): 0x%.4x 0x%.4x did not match\n",
__func__,
unlock_addrs[uaddr].addr1 & mask,
unlock_addrs[uaddr].addr2 & mask);
unlock_addrs[uaddr].addr1,
unlock_addrs[uaddr].addr2);
goto match_done;
}
......@@ -1857,10 +1898,10 @@ static inline int jedec_match( __u32 base,
*/
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ );
if(cfi->addr_unlock1) {
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
}
cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
/* FIXME - should have a delay before continuing */
match_done:
......@@ -1873,19 +1914,18 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
{
int i;
enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
u32 probe_offset1, probe_offset2;
retry:
if (!cfi->numchips) {
unsigned long mask = ~(cfi->device_type-1);
uaddr_idx++;
if (MTD_UADDR_UNNECESSARY == uaddr_idx)
return 0;
/* Mask out address bits which are smaller than the device type */
cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask;
cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask;
cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1;
cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2;
}
/* Make certain we aren't probing past the end of map */
......@@ -1896,19 +1936,19 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
return 0;
}
if ((base + cfi->addr_unlock1) >= map->size) {
printk(KERN_NOTICE
"Probe at addr_unlock1(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n",
base, cfi->addr_unlock1, map->size -1);
return 0;
}
if ((base + cfi->addr_unlock2) >= map->size) {
printk(KERN_NOTICE
"Probe at addr_unlock2(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n",
base, cfi->addr_unlock2, map->size -1);
return 0;
/* Ensure the unlock addresses we try stay inside the map */
probe_offset1 = cfi_build_cmd_addr(
cfi->addr_unlock1,
cfi_interleave(cfi),
cfi->device_type);
probe_offset2 = cfi_build_cmd_addr(
cfi->addr_unlock1,
cfi_interleave(cfi),
cfi->device_type);
if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) ||
((base + probe_offset2 + map_bankwidth(map)) >= map->size))
{
goto retry;
}
/* Reset */
......@@ -1916,10 +1956,10 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
/* Autoselect Mode */
if(cfi->addr_unlock1) {
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
}
cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
/* FIXME - should have a delay before continuing */
if (!cfi->numchips) {
......@@ -1930,7 +1970,7 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
cfi->id = jedec_read_id(map, base, cfi);
DEBUG(MTD_DEBUG_LEVEL3,
"Search for id:(%02x %02x) interleave(%d) type(%d)\n",
cfi->mfr, cfi->id, cfi->interleave, cfi->device_type);
cfi->mfr, cfi->id, cfi_interleave(cfi), cfi->device_type);
for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) {
if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) {
DEBUG( MTD_DEBUG_LEVEL3,
......@@ -2004,7 +2044,7 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
jedec_reset(base, map, cfi);
printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
map->name, cfi->interleave, cfi->device_type*8, base,
map->name, cfi_interleave(cfi), cfi->device_type*8, base,
map->bankwidth*8);
return 1;
......
......@@ -2,7 +2,7 @@
* amd76xrom.c
*
* Normal mappings of chips in physical memory
* $Id: amd76xrom.c,v 1.12 2004/07/14 14:44:31 thayne Exp $
* $Id: amd76xrom.c,v 1.17 2004/09/18 01:59:56 eric Exp $
*/
#include <linux/module.h>
......@@ -12,61 +12,73 @@
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#include <linux/config.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/list.h>
#define xstr(s) str(s)
#define str(s) #s
#define MOD_NAME xstr(KBUILD_BASENAME)
#define MTD_DEV_NAME_LENGTH 16
#define ADDRESS_NAME_LEN 18
#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
struct amd76xrom_window {
void __iomem *virt;
unsigned long phys;
unsigned long size;
struct list_head maps;
struct resource rsrc;
struct pci_dev *pdev;
};
struct amd76xrom_map_info {
struct list_head list;
struct map_info map;
struct mtd_info *mtd;
void __iomem * window_addr;
u32 window_start, window_size;
struct pci_dev *pdev;
struct resource window_rsrc;
struct resource rom_rsrc;
char mtd_name[MTD_DEV_NAME_LENGTH];
struct resource rsrc;
char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
};
static struct amd76xrom_map_info amd76xrom_map = {
.map = {
.name = MOD_NAME,
.size = 0,
.bankwidth = 1,
}
/* remaining fields of structure are initialized to 0 */
static struct amd76xrom_window amd76xrom_window = {
.maps = LIST_HEAD_INIT(amd76xrom_window.maps),
};
static void amd76xrom_cleanup(struct amd76xrom_map_info *info)
static void amd76xrom_cleanup(struct amd76xrom_window *window)
{
struct amd76xrom_map_info *map, *scratch;
u8 byte;
if (window->pdev) {
/* Disable writes through the rom window */
pci_read_config_byte(info->pdev, 0x40, &byte);
pci_write_config_byte(info->pdev, 0x40, byte & ~1);
if (info->mtd) {
del_mtd_device(info->mtd);
map_destroy(info->mtd);
info->mtd = NULL;
info->map.virt = NULL;
pci_read_config_byte(window->pdev, 0x40, &byte);
pci_write_config_byte(window->pdev, 0x40, byte & ~1);
}
/* Free all of the mtd devices */
list_for_each_entry_safe(map, scratch, &window->maps, list) {
if (map->rsrc.parent) {
release_resource(&map->rsrc);
}
if (info->rom_rsrc.parent)
release_resource(&info->rom_rsrc);
if (info->window_rsrc.parent)
release_resource(&info->window_rsrc);
if (info->window_addr) {
iounmap(info->window_addr);
info->window_addr = NULL;
del_mtd_device(map->mtd);
map_destroy(map->mtd);
list_del(&map->list);
kfree(map);
}
if (window->rsrc.parent)
release_resource(&window->rsrc);
if (window->virt) {
iounmap(window->virt);
window->virt = NULL;
window->phys = 0;
window->size = 0;
window->pdev = NULL;
}
}
......@@ -74,35 +86,28 @@ static void amd76xrom_cleanup(struct amd76xrom_map_info *info)
static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct rom_window {
u32 start;
u32 size;
u8 segen_bits;
};
static struct rom_window rom_window[] = {
/*
* Need the 5MiB window for chips that have block lock/unlock
* registers located below 4MiB window.
*/
{ 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), },
{ 0xffc00000, 4*1024*1024, (1<<7), },
{ 0xffff0000, 64*1024, 0 },
{ 0 , 0, 0 },
};
static const u32 rom_probe_sizes[] = {
5*1024*1024, 4*1024*1024, 2*1024*1024, 1024*1024, 512*1024,
256*1024, 128*1024, 64*1024, 0};
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
u8 byte;
struct amd76xrom_map_info *info = &amd76xrom_map;
struct rom_window *window;
int i;
u32 rom_size;
struct amd76xrom_window *window = &amd76xrom_window;
struct amd76xrom_map_info *map = 0;
unsigned long map_top;
info->pdev = pdev;
window = &rom_window[0];
/* Remember the pci dev I find the window in */
window->pdev = pdev;
/* Assume the rom window is properly setup, and find it's size */
pci_read_config_byte(pdev, 0x43, &byte);
if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) {
window->phys = 0xffb00000; /* 5MiB */
}
else if ((byte & (1<<7)) == (1<<7)) {
window->phys = 0xffc00000; /* 4MiB */
}
else {
window->phys = 0xffff0000; /* 64KiB */
}
window->size = 0xffffffffUL - window->phys + 1UL;
while (window->size) {
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to a fragment of the window being
......@@ -110,22 +115,25 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
* request_mem_region() fails then once the rom size is
* discovered we will try to reserve the unreserved fragment.
*/
info->window_rsrc.name = MOD_NAME;
info->window_rsrc.start = window->start;
info->window_rsrc.end = window->start + window->size - 1;
info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &info->window_rsrc)) {
info->window_rsrc.parent = NULL;
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
window->rsrc.end = window->phys + window->size - 1;
window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &window->rsrc)) {
window->rsrc.parent = NULL;
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource"
" 0x%.08lx-0x%.08lx - kernel bug?\n",
__func__,
info->window_rsrc.start, info->window_rsrc.end);
window->rsrc.start, window->rsrc.end);
}
#if 0
/* Enable the selected rom window */
pci_read_config_byte(pdev, 0x43, &byte);
pci_write_config_byte(pdev, 0x43, byte | window->segen_bits);
pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits);
#endif
/* Enable writes through the rom window */
pci_read_config_byte(pdev, 0x40, &byte);
......@@ -133,108 +141,141 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
/* FIXME handle registers 0x80 - 0x8C the bios region locks */
printk(KERN_NOTICE MOD_NAME " window : %x at %x\n",
window->size, window->start);
/* For write accesses caches are useless */
info->window_addr = ioremap_nocache(window->start,
window->size);
if (!info->window_addr) {
printk(KERN_ERR "Failed to ioremap\n");
continue;
window->virt = ioremap_nocache(window->phys, window->size);
if (!window->virt) {
printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
window->phys, window->size);
goto out;
}
info->mtd = NULL;
for(i = 0; (rom_size = rom_probe_sizes[i]); i++) {
char **chip_type;
if (rom_size > window->size) {
continue;
/* Get the first address to look for an rom chip at */
map_top = window->phys;
#if 1
/* The probe sequence run over the firmware hub lock
* registers sets them to 0x7 (no access).
* Probe at most the last 4M of the address space.
*/
if (map_top < 0xffc00000) {
map_top = 0xffc00000;
}
info->map.phys = window->start + window->size - rom_size;
info->map.virt =
info->window_addr + window->size - rom_size;
info->map.size = rom_size;
simple_map_init(&info->map);
chip_type = rom_probe_types;
for(; !info->mtd && *chip_type; chip_type++) {
info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map);
#endif
/* Loop through and look for rom chips */
while((map_top - 1) < 0xffffffffUL) {
struct cfi_private *cfi;
unsigned long offset;
int i;
if (!map) {
map = kmalloc(sizeof(*map), GFP_KERNEL);
}
if (info->mtd) goto found_mtd;
if (!map) {
printk(KERN_ERR MOD_NAME ": kmalloc failed");
goto out;
}
iounmap(info->window_addr);
info->window_addr = NULL;
memset(map, 0, sizeof(*map));
INIT_LIST_HEAD(&map->list);
map->map.name = map->map_name;
map->map.phys = map_top;
offset = map_top - window->phys;
map->map.virt = (void __iomem *)
(((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08lx",
MOD_NAME, map->map.phys);
/* There is no generic VPP support */
for(map->map.bankwidth = 32; map->map.bankwidth;
map->map.bankwidth >>= 1)
{
char **probe_type;
/* Skip bankwidths that are not supported */
if (!map_bankwidth_supported(map->map.bankwidth))
continue;
/* Disable writes through the rom window */
pci_read_config_byte(pdev, 0x40, &byte);
pci_write_config_byte(pdev, 0x40, byte & ~1);
/* Setup the map methods */
simple_map_init(&map->map);
window++;
/* Try all of the probe methods */
probe_type = rom_probe_types;
for(; *probe_type; probe_type++) {
map->mtd = do_map_probe(*probe_type, &map->map);
if (map->mtd)
goto found;
}
goto failed;
found_mtd:
printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n",
window->size - rom_size);
info->mtd->owner = THIS_MODULE;
if (!info->window_rsrc.parent) {
/* failed to reserve entire window - try fragments */
info->window_rsrc.name = MOD_NAME;
info->window_rsrc.start = window->start;
info->window_rsrc.end = window->start + window->size - rom_size - 1;
info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &info->window_rsrc)) {
printk(KERN_ERR MOD_NAME
": cannot reserve window resource fragment\n");
#if 0
/*
* The BIOS e820 usually reserves this so it isn't
* usually an error.
*/
goto failed;
#endif
}
map_top += ROM_PROBE_STEP_SIZE;
continue;
found:
/* Trim the size if we are larger than the map */
if (map->mtd->size > map->map.size) {
printk(KERN_WARNING MOD_NAME
" rom(%u) larger than window(%lu). fixing...\n",
map->mtd->size, map->map.size);
map->mtd->size = map->map.size;
}
add_mtd_device(info->mtd);
info->window_start = window->start;
info->window_size = window->size;
if (info->window_rsrc.parent) {
if (window->rsrc.parent) {
/*
* Registering the MTD device in iomem may not be possible
* if there is a BIOS "reserved" and BUSY range. If this
* fails then continue anyway.
*/
snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
"mtd%d", info->mtd->index);
info->rom_rsrc.name = info->mtd_name;
info->rom_rsrc.start = window->start + window->size - rom_size;
info->rom_rsrc.end = window->start + window->size - 1;
info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
map->rsrc.name = map->map_name;
map->rsrc.start = map->map.phys;
map->rsrc.end = map->map.phys + map->mtd->size - 1;
map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&window->rsrc, &map->rsrc)) {
printk(KERN_ERR MOD_NAME
": cannot reserve MTD resource\n");
info->rom_rsrc.parent = NULL;
map->rsrc.parent = NULL;
}
}
return 0;
/* Make the whole region visible in the map */
map->map.virt = window->virt;
map->map.phys = window->phys;
cfi = map->map.fldrv_priv;
for(i = 0; i < cfi->numchips; i++) {
cfi->chips[i].start += offset;
}
/* Now that the mtd devices is complete claim and export it */
map->mtd->owner = THIS_MODULE;
if (add_mtd_device(map->mtd)) {
map_destroy(map->mtd);
map->mtd = 0;
goto out;
}
failed:
amd76xrom_cleanup(info);
/* Calculate the new value of map_top */
map_top += map->mtd->size;
/* File away the map structure */
list_add(&map->list, &window->maps);
map = 0;
}
out:
/* Free any left over map structures */
if (map) {
kfree(map);
}
/* See if I have any map structures */
if (list_empty(&window->maps)) {
amd76xrom_cleanup(window);
return -ENODEV;
}
return 0;
}
static void __devexit amd76xrom_remove_one (struct pci_dev *pdev)
{
struct amd76xrom_map_info *info = &amd76xrom_map;
struct amd76xrom_window *window = &amd76xrom_window;
amd76xrom_cleanup(info);
amd76xrom_cleanup(window);
}
static struct pci_device_id amd76xrom_pci_tbl[] = {
......@@ -269,7 +310,6 @@ int __init init_amd76xrom(void)
}
}
if (pdev) {
amd76xrom_map.pdev = pdev;
return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]);
}
return -ENXIO;
......@@ -280,7 +320,7 @@ int __init init_amd76xrom(void)
static void __exit cleanup_amd76xrom(void)
{
amd76xrom_remove_one(amd76xrom_map.pdev);
amd76xrom_remove_one(amd76xrom_window.pdev);
}
module_init(init_amd76xrom);
......
......@@ -2,7 +2,7 @@
* ichxrom.c
*
* Normal mappings of chips in physical memory
* $Id: ichxrom.c,v 1.8 2004/07/16 17:43:11 dwmw2 Exp $
* $Id: ichxrom.c,v 1.14 2004/09/18 01:59:56 eric Exp $
*/
#include <linux/module.h>
......@@ -12,187 +12,87 @@
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#include <linux/config.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/mtd/cfi.h>
#include <linux/list.h>
#define xstr(s) str(s)
#define str(s) #s
#define MOD_NAME xstr(KBUILD_BASENAME)
#define MTD_DEV_NAME_LENGTH 16
#define RESERVE_MEM_REGION 0
#define ADDRESS_NAME_LEN 18
#define MANUFACTURER_INTEL 0x0089
#define I82802AB 0x00ad
#define I82802AC 0x00ac
#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
#define ICHX_FWH_REGION_START 0xFF000000UL
#define ICHX_FWH_REGION_SIZE 0x01000000UL
#define BIOS_CNTL 0x4e
#define FWH_DEC_EN1 0xE3
#define FWH_DEC_EN2 0xF0
#define FWH_SEL1 0xE8
#define FWH_SEL2 0xEE
struct ichxrom_map_info {
struct map_info map;
struct mtd_info *mtd;
unsigned long window_addr;
struct ichxrom_window {
void __iomem* virt;
unsigned long phys;
unsigned long size;
struct list_head maps;
struct resource rsrc;
struct pci_dev *pdev;
struct resource window_rsrc;
struct resource rom_rsrc;
char mtd_name[MTD_DEV_NAME_LENGTH];
};
static inline unsigned long addr(struct map_info *map, unsigned long ofs)
{
unsigned long offset;
offset = ((8*1024*1024) - map->size) + ofs;
if (offset >= (4*1024*1024)) {
offset += 0x400000;
}
return map->map_priv_1 + 0x400000 + offset;
}
static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
{
return addr - map->map_priv_1 + ICHX_FWH_REGION_START;
}
static map_word ichxrom_read(struct map_info *map, unsigned long ofs)
{
map_word val;
int i;
switch(map->bankwidth) {
case 1: val.x[0] = __raw_readb(addr(map, ofs)); break;
case 2: val.x[0] = __raw_readw(addr(map, ofs)); break;
case 4: val.x[0] = __raw_readl(addr(map, ofs)); break;
#if BITS_PER_LONG >= 64
case 8: val.x[0] = __raw_readq(addr(map, ofs)); break;
#endif
default: val.x[0] = 0; break;
}
for(i = 1; i < map_words(map); i++) {
val.x[i] = 0;
}
return val;
}
static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
memcpy_fromio(to, addr(map, from), len);
}
static void ichxrom_write(struct map_info *map, map_word d, unsigned long ofs)
{
switch(map->bankwidth) {
case 1: __raw_writeb(d.x[0], addr(map,ofs)); break;
case 2: __raw_writew(d.x[0], addr(map,ofs)); break;
case 4: __raw_writel(d.x[0], addr(map,ofs)); break;
#if BITS_PER_LONG >= 64
case 8: __raw_writeq(d.x[0], addr(map,ofs)); break;
#endif
}
mb();
}
static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
memcpy_toio(addr(map, to), from, len);
}
static struct ichxrom_map_info ichxrom_map = {
.map = {
.name = MOD_NAME,
.phys = NO_XIP,
.size = 0,
.bankwidth = 1,
.read = ichxrom_read,
.copy_from = ichxrom_copy_from,
.write = ichxrom_write,
.copy_to = ichxrom_copy_to,
/* Firmware hubs only use vpp when being programmed
* in a factory setting. So in-place programming
* needs to use a different method.
*/
},
/* remaining fields of structure are initialized to 0 */
struct ichxrom_map_info {
struct list_head list;
struct map_info map;
struct mtd_info *mtd;
struct resource rsrc;
char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
};
enum fwh_lock_state {
FWH_DENY_WRITE = 1,
FWH_IMMUTABLE = 2,
FWH_DENY_READ = 4,
static struct ichxrom_window ichxrom_window = {
.maps = LIST_HEAD_INIT(ichxrom_window.maps),
};
static void ichxrom_cleanup(struct ichxrom_map_info *info)
static void ichxrom_cleanup(struct ichxrom_window *window)
{
struct ichxrom_map_info *map, *scratch;
u16 word;
/* Disable writes through the rom window */
pci_read_config_word(info->pdev, BIOS_CNTL, &word);
pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1);
if (info->mtd) {
del_mtd_device(info->mtd);
map_destroy(info->mtd);
info->mtd = NULL;
info->map.virt = 0;
pci_read_config_word(window->pdev, BIOS_CNTL, &word);
pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
/* Free all of the mtd devices */
list_for_each_entry_safe(map, scratch, &window->maps, list) {
if (map->rsrc.parent)
release_resource(&map->rsrc);
del_mtd_device(map->mtd);
map_destroy(map->mtd);
list_del(&map->list);
kfree(map);
}
if (info->rom_rsrc.parent)
release_resource(&info->rom_rsrc);
if (info->window_rsrc.parent)
release_resource(&info->window_rsrc);
if (info->window_addr) {
iounmap((void *)(info->window_addr));
info->window_addr = 0;
if (window->rsrc.parent)
release_resource(&window->rsrc);
if (window->virt) {
iounmap(window->virt);
window->virt = NULL;
window->phys = 0;
window->size = 0;
window->pdev = NULL;
}
}
static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
enum fwh_lock_state state)
{
struct map_info *map = mtd->priv;
unsigned long start = ofs;
unsigned long end = start + len -1;
/* FIXME do I need to guard against concurrency here? */
/* round down to 64K boundaries */
start = start & ~0xFFFF;
end = end & ~0xFFFF;
while (start <= end) {
unsigned long ctrl_addr;
ctrl_addr = addr(map, start) - 0x400000 + 2;
writeb(state, ctrl_addr);
start = start + 0x10000;
}
return 0;
}
static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
}
static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
return ichxrom_set_lock_state(mtd, ofs, len, 0);
}
static int __devinit ichxrom_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
struct ichxrom_window *window = &ichxrom_window;
struct ichxrom_map_info *map = 0;
unsigned long map_top;
u8 byte;
u16 word;
struct ichxrom_map_info *info = &ichxrom_map;
unsigned long map_size;
static char *probes[] = { "cfi_probe", "jedec_probe" };
struct cfi_private *cfi;
/* For now I just handle the ichx and I assume there
* are not a lot of resources up at the top of the address
......@@ -204,25 +104,55 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
* Also you can page firmware hubs if an 8MB window isn't enough
* but don't currently handle that case either.
*/
window->pdev = pdev;
/* Find a region continuous to the end of the ROM window */
window->phys = 0;
pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
if (byte == 0xff) {
window->phys = 0xffc00000;
pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
if ((byte & 0x0f) == 0x0f) {
window->phys = 0xff400000;
}
else if ((byte & 0x0e) == 0x0e) {
window->phys = 0xff500000;
}
else if ((byte & 0x0c) == 0x0c) {
window->phys = 0xff600000;
}
else if ((byte & 0x08) == 0x08) {
window->phys = 0xff700000;
}
}
else if ((byte & 0xfe) == 0xfe) {
window->phys = 0xffc80000;
}
else if ((byte & 0xfc) == 0xfc) {
window->phys = 0xffd00000;
}
else if ((byte & 0xf8) == 0xf8) {
window->phys = 0xffd80000;
}
else if ((byte & 0xf0) == 0xf0) {
window->phys = 0xffe00000;
}
else if ((byte & 0xe0) == 0xe0) {
window->phys = 0xffe80000;
}
else if ((byte & 0xc0) == 0xc0) {
window->phys = 0xfff00000;
}
else if ((byte & 0x80) == 0x80) {
window->phys = 0xfff80000;
}
info->pdev = pdev;
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to the window being "reseved" by the BIOS.
*/
info->window_rsrc.name = MOD_NAME;
info->window_rsrc.start = ICHX_FWH_REGION_START;
info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1;
info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &info->window_rsrc)) {
info->window_rsrc.parent = NULL;
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource"
" 0x%.08lx-0x%.08lx - kernel bug?\n",
__func__,
info->window_rsrc.start, info->window_rsrc.end);
if (window->phys == 0) {
printk(KERN_ERR MOD_NAME ": Rom window is closed\n");
goto out;
}
window->phys -= 0x400000UL;
window->size = (0xffffffffUL - window->phys) + 1UL;
/* Enable writes through the rom window */
pci_read_config_word(pdev, BIOS_CNTL, &word);
......@@ -231,119 +161,167 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
* this device, so don't even try.
*/
printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
goto failed;
goto out;
}
pci_write_config_word(pdev, BIOS_CNTL, word | 1);
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to the window being "reseved" by the BIOS.
*/
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
window->rsrc.end = window->phys + window->size - 1;
window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &window->rsrc)) {
window->rsrc.parent = NULL;
printk(KERN_DEBUG MOD_NAME
": %s(): Unable to register resource"
" 0x%.08lx-0x%.08lx - kernel bug?\n",
__func__,
window->rsrc.start, window->rsrc.end);
}
/* Map the firmware hub into my address space. */
/* Does this use too much virtual address space? */
info->window_addr = (unsigned long)ioremap(
ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
if (!info->window_addr) {
printk(KERN_ERR "Failed to ioremap\n");
goto failed;
window->virt = ioremap_nocache(window->phys, window->size);
if (!window->virt) {
printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
window->phys, window->size);
goto out;
}
/* For now assume the firmware has setup all relevant firmware
* windows. We don't have enough information to handle this case
* intelligently.
*/
/* FIXME select the firmware hub and enable a window to it. */
info->mtd = NULL;
info->map.map_priv_1 = info->window_addr;
/* Loop through the possible bankwidths */
for(ichxrom_map.map.bankwidth = 4; ichxrom_map.map.bankwidth; ichxrom_map.map.bankwidth >>= 1) {
map_size = ICHX_FWH_REGION_SIZE;
while(!info->mtd && (map_size > 0)) {
int i;
info->map.size = map_size;
for(i = 0; i < sizeof(probes)/sizeof(char *); i++) {
info->mtd = do_map_probe(probes[i], &ichxrom_map.map);
if (info->mtd)
break;
/* Get the first address to look for an rom chip at */
map_top = window->phys;
if ((window->phys & 0x3fffff) != 0) {
map_top = window->phys + 0x400000;
}
map_size -= 512*1024;
#if 1
/* The probe sequence run over the firmware hub lock
* registers sets them to 0x7 (no access).
* Probe at most the last 4M of the address space.
*/
if (map_top < 0xffc00000) {
map_top = 0xffc00000;
}
if (info->mtd)
break;
#endif
/* Loop through and look for rom chips */
while((map_top - 1) < 0xffffffffUL) {
struct cfi_private *cfi;
unsigned long offset;
int i;
if (!map) {
map = kmalloc(sizeof(*map), GFP_KERNEL);
}
if (!info->mtd) {
goto failed;
if (!map) {
printk(KERN_ERR MOD_NAME ": kmalloc failed");
goto out;
}
cfi = ichxrom_map.map.fldrv_priv;
if ((cfi->mfr == MANUFACTURER_INTEL) && (
(cfi->id == I82802AB) ||
(cfi->id == I82802AC)))
{
/* If it is a firmware hub put in the special lock
* and unlock routines.
memset(map, 0, sizeof(*map));
INIT_LIST_HEAD(&map->list);
map->map.name = map->map_name;
map->map.phys = map_top;
offset = map_top - window->phys;
map->map.virt = (void __iomem *)
(((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08lx",
MOD_NAME, map->map.phys);
/* Firmware hubs only use vpp when being programmed
* in a factory setting. So in-place programming
* needs to use a different method.
*/
info->mtd->lock = ichxrom_lock;
info->mtd->unlock = ichxrom_unlock;
for(map->map.bankwidth = 32; map->map.bankwidth;
map->map.bankwidth >>= 1)
{
char **probe_type;
/* Skip bankwidths that are not supported */
if (!map_bankwidth_supported(map->map.bankwidth))
continue;
/* Setup the map methods */
simple_map_init(&map->map);
/* Try all of the probe methods */
probe_type = rom_probe_types;
for(; *probe_type; probe_type++) {
map->mtd = do_map_probe(*probe_type, &map->map);
if (map->mtd)
goto found;
}
if (info->mtd->size > info->map.size) {
printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%lu). fixing...\n",
info->mtd->size, info->map.size);
info->mtd->size = info->map.size;
}
info->mtd->owner = THIS_MODULE;
add_mtd_device(info->mtd);
if (info->window_rsrc.parent) {
map_top += ROM_PROBE_STEP_SIZE;
continue;
found:
/* Trim the size if we are larger than the map */
if (map->mtd->size > map->map.size) {
printk(KERN_WARNING MOD_NAME
" rom(%u) larger than window(%lu). fixing...\n",
map->mtd->size, map->map.size);
map->mtd->size = map->map.size;
}
if (window->rsrc.parent) {
/*
* Registering the MTD device in iomem may not be possible
* if there is a BIOS "reserved" and BUSY range. If this
* fails then continue anyway.
*/
snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
"mtd%d", info->mtd->index);
info->rom_rsrc.name = info->mtd_name;
info->rom_rsrc.start = ICHX_FWH_REGION_START
+ ICHX_FWH_REGION_SIZE - map_size;
info->rom_rsrc.end = ICHX_FWH_REGION_START
+ ICHX_FWH_REGION_SIZE;
info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
map->rsrc.name = map->map_name;
map->rsrc.start = map->map.phys;
map->rsrc.end = map->map.phys + map->mtd->size - 1;
map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&window->rsrc, &map->rsrc)) {
printk(KERN_ERR MOD_NAME
": cannot reserve MTD resource\n");
info->rom_rsrc.parent = NULL;
map->rsrc.parent = NULL;
}
}
return 0;
/* Make the whole region visible in the map */
map->map.virt = window->virt;
map->map.phys = window->phys;
cfi = map->map.fldrv_priv;
for(i = 0; i < cfi->numchips; i++) {
cfi->chips[i].start += offset;
}
failed:
ichxrom_cleanup(info);
return -ENODEV;
}
/* Now that the mtd devices is complete claim and export it */
map->mtd->owner = THIS_MODULE;
if (add_mtd_device(map->mtd)) {
map_destroy(map->mtd);
map->mtd = 0;
goto out;
}
static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
{
struct ichxrom_map_info *info = &ichxrom_map;
u16 word;
/* Calculate the new value of map_top */
map_top += map->mtd->size;
del_mtd_device(info->mtd);
map_destroy(info->mtd);
info->mtd = NULL;
info->map.map_priv_1 = 0;
/* File away the map structure */
list_add(&map->list, &window->maps);
map = 0;
}
iounmap((void *)(info->window_addr));
info->window_addr = 0;
out:
/* Free any left over map structures */
if (map) {
kfree(map);
}
/* See if I have any map structures */
if (list_empty(&window->maps)) {
ichxrom_cleanup(window);
return -ENODEV;
}
return 0;
}
/* Disable writes through the rom window */
pci_read_config_word(pdev, BIOS_CNTL, &word);
pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
#if RESERVE_MEM_REGION
release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
#endif
static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
{
struct ichxrom_window *window = &ichxrom_window;
ichxrom_cleanup(window);
}
static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
......@@ -371,7 +349,6 @@ static struct pci_driver ichxrom_driver = {
};
#endif
static struct pci_dev *mydev;
int __init init_ichxrom(void)
{
struct pci_dev *pdev;
......@@ -385,7 +362,6 @@ int __init init_ichxrom(void)
}
}
if (pdev) {
mydev = pdev;
return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
}
return -ENXIO;
......@@ -396,7 +372,7 @@ int __init init_ichxrom(void)
static void __exit cleanup_ichxrom(void)
{
ichxrom_remove_one(mydev);
ichxrom_remove_one(ichxrom_window.pdev);
}
module_init(init_ichxrom);
......
/* Common Flash Interface structures
* See http://support.intel.com/design/flash/technote/index.htm
* $Id: cfi.h,v 1.45 2004/07/20 02:44:27 dwmw2 Exp $
* $Id: cfi.h,v 1.48 2004/10/20 23:08:05 dwmw2 Exp $
*/
#ifndef __MTD_CFI_H__
......@@ -177,16 +177,19 @@ struct cfi_bri_query {
uint32_t ConfField[1]; /* Not host ordered */
} __attribute__((packed));
#define P_ID_NONE 0
#define P_ID_INTEL_EXT 1
#define P_ID_AMD_STD 2
#define P_ID_INTEL_STD 3
#define P_ID_AMD_EXT 4
#define P_ID_ST_ADV 32
#define P_ID_MITSUBISHI_STD 256
#define P_ID_MITSUBISHI_EXT 257
#define P_ID_SST_PAGE 258
#define P_ID_RESERVED 65535
#define P_ID_NONE 0x0000
#define P_ID_INTEL_EXT 0x0001
#define P_ID_AMD_STD 0x0002
#define P_ID_INTEL_STD 0x0003
#define P_ID_AMD_EXT 0x0004
#define P_ID_WINBOND 0x0006
#define P_ID_ST_ADV 0x0020
#define P_ID_MITSUBISHI_STD 0x0100
#define P_ID_MITSUBISHI_EXT 0x0101
#define P_ID_SST_PAGE 0x0102
#define P_ID_INTEL_PERFORMANCE 0x0200
#define P_ID_INTEL_DATA 0x0210
#define P_ID_RESERVED 0xffff
#define CFI_MODE_CFI 1
......@@ -350,17 +353,26 @@ static inline void cfi_spin_unlock(spinlock_t *mutex)
struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
const char* name);
struct cfi_fixup {
uint16_t mfr;
uint16_t id;
void (*fixup)(struct map_info *map, void* param);
void (*fixup)(struct mtd_info *mtd, void* param);
void* param;
};
#define CFI_MFR_ANY 0xffff
#define CFI_ID_ANY 0xffff
void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups);
#define CFI_MFR_AMD 0x0001
#define CFI_MFR_ST 0x0020 /* STMicroelectronics */
void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk);
int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
loff_t ofs, size_t len, void *thunk);
#endif /* __MTD_CFI_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment