X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmtd%2Fchips%2Fcfi_util.c;h=2b2ede2bfccaeca3f9e0e47f2869d501b1633abf;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=d1a785628c496d2498e391e88c7a50f87997decb;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index d1a785628..2b2ede2bf 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -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.8 2004/12/14 19:55:56 nico Exp $ * */ @@ -22,12 +22,14 @@ #include #include #include +#include +#include #include #include #include struct cfi_extquery * -cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) +__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) { struct cfi_private *cfi = map->fldrv_priv; __u32 base = 0; // cfi->chips[0].start; @@ -39,21 +41,35 @@ cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) if (!adr) goto out; - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - extp = kmalloc(size, GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); goto out; } - + +#ifdef CONFIG_MTD_XIP + local_irq_disable(); +#endif + + /* Switch it into Query Mode */ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + /* Read in the Extended Query Table */ for (i=0; idevice_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); + +#ifdef CONFIG_MTD_XIP + (void) map_read(map, base); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); +#endif + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { printk(KERN_WARNING " Unknown %s Extended Query " @@ -61,32 +77,120 @@ cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) extp->MinorVersion); kfree(extp); extp = NULL; - goto out; } -out: - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); - - return extp; + out: return extp; } 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 (inumeraseregions && (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) { + int size = regions[i].erasesize; + + ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); + + if (ret) + return ret; + + adr += size; + ofs += size; + len -= size; + + if (ofs == regions[i].offset + size * regions[i].numblocks) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + return 0; +} + +EXPORT_SYMBOL(cfi_varsize_frob); + MODULE_LICENSE("GPL");