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
This diff is collapsed.
This diff is collapsed.
/*
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>
......@@ -243,12 +243,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;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* 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