Exemple #1
0
/*
 * Erase an address range on the nor chip.  The address range may extend
 * one or more erase sectors.  Return an error is there is a problem erasing.
 */
static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct spi_nor *nor = mtd_to_spi_nor(mtd);
	u32 addr, len;
	uint32_t rem;
	int ret;

	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
			(long long)instr->len);

	div_u64_rem(instr->len, mtd->erasesize, &rem);
	if (rem)
		return -EINVAL;

	addr = instr->addr;
	len = instr->len;

	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE);
	if (ret)
		return ret;

	/* whole-chip erase? */
	if (len == mtd->size) {
		if (erase_chip(nor)) {
			ret = -EIO;
			goto erase_err;
		}

	/* REVISIT in some cases we could speed up erasing large regions
	 * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K.  We may have set up
	 * to use "small sector erase", but that's not always optimal.
	 */

	/* "sector"-at-a-time erase */
	} else {
		while (len) {
			if (nor->erase(nor, addr)) {
				ret = -EIO;
				goto erase_err;
			}

			addr += mtd->erasesize;
			len -= mtd->erasesize;
		}
	}

	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return ret;

erase_err:
	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
	instr->state = MTD_ERASE_FAILED;
	return ret;
}
Exemple #2
0
static int sflash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct map_info *map = mtd->priv;
	MV_SFLASH_INFO *sflash = map->fldrv_priv;
/*	MV_SFLASH_INFO *sflash = mtd->priv;*/
	MV_U32 fsec, lsec;
	int i;
	MV_ULONG flags = 0, sflash_in_irq = 0;

	DB(printk("\nINFO: %s - Addr %08x, len %d",__FUNCTION__, instr->addr, instr->len));
	
	if(instr->addr & (mtd->erasesize - 1))
	{
		printk(KERN_NOTICE "\nError: %s - Erase address not sector alligned",__FUNCTION__);
		return -EINVAL;
	}
	if(instr->len & (mtd->erasesize - 1))
	{
		printk(KERN_NOTICE "\nError: %s - Erase length is not sector alligned",__FUNCTION__);
		return -EINVAL;
	}
	if(instr->len + instr->addr > mtd->size)
	{
		printk(KERN_NOTICE "\nError: %s - Erase exceeded flash size",__FUNCTION__);
		return -EINVAL;
	}

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
	fsec = (instr->addr / mtd->erasesize);
	lsec = (fsec +(instr->len / mtd->erasesize));
#else
	fsec = instr->addr;
	do_div(fsec, mtd->erasesize);
	lsec = instr->len;
	do_div(lsec, mtd->erasesize);
	lsec = (fsec + lsec);
#endif

	DB(printk("\nINFO: %s - from sector %u to %u",__FUNCTION__, fsec, 
		  lsec-1));
	
	sflash_disable_irqs(flags, sflash_in_irq);
	for (i=fsec; i<lsec; i++)
	{
		if (mvSFlashSectorErase(sflash, i) != MV_OK)
		{
			sflash_enable_irqs(flags, sflash_in_irq);
			printk(KERN_NOTICE "\nError: %s - mvSFlashSectorErase on sector %d",__FUNCTION__, i);
			return -1;
		}
	}
	sflash_enable_irqs(flags, sflash_in_irq);
	
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}
Exemple #3
0
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	if (check_offs_len(mtd, instr->addr, instr->len))
		return -EINVAL;
	memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);
	return 0;
}
Exemple #4
0
/*
 * Erase an address range on the flash chip.  The address range may extend
 * one or more erase sectors.  Return an error is there is a problem erasing.
 */
static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct m25p *flash = mtd_to_m25p(mtd);
	u32 addr,len;

	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",
			flash->spi->dev.bus_id, __func__, "at",
			(u32)instr->addr, instr->len);

	/* sanity checks */
	if (instr->addr + instr->len > flash->mtd.size)
		return -EINVAL;
	if ((instr->addr % mtd->erasesize) != 0
			|| (instr->len % mtd->erasesize) != 0) {
		return -EINVAL;
	}

	addr = instr->addr;
	len = instr->len;

	mutex_lock(&flash->lock);

	/* whole-chip erase? */
	if (len == flash->mtd.size && erase_chip(flash)) {
		instr->state = MTD_ERASE_FAILED;
		mutex_unlock(&flash->lock);
		return -EIO;

	/* REVISIT in some cases we could speed up erasing large regions
	 * by using OPCODE_SE instead of OPCODE_BE_4K.  We may have set up
	 * to use "small sector erase", but that's not always optimal.
	 */

	/* "sector"-at-a-time erase */
	} else {
		while (len) {
			if (erase_sector(flash, addr)) {
				instr->state = MTD_ERASE_FAILED;
				mutex_unlock(&flash->lock);
				return -EIO;
			}

			addr += mtd->erasesize;
			len -= mtd->erasesize;
		}
	}

	mutex_unlock(&flash->lock);

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}
/*******************************************************************
 * mtd stuff
 ******************************************************************/
static int bcmmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
    uint32_t rem;
    u32 addr,len;
    int partition, offset;
	int ret;
	BlockCache *bc;

    /* sanity czech */
    if (instr->addr + instr->len > mtd->size)
		return -EINVAL;

    div_u64_rem(instr->len, mtd->erasesize, &rem);

    if (rem)
		return -EINVAL;

    addr = instr->addr;
    len = instr->len;

#if DEBUG_VFLASH_MTD
    printk("-->%s addr = %08x len = %08x\n", __func__, (unsigned int)addr, (unsigned int)len);
#endif
    /* whole-chip erase is not supported */
    if (len == mtd->size) 
    {
        instr->state = MTD_ERASE_FAILED;
        return -EIO;
    }
    else 
    { 
        while (len) 
        {
			bc = find_block_cache(addr);
			if (bc)
			{
				invalidate_block_cache(bc);
				release_block_cache(bc);
			}
			partition = get_flash_partition(addr);
			offset = addr - flash_partition_ptr[partition].offset;
			ret = vflash_erase_block(partition, offset, mtd->erasesize);
			if (ret < 0)
				return ret;
    	    addr += mtd->erasesize;
			len -= mtd->erasesize;
		}
    }

    instr->state = MTD_ERASE_DONE;
    mtd_erase_callback(instr);

    return 0;
}
Exemple #6
0
/* erase a specified part of the device */
static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct blkmtd_dev *dev = mtd->priv;
	struct mtd_erase_region_info *einfo = mtd->eraseregions;
	int numregions = mtd->numeraseregions;
	size_t from;
	u_long len;
	int err = -EIO;
	size_t retlen;

	instr->state = MTD_ERASING;
	from = instr->addr;
	len = instr->len;

	/* check erase region has valid start and length */
	DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n",
	      mtd->name+9, from, len);
	while(numregions) {
		DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n",
		      einfo->offset, einfo->erasesize, einfo->numblocks);
		if(from >= einfo->offset
		   && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) {
			if(len == einfo->erasesize
			   && ( (from - einfo->offset) % einfo->erasesize == 0))
				break;
		}
		numregions--;
		einfo++;
	}

	if(!numregions) {
		/* Not a valid erase block */
		err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from);
		instr->state = MTD_ERASE_FAILED;
		err = -EIO;
	}

	if(instr->state != MTD_ERASE_FAILED) {
		/* do the erase */
		DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len);
		err = write_pages(dev, NULL, from, len, &retlen);
		if(err || retlen != len) {
			err("erase failed err = %d", err);
			instr->state = MTD_ERASE_FAILED;
		} else {
			instr->state = MTD_ERASE_DONE;
		}
	}

	DEBUG(3, "blkmtd: erase: checking callback\n");
	mtd_erase_callback(instr);
	DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err);
	return err;
}
Exemple #7
0
static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	slram_priv_t *priv = mtd->priv;

	memset(priv->start + instr->addr, 0xff, instr->len);
	/* This'll catch a few races. Free the thing before returning :)
	 * I don't feel at all ashamed. This kind of thing is possible anyway
	 * with flash, but unlikely.
	 */
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);
	return(0);
}
static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
{
	struct map_info *map = mtd->priv;
	map_word allff;
	unsigned long i;

	allff = map_word_ff(map);
	for (i=0; i<instr->len; i += map_bankwidth(map))
		map_write(map, allff, instr->addr + i);
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);
	return 0;
}
Exemple #9
0
 /*
 * Erase is an asynchronous operation.  Device drivers are supposed
 * to call instr->callback() whenever the operation completes, even
 * if it completes with a failure.
 * Callers are supposed to pass a callback function and wait for it
 * to be called before writing to the block.
 */
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr)
		return -EINVAL;
	if (!(mtd->flags & MTD_WRITEABLE))
		return -EROFS;
	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
	if (!instr->len) {
		instr->state = MTD_ERASE_DONE;
		mtd_erase_callback(instr);
		return 0;
	}
	return mtd->_erase(mtd, instr);
}
Exemple #10
0
/**
 * gluebi_erase - erase operation of emulated MTD devices.
 * @mtd: the MTD device description object
 * @instr: the erase operation description
 *
 * This function calls the erase callback when finishes. Returns zero in case
 * of success and a negative error code in case of failure.
 */
static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	int err, i, lnum, count;
	struct ubi_volume *vol;
	struct ubi_device *ubi;

	dbg_gen("erase %llu bytes at offset %llu", (unsigned long long)instr->len,
		 (unsigned long long)instr->addr);

	if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
		return -EINVAL;

	if (instr->len < 0 || instr->addr + instr->len > mtd->size)
		return -EINVAL;

	if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd))
		return -EINVAL;

	lnum = mtd_div_by_eb(instr->addr, mtd);
	count = mtd_div_by_eb(instr->len, mtd);

	vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
	ubi = vol->ubi;

	if (ubi->ro_mode)
		return -EROFS;

	for (i = 0; i < count; i++) {
		err = ubi_eba_unmap_leb(ubi, vol, lnum + i);
		if (err)
			goto out_err;
	}

	/*
	 * MTD erase operations are synchronous, so we have to make sure the
	 * physical eraseblock is wiped out.
	 */
	err = ubi_wl_flush(ubi);
	if (err)
		goto out_err;

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);
	return 0;

out_err:
	instr->state = MTD_ERASE_FAILED;
	instr->fail_addr = (long long)lnum * mtd->erasesize;
	return err;
}
Exemple #11
0
/**
 * gluebi_erase - erase operation of emulated MTD devices.
 * @mtd: the MTD device description object
 * @instr: the erase operation description
 *
 * This function calls the erase callback when finishes. Returns zero in case
 * of success and a negative error code in case of failure.
 */
static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	int err, i, lnum, count;
	struct ubi_volume *vol;
	struct ubi_device *ubi;

	dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr);

	if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
		return -EINVAL;

	if (instr->len < 0 || instr->addr + instr->len > mtd->size)
		return -EINVAL;

	if (instr->addr % mtd->writesize || instr->len % mtd->writesize)
		return -EINVAL;

	lnum = instr->addr / mtd->erasesize;
	count = instr->len / mtd->erasesize;

	vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
	ubi = vol->ubi;

	if (ubi->ro_mode)
		return -EROFS;

	for (i = 0; i < count; i++) {
		err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i);
		if (err)
			goto out_err;
	}

	/*
	 * MTD erase operations are synchronous, so we have to make sure the
	 * physical eraseblock is wiped out.
	 */
	err = ubi_wl_flush(ubi);
	if (err)
		goto out_err;

        instr->state = MTD_ERASE_DONE;
        mtd_erase_callback(instr);
	return 0;

out_err:
	instr->state = MTD_ERASE_FAILED;
	instr->fail_addr = lnum * mtd->erasesize;
	return err;
}
Exemple #12
0
static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct ps3vram_priv *priv = mtd->priv;

	if (instr->addr + instr->len > mtd->size)
		return -EINVAL;

	/* Set bytes to 0xFF */
	memset(priv->base + instr->addr, 0xFF, instr->len);

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}
Exemple #13
0
static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
{
	/* Yeah, it's inefficient. Who cares? It's faster than a _real_
	   flash erase. */
	struct map_info *map = mtd->priv;
	map_word allff;
	unsigned long i;

	allff = map_word_ff(map);
	for (i=0; i<instr->len; i += map_bankwidth(map))
		map_write(map, allff, instr->addr + i);
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);
	return 0;
}
Exemple #14
0
static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
	struct efx_nic *efx = mtd->priv;
	int rc;

	rc = efx->type->mtd_erase(mtd, erase->addr, erase->len);
	if (rc == 0) {
		erase->state = MTD_ERASE_DONE;
	} else {
		erase->state = MTD_ERASE_FAILED;
		erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
	}
	mtd_erase_callback(erase);
	return rc;
}
static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
	struct efx_mtd *efx_mtd = mtd->priv;
	int rc;

	rc = efx_mtd->ops->erase(mtd, erase->addr, erase->len);
	if (rc == 0) {
		erase->state = MTD_ERASE_DONE;
	} else {
		erase->state = MTD_ERASE_FAILED;
		erase->fail_addr = 0xffffffff;
	}
	mtd_erase_callback(erase);
	return rc;
}
Exemple #16
0
static int
ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
    DEBUG(MTD_DEBUG_LEVEL2, "ram_erase(pos:%ld, len:%ld)\n", (long)instr->addr, (long)instr->len);
    if (instr->addr + instr->len > mtd->size) {
        DEBUG(MTD_DEBUG_LEVEL1, "ram_erase() out of bounds (%ld > %ld)\n", (long)(instr->addr + instr->len), (long)mtd->size);
        return -EINVAL;
    }

    memset((char *)mtd->priv + instr->addr, 0xff, instr->len);

    instr->state = MTD_ERASE_DONE;
    mtd_erase_callback(instr);

    return 0;
}
/*
 * Erase an address range on the flash chip.  The address range may extend
 * one or more erase sectors.  Return an error is there is a problem erasing.
 */
static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct m25p *flash = mtd_to_m25p(mtd);
	u32 addr,len;

	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",
			flash->spi->dev.bus_id, __func__, "at",
			(u32)instr->addr, instr->len);

	/* sanity checks */
	if (instr->addr + instr->len > flash->mtd.size)
		return -EINVAL;
	if ((instr->addr % mtd->erasesize) != 0
			|| (instr->len % mtd->erasesize) != 0) {
		return -EINVAL;
	}

	addr = instr->addr;
	len = instr->len;

	mutex_lock(&flash->lock);

	/* REVISIT in some cases we could speed up erasing large regions
	 * by using OPCODE_SE instead of OPCODE_BE_4K
	 */

	/* now erase those sectors */
	while (len) {
		if (erase_sector(flash, addr)) {
			instr->state = MTD_ERASE_FAILED;
			mutex_unlock(&flash->lock);
			return -EIO;
		}

		addr += mtd->erasesize;
		len -= mtd->erasesize;
	}

	mutex_unlock(&flash->lock);

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}
Exemple #18
0
static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct sst25l_flash *flash = to_sst25l_flash(mtd);
	uint32_t addr, end;
	int err;

	/* Sanity checks */
	if (instr->addr + instr->len > flash->mtd.size)
		return -EINVAL;

	if ((uint32_t)instr->len % mtd->erasesize)
		return -EINVAL;

	if ((uint32_t)instr->addr % mtd->erasesize)
		return -EINVAL;

	addr = instr->addr;
	end = addr + instr->len;

	mutex_lock(&flash->lock);

	err = sst25l_wait_till_ready(flash);
	if (err) {
		mutex_unlock(&flash->lock);
		return err;
	}

	while (addr < end) {
		err = sst25l_erase_sector(flash, addr);
		if (err) {
			mutex_unlock(&flash->lock);
			instr->state = MTD_ERASE_FAILED;
			dev_err(&flash->spi->dev, "Erase failed\n");
			return err;
		}

		addr += mtd->erasesize;
	}

	mutex_unlock(&flash->lock);

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);
	return 0;
}
static int tile_srom_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct tile_srom_dev *dev = to_tile_srom_dev(mtd);
	u32 addr, end;

	/* Sanity checks */
	if (instr->addr + instr->len > mtd->size)
		return -EINVAL;

	if ((u32)instr->len & (mtd->erasesize - 1)) {
		pr_err("tile_srom: erase length invalid: erase size %#x, "
		       "length %#x\n", mtd->erasesize, (u32)instr->len);
		return -EINVAL;
	}

	if ((u32)instr->addr & (mtd->erasesize - 1)) {
		pr_err("tile_srom: erase addr invalid: erase size %#X, "
		       "addr %#x\n", mtd->erasesize, (u32)instr->addr);
		return -EINVAL;
	}

	addr = instr->addr;
	end = addr + instr->len;

	while (addr < end) {
		int hv_retval;
		char dummy = 0;

		hv_retval = hv_dev_pwrite(dev->hv_devhdl, 0,
					  (HV_VirtAddr)&dummy, sizeof(dummy),
					  SROM_ERASE_OFF | (u32) addr);
		if (hv_retval != sizeof(dummy)) {
			DEBUG(MTD_DEBUG_LEVEL1, "hv_dev_pwrite for erase"
			      " failed, error %d\n", hv_retval);
			return -EIO;
		}

		addr += mtd->erasesize;
	}

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);
	return 0;
}
Exemple #20
0
/**
 * @mtd: the device
 * @erase: the erase info
 * Returns 0 if erase successful or -ERRNO if an error occurred
 */
static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
{
	int rc;

	erase->state = MTD_ERASING;

	/* todo: register our own notifier to do a true async implementation */
	rc =  powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
			erase->len, NULL, NULL);

	if (rc) {
		erase->fail_addr = erase->addr;
		erase->state = MTD_ERASE_FAILED;
	} else {
		erase->state = MTD_ERASE_DONE;
	}
	mtd_erase_callback(erase);
	return rc;
}
Exemple #21
0
/*
	We could store these in the mtd structure, but we only support 1 device..
	static struct mtd_info *mtd_info;
*/
static int sf_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	int ret;
	struct wmt_sf_info_t *info = (struct wmt_sf_info_t *)mtd->priv;
	struct sfreg_t *sfreg = info->reg;

	REG32_VAL(PMCEU_ADDR) |= SF_CLOCK_EN;

	ret = spi_flash_sector_erase((unsigned long)instr->addr, sfreg);

	if (ret != ERR_OK) {
		printk(KERN_ERR "sf_erase() error at address 0x%lx \n", (unsigned long)instr->addr);
		return -EINVAL;
	}
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	REG32_VAL(PMCEU_ADDR) &= ~(SF_CLOCK_EN);

	return 0;
}
Exemple #22
0
static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct ps3vram_priv *priv = mtd->priv;

	if (instr->addr + instr->len > mtd->size)
		return -EINVAL;

	mutex_lock(&priv->lock);

	ps3vram_cache_flush(mtd);

	/* Set bytes to 0xFF */
	memset_io(priv->ddr_base + instr->addr, 0xFF, instr->len);

	mutex_unlock(&priv->lock);

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}
Exemple #23
0
static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	flash_info_t *fi = mtd->priv;
	size_t a_start = fi->start[0] + instr->addr;
	size_t a_end = a_start + instr->len;
	int s_first = -1;
	int s_last = -1;
	int error, sect;

	for (sect = 0; sect < fi->sector_count - 1; sect++) {
		if (a_start == fi->start[sect])
			s_first = sect;

		if (a_end == fi->start[sect + 1]) {
			s_last = sect;
			break;
		}
	}

	if (s_first >= 0 && s_first <= s_last) {
		instr->state = MTD_ERASING;

		flash_set_verbose(0);
		error = flash_erase(fi, s_first, s_last);
		flash_set_verbose(1);

		if (error) {
			instr->state = MTD_ERASE_FAILED;
			return -EIO;
		}

		instr->state = MTD_ERASE_DONE;
		mtd_erase_callback(instr);
		return 0;
	}

	return -EINVAL;
}
Exemple #24
0
static int
spiflash_erase (struct mtd_info *mtd,struct erase_info *instr)
{
	struct opcodes *ptr_opcode;
	u32 temp, reg;

   	/* sanity checks */
   	if (instr->addr + instr->len > mtd->size) return (-EINVAL);

	if (!spiflash_wait_ready(FL_ERASING))
		return -EINTR;

	spiflash_sendcmd(SPI_WRITE_ENABLE, 0);
	busy_wait((reg = spiflash_regread32(SPI_FLASH_CTL)) & SPI_CTL_BUSY, 0);
	reg = spiflash_regread32(SPI_FLASH_CTL);

	ptr_opcode = &stm_opcodes[SPI_SECTOR_ERASE];
	temp = ((__u32)instr->addr << 8) | (__u32)(ptr_opcode->code);
	spiflash_regwrite32(SPI_FLASH_OPCODE, temp);

	reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | ptr_opcode->tx_cnt | SPI_CTL_START;
	spiflash_regwrite32(SPI_FLASH_CTL, reg);

	/* this will take some time */
	spin_unlock_bh(&spidata->mutex);
	msleep(800);
	spin_lock_bh(&spidata->mutex);

	busy_wait(spiflash_sendcmd(SPI_RD_STATUS, 0) & SPI_STATUS_WIP, 20);
	spiflash_done();

   	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

   	return 0;
}
/*
 * Erase pages of flash.
 */
static int 
ifx_dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
    struct dataflash_t *priv = (struct dataflash_t *)mtd->priv;
    unsigned         blocksize = priv->page_size << 3;
    u8               *command;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
    uint32_t		rem;

    IFX_DATA_FLASH_PRINT("erase addr=0x%llx len %lld\n",
            (long long)instr->addr, (long long)instr->len);
#else
    IFX_DATA_FLASH_PRINT("erase addr=0x%x len 0x%x\n",
            instr->addr, instr->len);
#endif
	/* Sanity checks */
    if (instr->addr + instr->len > mtd->size)
        return -EINVAL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
    div_u64_rem(instr->len, priv->page_size, &rem);
    if (rem)
        return -EINVAL;
    div_u64_rem(instr->addr, priv->page_size, &rem);
    if (rem)
        return -EINVAL;
#else
    if (((instr->len % priv->page_size) != 0) 
        || ((instr->addr % priv->page_size) != 0)) {
        return -EINVAL;
    }
#endif

    command = priv->command;
    down(&priv->lock);
    while (instr->len > 0) {
        unsigned int    pageaddr;
        int     do_block;

        /* Calculate flash page address; use block erase (for speed) if
         * we're at a block boundary and need to erase the whole block.
         */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
        pageaddr = div_u64(instr->addr, priv->page_size);
#else
        pageaddr = instr->addr / priv->page_size;
#endif 
        do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize;
        pageaddr = pageaddr << priv->page_offset;

        command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
        command[1] = (u8)(pageaddr >> 16);
        command[2] = (u8)(pageaddr >> 8);
        command[3] = 0;

        IFX_DATA_FLASH_PRINT("ERASE %s: (%x) %x %x %x [%i]\n",
            do_block ? "block" : "page",
            command[0], command[1], command[2], command[3],
            pageaddr);

        ifx_dataflash_session(priv->spi, command, 4, 0, NULL, 0, NULL, 0);
        if (do_block) {
            instr->addr += blocksize;
            instr->len -= blocksize;
        } else {
            instr->addr += priv->page_size;
            instr->len -= priv->page_size;
        }
    }
    up(&priv->lock);
    /* Inform MTD subsystem that erase is complete */
    instr->state = MTD_ERASE_DONE;
    mtd_erase_callback(instr);
    return 0;
}
Exemple #26
0
static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
{
   __u32 addr,len;
   int i,first;

#ifdef LART_DEBUG
   printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n",__FUNCTION__,instr->addr,instr->len);
#endif

   /* sanity checks */
   if (instr->addr + instr->len > mtd->size) return (-EINVAL);

   /*
	* check that both start and end of the requested erase are
	* aligned with the erasesize at the appropriate addresses.
	*
	* 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.
	*/
   for (i = 0; i < mtd->numeraseregions && instr->addr >= mtd->eraseregions[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 (instr->addr & (mtd->eraseregions[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.
	*
	* as before, drop back one to point at the region in which
	* the address actually falls
	*/
   for (; i < mtd->numeraseregions && instr->addr + instr->len >= mtd->eraseregions[i].offset; i++) ;
   i--;

   /* is the end aligned on a block boundary? */
   if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL);

   addr = instr->addr;
   len = instr->len;

   i = first;

   /* now erase those blocks */
   while (len)
	 {
		if (!erase_block (addr))
		  {
			 instr->state = MTD_ERASE_FAILED;
			 return (-EIO);
		  }

		addr += mtd->eraseregions[i].erasesize;
		len -= mtd->eraseregions[i].erasesize;

		if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++;
	 }

   instr->state = MTD_ERASE_DONE;
   mtd_erase_callback(instr);

   return (0);
}
/*
 * Erase pages of flash.
 */
static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct dataflash	*priv = mtd->priv;
	struct spi_device	*spi = priv->spi;
	struct spi_transfer	x = { .tx_dma = 0, };
	struct spi_message	msg;
	unsigned		blocksize = priv->page_size << 3;
	uint8_t			*command;
	uint32_t		rem;

	pr_debug("%s: erase addr=0x%llx len 0x%llx\n",
	      dev_name(&spi->dev), (long long)instr->addr,
	      (long long)instr->len);

	div_u64_rem(instr->len, priv->page_size, &rem);
	if (rem)
		return -EINVAL;
	div_u64_rem(instr->addr, priv->page_size, &rem);
	if (rem)
		return -EINVAL;

	spi_message_init(&msg);

	x.tx_buf = command = priv->command;
	x.len = 4;
	spi_message_add_tail(&x, &msg);

	mutex_lock(&priv->lock);
	while (instr->len > 0) {
		unsigned int	pageaddr;
		int		status;
		int		do_block;

		/* Calculate flash page address; use block erase (for speed) if
		 * we're at a block boundary and need to erase the whole block.
		 */
		pageaddr = div_u64(instr->addr, priv->page_size);
		do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize;
		pageaddr = pageaddr << priv->page_offset;

		command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
		command[1] = (uint8_t)(pageaddr >> 16);
		command[2] = (uint8_t)(pageaddr >> 8);
		command[3] = 0;

		pr_debug("ERASE %s: (%x) %x %x %x [%i]\n",
			do_block ? "block" : "page",
			command[0], command[1], command[2], command[3],
			pageaddr);

		status = spi_sync(spi, &msg);
		(void) dataflash_waitready(spi);

		if (status < 0) {
			printk(KERN_ERR "%s: erase %x, err %d\n",
				dev_name(&spi->dev), pageaddr, status);
			/* REVISIT:  can retry instr->retries times; or
			 * giveup and instr->fail_addr = instr->addr;
			 */
			continue;
		}

		if (do_block) {
			instr->addr += blocksize;
			instr->len -= blocksize;
		} else {
			instr->addr += priv->page_size;
			instr->len -= priv->page_size;
		}
	}
	mutex_unlock(&priv->lock);

	/* Inform MTD subsystem that erase is complete */
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}

/*
 * Read from the DataFlash device.
 *   from   : Start offset in flash device
 *   len    : Amount to read
 *   retlen : About of data actually read
 *   buf    : Buffer containing the data
 */
static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
			       size_t *retlen, u_char *buf)
{
	struct dataflash	*priv = mtd->priv;
	struct spi_transfer	x[2] = { { .tx_dma = 0, }, };
	struct spi_message	msg;
	unsigned int		addr;
	uint8_t			*command;
	int			status;

	pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev),
			(unsigned)from, (unsigned)(from + len));

	/* Calculate flash page/byte address */
	addr = (((unsigned)from / priv->page_size) << priv->page_offset)
		+ ((unsigned)from % priv->page_size);

	command = priv->command;

	pr_debug("READ: (%x) %x %x %x\n",
		command[0], command[1], command[2], command[3]);

	spi_message_init(&msg);

	x[0].tx_buf = command;
	x[0].len = 8;
	spi_message_add_tail(&x[0], &msg);

	x[1].rx_buf = buf;
	x[1].len = len;
	spi_message_add_tail(&x[1], &msg);

	mutex_lock(&priv->lock);

	/* Continuous read, max clock = f(car) which may be less than
	 * the peak rate available.  Some chips support commands with
	 * fewer "don't care" bytes.  Both buffers stay unchanged.
	 */
	command[0] = OP_READ_CONTINUOUS;
	command[1] = (uint8_t)(addr >> 16);
	command[2] = (uint8_t)(addr >> 8);
	command[3] = (uint8_t)(addr >> 0);
	/* plus 4 "don't care" bytes */

	status = spi_sync(priv->spi, &msg);
	mutex_unlock(&priv->lock);

	if (status >= 0) {
		*retlen = msg.actual_length - 8;
		status = 0;
	} else
		pr_debug("%s: read %x..%x --> %d\n",
			dev_name(&priv->spi->dev),
			(unsigned)from, (unsigned)(from + len),
			status);
	return status;
}
/*
 * Erase an address range on the flash chip.  The address range may extend
 * one or more erase sectors.  Return an error is there is a problem erasing.
 */
static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct m25p *flash = mtd_to_m25p(mtd);
	u32 addr,len;
	uint64_t tmpdiv;
	int rem, rem1;

	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",
			flash->spi->dev.bus_id, __FUNCTION__, "at",
			(u32)instr->addr, instr->len);

	/* sanity checks */
	if (instr->addr + instr->len > device_size(&(flash->mtd)))
		return -EINVAL;
	tmpdiv = (uint64_t) instr->addr;
 	rem = do_div(tmpdiv, mtd->erasesize);
	tmpdiv = (uint64_t) instr->len;
	rem1 = do_div(tmpdiv, mtd->erasesize);
	if (rem != 0 || rem1 != 0) {
		return -EINVAL;
	}

	addr = instr->addr;
	len = instr->len;

	mutex_lock(&flash->lock);

	/* REVISIT in some cases we could speed up erasing large regions
	 * by using OPCODE_SE instead of OPCODE_BE_4K
	 */

	/* now erase those sectors */
	while (len) {
#ifdef CONFIG_MIPS_BRCM97XXX
		/* BSPI remaps each 4MB segment */
		if (erase_sector(flash, (addr + 0x400000) & 0xffffff)) {
#else
		if (erase_sector(flash, addr)) {
#endif
			instr->state = MTD_ERASE_FAILED;
			mutex_unlock(&flash->lock);
			return -EIO;
		}

		addr += mtd->erasesize;
		len -= mtd->erasesize;
	}

	mutex_unlock(&flash->lock);

	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}

/*
 * Read an address range from the flash chip.  The address range
 * may be any size provided it is within the physical boundaries.
 */
static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
	size_t *retlen, u_char *buf)
{
	struct m25p *flash = mtd_to_m25p(mtd);
	struct spi_transfer t[2];
	struct spi_message m;
	size_t total_len = len;

	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
			flash->spi->dev.bus_id, __FUNCTION__, "from",
			(u32)from, len);

	/* sanity checks */
	if (!len)
		return 0;

	if (from + len > device_size(&(flash->mtd)))
		return -EINVAL;

	if (retlen)
		*retlen = 0;

	total_len = len;
	while(total_len) {
		len = total_len;

#if 0 //defined(BRCM_SPI_SS_WAR)
	/*
	 * For testing purposes only - read 12 bytes at a time:
	 *
	 * 3548a0 MSPI has a 12-byte limit (PR42350).
	 * MSPI emulated via BSPI has no such limit.
	 * In production BSPI is always used because it is much faster.
	 */
		if(len > 12)
			len = 12;
#endif
#ifdef CONFIG_MIPS_BRCM97XXX
		/* don't cross a 4MB boundary due to remapping */
		len = min(len, (0x400000 - ((u32)from & 0x3fffff)));
#endif
		spi_message_init(&m);
		memset(t, 0, (sizeof t));

		t[0].tx_buf = flash->command;
		t[0].len = sizeof(flash->command);
		spi_message_add_tail(&t[0], &m);

		t[1].rx_buf = buf;
		t[1].len = len;
		spi_message_add_tail(&t[1], &m);

		/* Byte count starts at zero. */

		mutex_lock(&flash->lock);

		/* Wait till previous write/erase is done. */
		if (wait_till_ready(flash)) {
			/* REVISIT status return?? */
			mutex_unlock(&flash->lock);
			return 1;
		}

		/* FIXME switch to OPCODE_FAST_READ.  It's required for higher
		 * clocks; and at this writing, every chip this driver handles
		 * supports that opcode.
		 */

		/* Set up the write data buffer. */
		flash->command[0] = OPCODE_READ;
#ifdef CONFIG_MIPS_BRCM97XXX
		/* BSPI remaps each 4MB segment */
		flash->command[1] = ((from >> 16) + 0x40) & 0xff;
#else
		flash->command[1] = from >> 16;
#endif
		flash->command[2] = from >> 8;
		flash->command[3] = from;

		spi_sync(flash->spi, &m);

		*retlen += m.actual_length - sizeof(flash->command);

		mutex_unlock(&flash->lock);

		from += len;
		buf += len;
		total_len -= len;
	}

	return 0;
}

/*
 * Write an address range to the flash chip.  Data must be written in
 * FLASH_PAGESIZE chunks.  The address range may be any size provided
 * it is within the physical boundaries.
 */
static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
	size_t *retlen, const u_char *buf)
{
	struct m25p *flash = mtd_to_m25p(mtd);
	u32 page_offset, page_size;
	struct spi_transfer t[2];
	struct spi_message m;

	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
			flash->spi->dev.bus_id, __FUNCTION__, "to",
			(u32)to, len);

	if (retlen)
		*retlen = 0;

	/* sanity checks */
	if (!len)
		return(0);

	if (to + len > device_size(&(flash->mtd)))
		return -EINVAL;
#ifdef BRCM_SPI_SS_WAR
	if(len > 12)
		return -EIO;
#endif

	spi_message_init(&m);
	memset(t, 0, (sizeof t));

	t[0].tx_buf = flash->command;
	t[0].len = sizeof(flash->command);
	spi_message_add_tail(&t[0], &m);

	t[1].tx_buf = buf;
	spi_message_add_tail(&t[1], &m);

	mutex_lock(&flash->lock);

	/* Wait until finished previous write command. */
	if (wait_till_ready(flash))
		return 1;

	write_enable(flash);

	/* Set up the opcode in the write buffer. */
	flash->command[0] = OPCODE_PP;
#ifdef CONFIG_MIPS_BRCM97XXX
	/* BSPI remaps each 4MB segment */
	flash->command[1] = ((to >> 16) + 0x40) & 0xff;
#else
	flash->command[1] = to >> 16;
#endif
	flash->command[2] = to >> 8;
	flash->command[3] = to;

	/* what page do we start with? */
	page_offset = to % FLASH_PAGESIZE;

	/* do all the bytes fit onto one page? */
	if (page_offset + len <= FLASH_PAGESIZE) {
		t[1].len = len;

		spi_sync(flash->spi, &m);

		*retlen = m.actual_length - sizeof(flash->command);
	} else {
		u32 i;

		/* the size of data remaining on the first page */
		page_size = FLASH_PAGESIZE - page_offset;

		t[1].len = page_size;
		spi_sync(flash->spi, &m);

		*retlen = m.actual_length - sizeof(flash->command);

		/* write everything in PAGESIZE chunks */
		for (i = page_size; i < len; i += page_size) {
			page_size = len - i;
			if (page_size > FLASH_PAGESIZE)
				page_size = FLASH_PAGESIZE;

			/* write the next page to flash */
#ifdef CONFIG_MIPS_BRCM97XXX
			/* BSPI remaps each 4MB segment */
			flash->command[1] = (((to + i) >> 16) + 0x40) & 0xff;
#else
			flash->command[1] = (to + i) >> 16;
#endif
			flash->command[2] = (to + i) >> 8;
			flash->command[3] = (to + i);

			t[1].tx_buf = buf + i;
			t[1].len = page_size;

			wait_till_ready(flash);

			write_enable(flash);

			spi_sync(flash->spi, &m);

			if (retlen)
				*retlen += m.actual_length
					- sizeof(flash->command);
		}
	}

	mutex_unlock(&flash->lock);

	return 0;
}


/****************************************************************************/

/*
 * SPI device driver setup and teardown
 */

struct flash_info {
	char		*name;

	/* JEDEC id zero means "no ID" (most older chips); otherwise it has
	 * a high byte of zero plus three data bytes: the manufacturer id,
	 * then a two byte device id.
	 */
	u32		jedec_id;

	/* The size listed here is what works with OPCODE_SE, which isn't
	 * necessarily called a "sector" by the vendor.
	 */
	unsigned	sector_size;
	u16		n_sectors;

	u16		flags;
#define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */
};


/* NOTE: double check command sets and memory organization when you add
 * more flash chips.  This current list focusses on newer chips, which
 * have been converging on command sets which including JEDEC ID.
 */
static struct flash_info __devinitdata m25p_data [] = {

	/* Atmel -- some are (confusingly) marketed as "DataFlash" */
	{ "at25fs010",  0x1f6601, 32 * 1024, 4, SECT_4K, },
	{ "at25fs040",  0x1f6604, 64 * 1024, 8, SECT_4K, },

	{ "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, },

	{ "at26f004",   0x1f0400, 64 * 1024, 8, SECT_4K, },
	{ "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, },
	{ "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, },
	{ "at26df321",  0x1f4701, 64 * 1024, 64, SECT_4K, },

	/* Spansion -- single (large) sector size only, at least
	 * for the chips listed here (without boot sectors).
	 */
	{ "s25sl004a", 0x010212, 64 * 1024, 8, },
	{ "s25sl008a", 0x010213, 64 * 1024, 16, },
	{ "s25sl016a", 0x010214, 64 * 1024, 32, },
	{ "s25sl032a", 0x010215, 64 * 1024, 64, },
	{ "s25sl064a", 0x010216, 64 * 1024, 128, },
#ifdef CONFIG_MIPS_BRCM97XXX
	{ "s25fl128p", 0x012018, 64 * 1024, 256, },
#endif

	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
	{ "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, },
	{ "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, },
	{ "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, },
	{ "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, },

	/* ST Microelectronics -- newer production may have feature updates */
	{ "m25p05",  0x202010,  32 * 1024, 2, },
	{ "m25p10",  0x202011,  32 * 1024, 4, },
	{ "m25p20",  0x202012,  64 * 1024, 4, },
	{ "m25p40",  0x202013,  64 * 1024, 8, },
#ifndef CONFIG_MIPS_BRCM97XXX
	/* ID 0 is detected when there's nothing on the bus */
	{ "m25p80",         0,  64 * 1024, 16, },
#endif
	{ "m25p16",  0x202015,  64 * 1024, 32, },
	{ "m25p32",  0x202016,  64 * 1024, 64, },
	{ "m25p64",  0x202017,  64 * 1024, 128, },
	{ "m25p128", 0x202018, 256 * 1024, 64, },

	{ "m45pe80", 0x204014,  64 * 1024, 16, },
	{ "m45pe16", 0x204015,  64 * 1024, 32, },

	{ "m25pe80", 0x208014,  64 * 1024, 16, },
	{ "m25pe16", 0x208015,  64 * 1024, 32, SECT_4K, },

	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
	{ "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, },
	{ "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, },
	{ "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, },
	{ "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, },
	{ "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, },
	{ "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, },
	{ "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, },
};

static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
{
	int			tmp;
	u8			code = OPCODE_RDID;
	u8			id[3];
	u32			jedec;
	struct flash_info	*info;

	/* JEDEC also defines an optional "extended device information"
	 * string for after vendor-specific data, after the three bytes
	 * we use here.  Supporting some chips might require using it.
	 */
	tmp = spi_write_then_read(spi, &code, 1, id, 3);
	if (tmp < 0) {
		DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
			spi->dev.bus_id, tmp);
		return NULL;
	}
	jedec = id[0];
	jedec = jedec << 8;
	jedec |= id[1];
	jedec = jedec << 8;
	jedec |= id[2];

	for (tmp = 0, info = m25p_data;
			tmp < ARRAY_SIZE(m25p_data);
			tmp++, info++) {
		if (info->jedec_id == jedec)
			return info;
	}
	dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
	return NULL;
}
/*
 * Erase pages of flash.
 */
static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct dataflash	*priv = mtd->priv;
	struct spi_device	*spi = priv->spi;
	struct spi_transfer	x;
	struct spi_message	msg;
	unsigned		blocksize = priv->page_size << 3;
	uint8_t			*command;
	uint32_t		rem;

	pr_debug("%s: erase addr=0x%llx len 0x%llx\n",
	      dev_name(&spi->dev), (long long)instr->addr,
	      (long long)instr->len);

	div_u64_rem(instr->len, priv->page_size, &rem);
	if (rem)
		return -EINVAL;
	div_u64_rem(instr->addr, priv->page_size, &rem);
	if (rem)
		return -EINVAL;

	spi_message_init(&msg);

	memset(&x, 0, sizeof(x));

	x.tx_buf = command = priv->command;
	x.len = 4;
	spi_message_add_tail(&x, &msg);

	while (instr->len > 0) {
		unsigned int	pageaddr;
		int		status;
		int		do_block;

		/* Calculate flash page address; use block erase (for speed) if
		 * we're at a block boundary and need to erase the whole block.
		 */
		pageaddr = div_u64(instr->addr, priv->page_size);
		do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize;
		pageaddr = pageaddr << priv->page_offset;

		command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
		command[1] = (uint8_t)(pageaddr >> 16);
		command[2] = (uint8_t)(pageaddr >> 8);
		command[3] = 0;

		pr_debug("ERASE %s: (%x) %x %x %x [%i]\n",
			do_block ? "block" : "page",
			command[0], command[1], command[2], command[3],
			pageaddr);

		status = spi_sync(spi, &msg);
		(void) dataflash_waitready(spi);

		if (status < 0) {
			printk(KERN_ERR "%s: erase %x, err %d\n",
				dev_name(&spi->dev), pageaddr, status);
			/* REVISIT:  can retry instr->retries times; or
			 * giveup and instr->fail_addr = instr->addr;
			 */
			continue;
		}

		if (do_block) {
			instr->addr += blocksize;
			instr->len -= blocksize;
		} else {
			instr->addr += priv->page_size;
			instr->len -= priv->page_size;
		}
	}

	/* Inform MTD subsystem that erase is complete */
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}