- Fix mtd erase with mtdpart (Marek Behún)
- NXP fspi driver fixes (Kuldeep Singh)
This commit is contained in:
Tom Rini 2021-10-23 10:49:28 -04:00
commit 355d1e24f6
19 changed files with 159 additions and 108 deletions

View File

@ -186,9 +186,7 @@ next:
static int onenand_block_erase(u32 start, u32 size, int force)
{
struct onenand_chip *this = mtd->priv;
struct erase_info instr = {
.callback = NULL,
};
struct erase_info instr = {};
loff_t ofs;
int ret;
int blocksize = 1 << this->erase_shift;
@ -219,10 +217,7 @@ static int onenand_block_erase(u32 start, u32 size, int force)
static int onenand_block_test(u32 start, u32 size)
{
struct onenand_chip *this = mtd->priv;
struct erase_info instr = {
.callback = NULL,
.priv = 0,
};
struct erase_info instr = {};
int blocks;
loff_t ofs;

View File

@ -153,7 +153,6 @@ static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr)
putc('\n');
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
instr->state = MTD_ERASE_FAILED;
mtd_erase_callback(instr);
return -EIO;
}
flash = pdata->base + addr;
@ -177,7 +176,6 @@ static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr)
writel(stat, &regs->isr); /* clear isr */
instr->fail_addr = addr;
instr->state = MTD_ERASE_FAILED;
mtd_erase_callback(instr);
return -EIO;
}
if (flash_verbose)
@ -189,7 +187,6 @@ static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr)
addr += mtd->erasesize;
}
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}

View File

@ -58,7 +58,6 @@ static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
}
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}

View File

@ -338,14 +338,6 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
return -EINVAL;
}
static void concat_erase_callback(struct erase_info *instr)
{
/* Nothing to do here in U-Boot */
#ifndef __UBOOT__
wake_up((wait_queue_head_t *) instr->priv);
#endif
}
static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
{
int err;
@ -358,7 +350,6 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
init_waitqueue_head(&waitq);
erase->mtd = mtd;
erase->callback = concat_erase_callback;
erase->priv = (unsigned long) &waitq;
/*
@ -498,8 +489,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
if (err)
return err;
if (instr->callback)
instr->callback(instr);
return 0;
}

View File

@ -906,13 +906,6 @@ void __put_mtd_device(struct mtd_info *mtd)
}
EXPORT_SYMBOL_GPL(__put_mtd_device);
/*
* 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)
@ -922,7 +915,6 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
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);

View File

@ -446,27 +446,16 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
int ret;
instr->addr += mtd->offset;
ret = mtd->parent->_erase(mtd->parent, instr);
if (ret) {
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= mtd->offset;
instr->addr -= mtd->offset;
}
if (ret && instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= mtd->offset;
instr->addr -= mtd->offset;
return ret;
}
void mtd_erase_callback(struct erase_info *instr)
{
if (instr->mtd->_erase == part_erase) {
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= instr->mtd->offset;
instr->addr -= instr->mtd->offset;
}
if (instr->callback)
instr->callback(instr);
}
EXPORT_SYMBOL_GPL(mtd_erase_callback);
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
return mtd->parent->_lock(mtd->parent, ofs + mtd->offset, len);

View File

@ -3602,10 +3602,6 @@ erase_exit:
chip->select_chip(mtd, -1);
nand_release_device(mtd);
/* Do call back function */
if (!ret)
mtd_erase_callback(instr);
/* Return more or less happy */
return ret;
}

View File

@ -14,6 +14,8 @@
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_MACRONIX 0xC2
#define MACRONIX_ECCSR_MASK 0x0F
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
@ -59,7 +61,13 @@ static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
SPI_MEM_OP_DUMMY(1, 1),
SPI_MEM_OP_DATA_IN(1, eccsr, 1));
return spi_mem_exec_op(spinand->slave, &op);
int ret = spi_mem_exec_op(spinand->slave, &op);
if (ret)
return ret;
*eccsr &= MACRONIX_ECCSR_MASK;
return 0;
}
static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,

View File

@ -1836,9 +1836,6 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
erase_exit:
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
/* Do call back function */
if (!ret)
mtd_erase_callback(instr);
/* Deselect and wake up anyone waiting on the device */
onenand_release_device(mtd);

View File

@ -46,7 +46,6 @@ static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
}
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}

View File

@ -908,30 +908,40 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
bool addr_known = false;
u32 addr, len, rem;
int ret;
int ret, err;
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
(long long)instr->len);
if (!instr->len)
return 0;
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
if (rem) {
ret = -EINVAL;
goto err;
}
addr = instr->addr;
len = instr->len;
instr->state = MTD_ERASING;
addr_known = true;
while (len) {
WATCHDOG_RESET();
if (ctrlc()) {
addr_known = false;
ret = -EINTR;
goto erase_err;
}
#ifdef CONFIG_SPI_FLASH_BAR
ret = write_bar(nor, addr);
if (ret < 0)
return ret;
goto erase_err;
#endif
write_enable(nor);
ret = write_enable(nor);
if (ret < 0)
goto erase_err;
ret = spi_nor_erase_sector(nor, addr);
if (ret < 0)
@ -945,11 +955,24 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
goto erase_err;
}
addr_known = false;
erase_err:
#ifdef CONFIG_SPI_FLASH_BAR
ret = clean_bar(nor);
err = clean_bar(nor);
if (!ret)
ret = err;
#endif
write_disable(nor);
err = write_disable(nor);
if (!ret)
ret = err;
err:
if (ret) {
instr->fail_addr = addr_known ? addr : MTD_FAIL_ADDR_UNKNOWN;
instr->state = MTD_ERASE_FAILED;
} else {
instr->state = MTD_ERASE_DONE;
}
return ret;
}
@ -1665,9 +1688,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
if (!len)
return 0;
for (i = 0; i < len; ) {
ssize_t written;
loff_t addr = to + i;
@ -3224,6 +3244,21 @@ static struct spi_nor_fixups s25hx_t_fixups = {
.post_bfpt = s25hx_t_post_bfpt_fixup,
.post_sfdp = s25hx_t_post_sfdp_fixup,
};
static int s25fl256l_setup(struct spi_nor *nor, const struct flash_info *info,
const struct spi_nor_flash_parameter *params)
{
return -ENOTSUPP; /* Bank Address Register is not supported */
}
static void s25fl256l_default_init(struct spi_nor *nor)
{
nor->setup = s25fl256l_setup;
}
static struct spi_nor_fixups s25fl256l_fixups = {
.default_init = s25fl256l_default_init,
};
#endif
#ifdef CONFIG_SPI_FLASH_S28HS512T
@ -3646,6 +3681,10 @@ void spi_nor_set_fixups(struct spi_nor *nor)
break;
}
}
if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) &&
!strcmp(nor->info->name, "s25fl256l"))
nor->fixups = &s25fl256l_fixups;
#endif
#ifdef CONFIG_SPI_FLASH_S28HS512T

View File

@ -122,6 +122,11 @@ const struct flash_info spi_nor_ids[] = {
SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("gd25lq256d", 0xc86019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
#endif
#ifdef CONFIG_SPI_FLASH_ISSI /* ISSI */
/* ISSI */
@ -133,6 +138,8 @@ const struct flash_info spi_nor_ids[] = {
SECT_4K | SPI_NOR_DUAL_READ) },
{ INFO("is25lp256", 0x9d6019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ) },
{ INFO("is25lp512", 0x9d601a, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("is25wp032", 0x9d7016, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("is25wp064", 0x9d7017, 0, 64 * 1024, 128,
@ -142,6 +149,8 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("is25wp256", 0x9d7019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_4B_OPCODES) },
{ INFO("is25wp512", 0x9d701a, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
#endif
#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */
/* Macronix */
@ -186,7 +195,7 @@ const struct flash_info spi_nor_ids[] = {
{ INFO6("mt25qu256a", 0x20bb19, 0x104400, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_FSR) },
{ INFO("n25q256ax1", 0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ | USE_FSR) },
{ INFO6("mt25qu512a", 0x20bb20, 0x104400, 64 * 1024, 1024,
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
USE_FSR) },
{ INFO("n25q512a", 0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ INFO6("mt25ql512a", 0x20ba20, 0x104400, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
@ -195,6 +204,7 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("n25q00a", 0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("mt25ql01g", 0x21ba20, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("mt25ql02g", 0x20ba22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE | SPI_NOR_4B_OPCODES) },
#ifdef CONFIG_SPI_FLASH_MT35XU
{ INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) },
#endif /* CONFIG_SPI_FLASH_MT35XU */
@ -227,6 +237,7 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("s25fl208k", 0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) },
{ INFO("s25fl064l", 0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("s25fl128l", 0x016018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("s25fl256l", 0x016019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO6("s25hl512t", 0x342a1a, 0x0f0390, 256 * 1024, 256,
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
USE_CLSR) },

View File

@ -304,18 +304,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
return err;
}
/**
* erase_callback - MTD erasure call-back.
* @ei: MTD erase information object.
*
* Note, even though MTD erase interface is asynchronous, all the current
* implementations are synchronous anyway.
*/
static void erase_callback(struct erase_info *ei)
{
wake_up_interruptible((wait_queue_head_t *)ei->priv);
}
/**
* do_sync_erase - synchronously erase a physical eraseblock.
* @ubi: UBI device description object
@ -346,7 +334,6 @@ retry:
ei.mtd = ubi->mtd;
ei.addr = (loff_t)pnum * ubi->peb_size;
ei.len = ubi->peb_size;
ei.callback = erase_callback;
ei.priv = (unsigned long)&wq;
err = mtd_erase(ubi->mtd, &ei);

View File

@ -41,6 +41,11 @@
#include <spi.h>
#include <spi-mem.h>
#include <asm/io.h>
#ifdef CONFIG_FSL_LAYERSCAPE
#include <asm/arch/clock.h>
#include <asm/arch/soc.h>
#include <asm/arch/speed.h>
#endif
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
@ -304,6 +309,9 @@
#define POLL_TOUT 5000
#define NXP_FSPI_MAX_CHIPSELECT 4
/* Access flash memory using IP bus only */
#define FSPI_QUIRK_USE_IP_ONLY BIT(0)
struct nxp_fspi_devtype_data {
unsigned int rxfifo;
unsigned int txfifo;
@ -312,7 +320,7 @@ struct nxp_fspi_devtype_data {
bool little_endian;
};
static const struct nxp_fspi_devtype_data lx2160a_data = {
static struct nxp_fspi_devtype_data lx2160a_data = {
.rxfifo = SZ_512, /* (64 * 64 bits) */
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
@ -320,7 +328,7 @@ static const struct nxp_fspi_devtype_data lx2160a_data = {
.little_endian = true, /* little-endian */
};
static const struct nxp_fspi_devtype_data imx8mm_data = {
static struct nxp_fspi_devtype_data imx8mm_data = {
.rxfifo = SZ_512, /* (64 * 64 bits) */
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
@ -335,9 +343,14 @@ struct nxp_fspi {
u32 memmap_phy;
u32 memmap_phy_size;
struct clk clk, clk_en;
const struct nxp_fspi_devtype_data *devtype_data;
struct nxp_fspi_devtype_data *devtype_data;
};
static inline int needs_ip_only(struct nxp_fspi *f)
{
return f->devtype_data->quirks & FSPI_QUIRK_USE_IP_ONLY;
}
/*
* R/W functions for big- or little-endian registers:
* The FSPI controller's endianness is independent of
@ -521,8 +534,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
for (i = 0; i < ARRAY_SIZE(lutval); i++)
fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x]\n",
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3]);
dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);
/* lock LUT */
fspi_writel(f, FSPI_LUTKEY_VALUE, f->iobase + FSPI_LUTKEY);
@ -769,12 +782,14 @@ static int nxp_fspi_exec_op(struct spi_slave *slave,
nxp_fspi_prepare_lut(f, op);
/*
* If we have large chunks of data, we read them through the AHB bus
* by accessing the mapped memory. In all other cases we use
* IP commands to access the flash.
* If we have large chunks of data, we read them through the AHB bus by
* accessing the mapped memory. In all other cases we use IP commands
* to access the flash. Read via AHB bus may be corrupted due to
* existence of an errata and therefore discard AHB read in such cases.
*/
if (op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
op->data.dir == SPI_MEM_DATA_IN) {
op->data.dir == SPI_MEM_DATA_IN &&
!needs_ip_only(f)) {
nxp_fspi_read_ahb(f, op);
} else {
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
@ -808,9 +823,42 @@ static int nxp_fspi_adjust_op_size(struct spi_slave *slave,
op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8);
}
/* Limit data bytes to RX FIFO in case of IP read only */
if (needs_ip_only(f) &&
op->data.dir == SPI_MEM_DATA_IN &&
op->data.nbytes > f->devtype_data->rxfifo)
op->data.nbytes = f->devtype_data->rxfifo;
return 0;
}
#ifdef CONFIG_FSL_LAYERSCAPE
static void erratum_err050568(struct nxp_fspi *f)
{
struct sys_info sysinfo;
u32 svr = 0, freq = 0;
/* Check for LS1028A variants */
svr = SVR_SOC_VER(get_svr());
if (svr != SVR_LS1017A ||
svr != SVR_LS1018A ||
svr != SVR_LS1027A ||
svr != SVR_LS1028A) {
dev_dbg(f->dev, "Errata applicable only for LS1028A variants\n");
return;
}
/* Read PLL frequency */
get_sys_info(&sysinfo);
freq = sysinfo.freq_systembus / 1000000; /* Convert to MHz */
dev_dbg(f->dev, "svr: %08x, Frequency: %dMhz\n", svr, freq);
/* Use IP bus only if PLL is 300MHz */
if (freq == 300)
f->devtype_data->quirks |= FSPI_QUIRK_USE_IP_ONLY;
}
#endif
static int nxp_fspi_default_setup(struct nxp_fspi *f)
{
void __iomem *base = f->iobase;
@ -831,6 +879,17 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
return ret;
#endif
#ifdef CONFIG_FSL_LAYERSCAPE
/*
* ERR050568: Flash access by FlexSPI AHB command may not work with
* platform frequency equal to 300 MHz on LS1028A.
* LS1028A reuses LX2160A compatible entry. Make errata applicable for
* Layerscape LS1028A platform family.
*/
if (device_is_compatible(f->dev, "nxp,lx2160a-fspi"))
erratum_err050568(f);
#endif
/* Reset the module */
/* w1c register, wait unit clear */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_MCR0,

4
env/onenand.c vendored
View File

@ -73,9 +73,7 @@ static int env_onenand_save(void)
#endif
loff_t env_addr = CONFIG_ENV_ADDR;
size_t retlen;
struct erase_info instr = {
.callback = NULL,
};
struct erase_info instr = {};
ret = env_export(&env_new);
if (ret)

View File

@ -145,7 +145,6 @@ int nandmtd_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber)
ei.len = dev->data_bytes_per_chunk * dev->param.chunks_per_block;
ei.time = 1000;
ei.retries = 2;
ei.callback = NULL;
ei.priv = (u_long) dev;
/* Todo finish off the ei if required */

View File

@ -51,7 +51,6 @@ struct erase_info {
u_long retries;
unsigned dev;
unsigned cell;
void (*callback) (struct erase_info *self);
u_long priv;
u_char state;
struct erase_info *next;
@ -535,16 +534,6 @@ extern int unregister_mtd_user (struct mtd_notifier *old);
#endif
void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size);
#ifdef CONFIG_MTD_PARTITIONS
void mtd_erase_callback(struct erase_info *instr);
#else
static inline void mtd_erase_callback(struct erase_info *instr)
{
if (instr->callback)
instr->callback(instr);
}
#endif
static inline int mtd_is_bitflip(int err) {
return err == -EUCLEAN;
}

View File

@ -69,7 +69,6 @@ static inline int nand_erase(struct mtd_info *info, loff_t off, size_t size)
instr.mtd = info;
instr.addr = off;
instr.len = size;
instr.callback = 0;
return mtd_erase(info, &instr);
}

View File

@ -165,6 +165,9 @@ static inline int spi_flash_read(struct spi_flash *flash, u32 offset,
struct mtd_info *mtd = &flash->mtd;
size_t retlen;
if (!len)
return 0;
return mtd->_read(mtd, offset, len, &retlen, buf);
}
@ -174,6 +177,9 @@ static inline int spi_flash_write(struct spi_flash *flash, u32 offset,
struct mtd_info *mtd = &flash->mtd;
size_t retlen;
if (!len)
return 0;
return mtd->_write(mtd, offset, len, &retlen, buf);
}
@ -188,6 +194,9 @@ static inline int spi_flash_erase(struct spi_flash *flash, u32 offset,
return -EINVAL;
}
if (!len)
return 0;
memset(&instr, 0, sizeof(instr));
instr.addr = offset;
instr.len = len;