summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/powerpc/dts-bindings/mtd-physmap.txt42
-rw-r--r--arch/arm/mach-omap2/board-apollon.c4
-rw-r--r--arch/arm/mach-omap2/gpmc.c63
-rw-r--r--arch/arm/plat-omap/include/mach/gpmc.h4
-rw-r--r--drivers/Makefile4
-rw-r--r--drivers/mtd/Kconfig16
-rw-r--r--drivers/mtd/afs.c2
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c11
-rwxr-xr-x[-rw-r--r--]drivers/mtd/chips/cfi_util.c4
-rw-r--r--drivers/mtd/chips/jedec_probe.c22
-rw-r--r--drivers/mtd/devices/Kconfig10
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/lart.c6
-rw-r--r--drivers/mtd/devices/m25p80.c137
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c4
-rw-r--r--drivers/mtd/devices/phram.c25
-rw-r--r--drivers/mtd/devices/slram.c2
-rw-r--r--drivers/mtd/devices/sst25l.c512
-rwxr-xr-x[-rw-r--r--]drivers/mtd/inftlcore.c2
-rw-r--r--drivers/mtd/maps/Kconfig12
-rw-r--r--drivers/mtd/maps/Makefile3
-rw-r--r--drivers/mtd/maps/gpio-addr-flash.c311
-rw-r--r--drivers/mtd/maps/physmap_of.c24
-rw-r--r--drivers/mtd/maps/plat-ram.c2
-rw-r--r--drivers/mtd/maps/pmcmsp-flash.c76
-rw-r--r--drivers/mtd/maps/uclinux.c8
-rw-r--r--drivers/mtd/mtdblock.c2
-rw-r--r--drivers/mtd/mtdconcat.c6
-rw-r--r--drivers/mtd/mtdcore.c4
-rw-r--r--drivers/mtd/mtdpart.c3
-rw-r--r--drivers/mtd/nand/Kconfig24
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/atmel_nand.c2
-rw-r--r--drivers/mtd/nand/cafe_nand.c2
-rw-r--r--drivers/mtd/nand/davinci_nand.c45
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c3
-rw-r--r--drivers/mtd/nand/mxc_nand.c16
-rw-r--r--drivers/mtd/nand/nand_base.c167
-rw-r--r--drivers/mtd/nand/nand_ecc.c31
-rw-r--r--drivers/mtd/nand/ndfc.c4
-rw-r--r--drivers/mtd/nand/omap2.c347
-rw-r--r--drivers/mtd/nand/orion_nand.c3
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c17
-rw-r--r--drivers/mtd/nand/sh_flctl.c5
-rw-r--r--drivers/mtd/nand/tmio_nand.c17
-rw-r--r--drivers/mtd/nand/txx9ndfmc.c52
-rw-r--r--drivers/mtd/nand/w90p910_nand.c382
-rw-r--r--drivers/mtd/ofpart.c21
-rw-r--r--drivers/mtd/onenand/Kconfig3
-rw-r--r--drivers/mtd/onenand/generic.c24
-rw-r--r--drivers/mtd/onenand/onenand_base.c20
-rw-r--r--drivers/mtd/tests/mtd_oobtest.c2
-rw-r--r--drivers/mtd/tests/mtd_pagetest.c12
-rw-r--r--fs/jffs2/background.c20
-rw-r--r--fs/jffs2/malloc.c4
-rw-r--r--include/linux/mtd/nand.h5
-rw-r--r--include/linux/mtd/nand_ecc.h6
-rw-r--r--include/linux/mtd/onenand.h8
-rw-r--r--include/linux/mtd/onenand_regs.h3
59 files changed, 2318 insertions, 250 deletions
diff --git a/Documentation/powerpc/dts-bindings/mtd-physmap.txt b/Documentation/powerpc/dts-bindings/mtd-physmap.txt
index 667c9bde8699..80152cb567d9 100644
--- a/Documentation/powerpc/dts-bindings/mtd-physmap.txt
+++ b/Documentation/powerpc/dts-bindings/mtd-physmap.txt
@@ -1,18 +1,19 @@
-CFI or JEDEC memory-mapped NOR flash
+CFI or JEDEC memory-mapped NOR flash, MTD-RAM (NVRAM...)
Flash chips (Memory Technology Devices) are often used for solid state
file systems on embedded devices.
- - compatible : should contain the specific model of flash chip(s)
- used, if known, followed by either "cfi-flash" or "jedec-flash"
- - reg : Address range(s) of the flash chip(s)
+ - compatible : should contain the specific model of mtd chip(s)
+ used, if known, followed by either "cfi-flash", "jedec-flash"
+ or "mtd-ram".
+ - reg : Address range(s) of the mtd chip(s)
It's possible to (optionally) define multiple "reg" tuples so that
- non-identical NOR chips can be described in one flash node.
- - bank-width : Width (in bytes) of the flash bank. Equal to the
+ non-identical chips can be described in one node.
+ - bank-width : Width (in bytes) of the bank. Equal to the
device width times the number of interleaved chips.
- - device-width : (optional) Width of a single flash chip. If
+ - device-width : (optional) Width of a single mtd chip. If
omitted, assumed to be equal to 'bank-width'.
- - #address-cells, #size-cells : Must be present if the flash has
+ - #address-cells, #size-cells : Must be present if the device has
sub-nodes representing partitions (see below). In this case
both #address-cells and #size-cells must be equal to 1.
@@ -22,24 +23,24 @@ are defined:
- vendor-id : Contains the flash chip's vendor id (1 byte).
- device-id : Contains the flash chip's device id (1 byte).
-In addition to the information on the flash bank itself, the
+In addition to the information on the mtd bank itself, the
device tree may optionally contain additional information
-describing partitions of the flash address space. This can be
+describing partitions of the address space. This can be
used on platforms which have strong conventions about which
-portions of the flash are used for what purposes, but which don't
+portions of a flash are used for what purposes, but which don't
use an on-flash partition table such as RedBoot.
-Each partition is represented as a sub-node of the flash device.
+Each partition is represented as a sub-node of the mtd device.
Each node's name represents the name of the corresponding
-partition of the flash device.
+partition of the mtd device.
Flash partitions
- - reg : The partition's offset and size within the flash bank.
- - label : (optional) The label / name for this flash partition.
+ - reg : The partition's offset and size within the mtd bank.
+ - label : (optional) The label / name for this partition.
If omitted, the label is taken from the node name (excluding
the unit address).
- read-only : (optional) This parameter, if present, is a hint to
- Linux that this flash partition should only be mounted
+ Linux that this partition should only be mounted
read-only. This is usually used for flash partitions
containing early-boot firmware images or data which should not
be clobbered.
@@ -78,3 +79,12 @@ Here an example with multiple "reg" tuples:
reg = <0 0x04000000>;
};
};
+
+An example using SRAM:
+
+ sram@2,0 {
+ compatible = "samsung,k6f1616u6a", "mtd-ram";
+ reg = <2 0 0x00200000>;
+ bank-width = <2>;
+ };
+
diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c
index 7a2b54c7291a..a1132288c701 100644
--- a/arch/arm/mach-omap2/board-apollon.c
+++ b/arch/arm/mach-omap2/board-apollon.c
@@ -87,7 +87,7 @@ static struct mtd_partition apollon_partitions[] = {
},
};
-static struct flash_platform_data apollon_flash_data = {
+static struct onenand_platform_data apollon_flash_data = {
.parts = apollon_partitions,
.nr_parts = ARRAY_SIZE(apollon_partitions),
};
@@ -99,7 +99,7 @@ static struct resource apollon_flash_resource[] = {
};
static struct platform_device apollon_onenand_device = {
- .name = "onenand",
+ .name = "onenand-flash",
.id = -1,
.dev = {
.platform_data = &apollon_flash_data,
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index f91934b2b092..15876828db23 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -57,6 +57,11 @@
#define GPMC_CHUNK_SHIFT 24 /* 16 MB */
#define GPMC_SECTION_SHIFT 28 /* 128 MB */
+#define PREFETCH_FIFOTHRESHOLD (0x40 << 8)
+#define CS_NUM_SHIFT 24
+#define ENABLE_PREFETCH (0x1 << 7)
+#define DMA_MPU_MODE 2
+
static struct resource gpmc_mem_root;
static struct resource gpmc_cs_mem[GPMC_CS_NUM];
static DEFINE_SPINLOCK(gpmc_mem_lock);
@@ -386,6 +391,63 @@ void gpmc_cs_free(int cs)
}
EXPORT_SYMBOL(gpmc_cs_free);
+/**
+ * gpmc_prefetch_enable - configures and starts prefetch transfer
+ * @cs: nand cs (chip select) number
+ * @dma_mode: dma mode enable (1) or disable (0)
+ * @u32_count: number of bytes to be transferred
+ * @is_write: prefetch read(0) or write post(1) mode
+ */
+int gpmc_prefetch_enable(int cs, int dma_mode,
+ unsigned int u32_count, int is_write)
+{
+ uint32_t prefetch_config1;
+
+ if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) {
+ /* Set the amount of bytes to be prefetched */
+ gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count);
+
+ /* Set dma/mpu mode, the prefetch read / post write and
+ * enable the engine. Set which cs is has requested for.
+ */
+ prefetch_config1 = ((cs << CS_NUM_SHIFT) |
+ PREFETCH_FIFOTHRESHOLD |
+ ENABLE_PREFETCH |
+ (dma_mode << DMA_MPU_MODE) |
+ (0x1 & is_write));
+ gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1);
+ } else {
+ return -EBUSY;
+ }
+ /* Start the prefetch engine */
+ gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpmc_prefetch_enable);
+
+/**
+ * gpmc_prefetch_reset - disables and stops the prefetch engine
+ */
+void gpmc_prefetch_reset(void)
+{
+ /* Stop the PFPW engine */
+ gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0);
+
+ /* Reset/disable the PFPW engine */
+ gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0);
+}
+EXPORT_SYMBOL(gpmc_prefetch_reset);
+
+/**
+ * gpmc_prefetch_status - reads prefetch status of engine
+ */
+int gpmc_prefetch_status(void)
+{
+ return gpmc_read_reg(GPMC_PREFETCH_STATUS);
+}
+EXPORT_SYMBOL(gpmc_prefetch_status);
+
static void __init gpmc_mem_init(void)
{
int cs;
@@ -452,6 +514,5 @@ void __init gpmc_init(void)
l &= 0x03 << 3;
l |= (0x02 << 3) | (1 << 0);
gpmc_write_reg(GPMC_SYSCONFIG, l);
-
gpmc_mem_init();
}
diff --git a/arch/arm/plat-omap/include/mach/gpmc.h b/arch/arm/plat-omap/include/mach/gpmc.h
index 921b16532ff5..9c99cda77ba6 100644
--- a/arch/arm/plat-omap/include/mach/gpmc.h
+++ b/arch/arm/plat-omap/include/mach/gpmc.h
@@ -103,6 +103,10 @@ extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base);
extern void gpmc_cs_free(int cs);
extern int gpmc_cs_set_reserved(int cs, int reserved);
extern int gpmc_cs_reserved(int cs);
+extern int gpmc_prefetch_enable(int cs, int dma_mode,
+ unsigned int u32_count, int is_write);
+extern void gpmc_prefetch_reset(void);
+extern int gpmc_prefetch_status(void);
extern void __init gpmc_init(void);
#endif
diff --git a/drivers/Makefile b/drivers/Makefile
index bc4205d2fc3c..2a1d41f05009 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -42,6 +42,8 @@ obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
obj-$(CONFIG_SCSI) += scsi/
obj-$(CONFIG_ATA) += ata/
+obj-$(CONFIG_MTD) += mtd/
+obj-$(CONFIG_SPI) += spi/
obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
@@ -50,8 +52,6 @@ obj-y += ieee1394/
obj-$(CONFIG_UIO) += uio/
obj-y += cdrom/
obj-y += auxdisplay/
-obj-$(CONFIG_MTD) += mtd/
-obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_PCCARD) += pcmcia/
obj-$(CONFIG_DIO) += dio/
obj-$(CONFIG_SBUS) += sbus/
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index b8e35a0b4d72..e4ec3659759a 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -25,6 +25,14 @@ config MTD_DEBUG_VERBOSE
help
Determines the verbosity level of the MTD debugging messages.
+config MTD_TESTS
+ tristate "MTD tests support"
+ depends on m
+ help
+ This option includes various MTD tests into compilation. The tests
+ should normally be compiled as kernel modules. The modules perform
+ various checks and verifications when loaded.
+
config MTD_CONCAT
tristate "MTD concatenating support"
help
@@ -45,14 +53,6 @@ config MTD_PARTITIONS
devices. Partitioning on NFTL 'devices' is a different - that's the
'normal' form of partitioning used on a block device.
-config MTD_TESTS
- tristate "MTD tests support"
- depends on m
- help
- This option includes various MTD tests into compilation. The tests
- should normally be compiled as kernel modules. The modules perform
- various checks and verifications when loaded.
-
config MTD_REDBOOT_PARTS
tristate "RedBoot partition table parsing"
depends on MTD_PARTITIONS
diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c
index d072ca5be689..cec7ab98b2a9 100644
--- a/drivers/mtd/afs.c
+++ b/drivers/mtd/afs.c
@@ -239,7 +239,7 @@ static int parse_afs_partitions(struct mtd_info *mtd,
parts[idx].offset = img_ptr;
parts[idx].mask_flags = 0;
- printk(" mtd%d: at 0x%08x, %5dKB, %8u, %s\n",
+ printk(" mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n",
idx, img_ptr, parts[idx].size / 1024,
iis.imageNumber, str);
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 61ea833e0908..94bb61e19047 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -282,16 +282,6 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
}
}
-static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param)
-{
- struct map_info *map = mtd->priv;
- struct cfi_private *cfi = map->fldrv_priv;
- if (cfi->cfiq->BufWriteTimeoutTyp) {
- pr_warning("Don't use write buffer on ST flash M29W128G\n");
- cfi->cfiq->BufWriteTimeoutTyp = 0;
- }
-}
-
static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
#ifdef AMD_BOOTLOC_BUG
@@ -308,7 +298,6 @@ static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
- { CFI_MFR_ST, 0x227E, fixup_M29W128G_write_buffer, NULL, },
#if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
#endif
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
index 34d40e25d312..c5a84fda5410 100644..100755
--- a/drivers/mtd/chips/cfi_util.c
+++ b/drivers/mtd/chips/cfi_util.c
@@ -81,6 +81,10 @@ void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
{
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);
+ /* M29W128G flashes require an additional reset command
+ when exit qry mode */
+ if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
+ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
}
EXPORT_SYMBOL_GPL(cfi_qry_mode_off);
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index ccc4cfc7e4b5..16bc2fa8128a 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -111,6 +111,11 @@
#define I28F320B3B 0x8897
#define I28F640B3T 0x8898
#define I28F640B3B 0x8899
+#define I28F640C3B 0x88CD
+#define I28F160F3T 0x88F3
+#define I28F160F3B 0x88F4
+#define I28F160C3T 0x88C2
+#define I28F160C3B 0x88C3
#define I82802AB 0x00ad
#define I82802AC 0x00ac
@@ -1103,6 +1108,19 @@ static const struct amd_flash_info jedec_table[] = {
}
}, {
.mfr_id = MANUFACTURER_INTEL,
+ .dev_id = I28F640C3B,
+ .name = "Intel 28F640C3B",
+ .devtypes = CFI_DEVICETYPE_X16,
+ .uaddr = MTD_UADDR_UNNECESSARY,
+ .dev_size = SIZE_8MiB,
+ .cmd_set = P_ID_INTEL_STD,
+ .nr_regions = 2,
+ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 127),
+ }
+ }, {
+ .mfr_id = MANUFACTURER_INTEL,
.dev_id = I82802AB,
.name = "Intel 82802AB",
.devtypes = CFI_DEVICETYPE_X8,
@@ -1156,8 +1174,8 @@ static const struct amd_flash_info jedec_table[] = {
.mfr_id = MANUFACTURER_NEC,
.dev_id = UPD29F064115,
.name = "NEC uPD29F064115",
- .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
- .uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */
+ .devtypes = CFI_DEVICETYPE_X16,
+ .uaddr = MTD_UADDR_0xAAAA_0x5555,
.dev_size = SIZE_8MiB,
.cmd_set = P_ID_AMD_STD,
.nr_regions = 3,
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 325fab92a62c..c222514bb70d 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ
help
This option enables FAST_READ access supported by ST M25Pxx.
+config MTD_SST25L
+ tristate "Support SST25L (non JEDEC) SPI Flash chips"
+ depends on SPI_MASTER
+ help
+ This enables access to the non JEDEC SST25L SPI flash chips, used
+ for program and data storage.
+
+ Set up your spi devices with the right board-specific platform data,
+ if you want to specify device partitioning.
+
config MTD_SLRAM
tristate "Uncached system RAM"
help
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 0993d5cf3923..ab5c9b92ac82 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
+obj-$(CONFIG_MTD_SST25L) += sst25l.o
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index 578de1c67bfe..f4359fe7150f 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -393,7 +393,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
* erase range is aligned with the erase size which is in
* effect here.
*/
- if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL);
+ if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1)))
+ return -EINVAL;
/* Remember the erase region we start on */
first = i;
@@ -409,7 +410,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
i--;
/* is the end aligned on a block boundary? */
- if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL);
+ if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)))
+ return -EINVAL;
addr = instr->addr;
len = instr->len;
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 10ed195c0c1c..00355862eba9 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -44,6 +44,11 @@
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
+/* Used for SST flashes only. */
+#define OPCODE_BP 0x02 /* Byte program */
+#define OPCODE_WRDI 0x04 /* Write disable */
+#define OPCODE_AAI_WP 0xad /* Auto address increment word program */
+
/* Status Register bits. */
#define SR_WIP 1 /* Write in progress */
#define SR_WEL 2 /* Write enable latch */
@@ -132,6 +137,15 @@ static inline int write_enable(struct m25p *flash)
return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
}
+/*
+ * Send write disble instruction to the chip.
+ */
+static inline int write_disable(struct m25p *flash)
+{
+ u8 code = OPCODE_WRDI;
+
+ return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
+}
/*
* Service routine to read status register until ready, or timeout occurs.
@@ -454,6 +468,111 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
return 0;
}
+static int sst_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);
+ struct spi_transfer t[2];
+ struct spi_message m;
+ size_t actual;
+ int cmd_sz, ret;
+
+ if (retlen)
+ *retlen = 0;
+
+ /* sanity checks */
+ if (!len)
+ return 0;
+
+ if (to + len > flash->mtd.size)
+ return -EINVAL;
+
+ spi_message_init(&m);
+ memset(t, 0, (sizeof t));
+
+ t[0].tx_buf = flash->command;
+ t[0].len = CMD_SIZE;
+ 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. */
+ ret = wait_till_ready(flash);
+ if (ret)
+ goto time_out;
+
+ write_enable(flash);
+
+ actual = to % 2;
+ /* Start write from odd address. */
+ if (actual) {
+ flash->command[0] = OPCODE_BP;
+ flash->command[1] = to >> 16;
+ flash->command[2] = to >> 8;
+ flash->command[3] = to;
+
+ /* write one byte. */
+ t[1].len = 1;
+ spi_sync(flash->spi, &m);
+ ret = wait_till_ready(flash);
+ if (ret)
+ goto time_out;
+ *retlen += m.actual_length - CMD_SIZE;
+ }
+ to += actual;
+
+ flash->command[0] = OPCODE_AAI_WP;
+ flash->command[1] = to >> 16;
+ flash->command[2] = to >> 8;
+ flash->command[3] = to;
+
+ /* Write out most of the data here. */
+ cmd_sz = CMD_SIZE;
+ for (; actual < len - 1; actual += 2) {
+ t[0].len = cmd_sz;
+ /* write two bytes. */
+ t[1].len = 2;
+ t[1].tx_buf = buf + actual;
+
+ spi_sync(flash->spi, &m);
+ ret = wait_till_ready(flash);
+ if (ret)
+ goto time_out;
+ *retlen += m.actual_length - cmd_sz;
+ cmd_sz = 1;
+ to += 2;
+ }
+ write_disable(flash);
+ ret = wait_till_ready(flash);
+ if (ret)
+ goto time_out;
+<