Commit 10f39f04 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-20121009' of git://git.infradead.org/mtd-2.6

Pull MTD updates from David Woodhouse:

 - Disable broken mtdchar mmap() on MMU systems
 - Additional ECC tests for NAND flash, and some test cleanups
 - New NAND and SPI chip support
 - Fixes/cleanup for SH FLCTL NAND controller driver
 - Improved hardware support for GPMI NAND controller
 - Conversions to device-tree support for various drivers
 - Removal of obsolete drivers (sbc8xxx, bcmring, etc.)
 - New LPC32xx drivers for MLC and SLC NAND
 - Further cleanup of NAND OOB/ECC handling
 - UAPI cleanup merge from David Howells (just moving files, since MTD
   headers were sorted out long ago to separate user-visible from kernel
   bits)

* tag 'for-linus-20121009' of git://git.infradead.org/mtd-2.6: (168 commits)
  mtd: Disable mtdchar mmap on MMU systems
  UAPI: (Scripted) Disintegrate include/mtd
  mtd: nand: detect Samsung K9GBG08U0A, K9GAG08U0F ID
  mtd: nand: decode Hynix MLC, 6-byte ID length
  mtd: nand: increase max OOB size to 640
  mtd: nand: add generic READ ID length calculation functions
  mtd: nand: split simple ID decode into its own function
  mtd: nand: split extended ID decoding into its own function
  mtd: nand: split BB marker options decoding into its own function
  mtd: nand: remove redundant ID read
  mtd: nand: remove unnecessary variable
  mtd: docg4: add missing HAS_IOMEM dependency
  mtd: gpmi: initialize the timing registers only one time
  mtd: gpmi: add EDO feature for imx6q
  mtd: gpmi: do not set the default values for the extra clocks
  mtd: gpmi: simplify the DLL setting code
  mtd: gpmi: add a new field for HW_GPMI_CTRL1
  mtd: gpmi: do not get the clock frequency in gpmi_begin()
  mtd: gpmi: add a new field for HW_GPMI_TIMING1
  mtd: add helpers to get the supportted ONFI timing mode
  ...
parents 72055425 f5cf8f07
......@@ -1216,8 +1216,6 @@ in this page</entry>
#define NAND_BBT_LASTBLOCK 0x00000010
/* The bbt is at the given page, else we must scan for the bbt */
#define NAND_BBT_ABSPAGE 0x00000020
/* The bbt is at the given page, else we must scan for the bbt */
#define NAND_BBT_SEARCH 0x00000040
/* bbt is stored per chip on multichip devices */
#define NAND_BBT_PERCHIP 0x00000080
/* bbt has a version counter at offset veroffs */
......
* Texas Instruments Davinci NAND
This file provides information, what the device node for the
davinci nand interface contain.
Required properties:
- compatible: "ti,davinci-nand";
- reg : contain 2 offset/length values:
- offset and length for the access window
- offset and length for accessing the aemif control registers
- ti,davinci-chipselect: Indicates on the davinci_nand driver which
chipselect is used for accessing the nand.
Recommended properties :
- ti,davinci-mask-ale: mask for ale
- ti,davinci-mask-cle: mask for cle
- ti,davinci-mask-chipsel: mask for chipselect
- ti,davinci-ecc-mode: ECC mode valid values for davinci driver:
- "none"
- "soft"
- "hw"
- ti,davinci-ecc-bits: used ECC bits, currently supported 1 or 4.
- ti,davinci-nand-buswidth: buswidth 8 or 16
- ti,davinci-nand-use-bbt: use flash based bad block table support.
Example (enbw_cmc board):
aemif@60000000 {
compatible = "ti,davinci-aemif";
#address-cells = <2>;
#size-cells = <1>;
reg = <0x68000000 0x80000>;
ranges = <2 0 0x60000000 0x02000000
3 0 0x62000000 0x02000000
4 0 0x64000000 0x02000000
5 0 0x66000000 0x02000000
6 0 0x68000000 0x02000000>;
nand@3,0 {
compatible = "ti,davinci-nand";
reg = <3 0x0 0x807ff
6 0x0 0x8000>;
#address-cells = <1>;
#size-cells = <1>;
ti,davinci-chipselect = <1>;
ti,davinci-mask-ale = <0>;
ti,davinci-mask-cle = <0>;
ti,davinci-mask-chipsel = <0>;
ti,davinci-ecc-mode = "hw";
ti,davinci-ecc-bits = <4>;
ti,davinci-nand-use-bbt;
};
};
......@@ -3,7 +3,9 @@ Atmel NAND flash
Required properties:
- compatible : "atmel,at91rm9200-nand".
- reg : should specify localbus address and size used for the chip,
and if availlable the ECC.
and hardware ECC controller if available.
If the hardware ECC is PMECC, it should contain address and size for
PMECC, PMECC Error Location controller and ROM which has lookup tables.
- atmel,nand-addr-offset : offset for the address latch.
- atmel,nand-cmd-offset : offset for the command latch.
- #address-cells, #size-cells : Must be present if the device has sub-nodes
......@@ -16,6 +18,15 @@ Optional properties:
- nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default.
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
"soft_bch".
- atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware.
Only supported by at91sam9x5 or later sam9 product.
- atmel,pmecc-cap : error correct capability for Programmable Multibit ECC
Controller. Supported values are: 2, 4, 8, 12, 24.
- atmel,pmecc-sector-size : sector size for ECC computation. Supported values
are: 512, 1024.
- atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM
for different sector size. First one is for sector size 512, the next is for
sector size 1024.
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
......@@ -39,3 +50,30 @@ nand0: nand@40000000,0 {
...
};
};
/* for PMECC supported chips */
nand0: nand@40000000 {
compatible = "atmel,at91rm9200-nand";
#address-cells = <1>;
#size-cells = <1>;
reg = < 0x40000000 0x10000000 /* bus addr & size */
0xffffe000 0x00000600 /* PMECC addr & size */
0xffffe600 0x00000200 /* PMECC ERRLOC addr & size */
0x00100000 0x00100000 /* ROM addr & size */
>;
atmel,nand-addr-offset = <21>; /* ale */
atmel,nand-cmd-offset = <22>; /* cle */
nand-on-flash-bbt;
nand-ecc-mode = "hw";
atmel,has-pmecc; /* enable PMECC */
atmel,pmecc-cap = <2>;
atmel,pmecc-sector-size = <512>;
atmel,pmecc-lookup-table-offset = <0x8000 0x10000>;
gpios = <&pioD 5 0 /* rdy */
&pioD 4 0 /* nce */
0 /* cd */
>;
partition@0 {
...
};
};
......@@ -12,6 +12,10 @@ Required properties:
- interrupt-names : The interrupt names "gpmi-dma", "bch";
- fsl,gpmi-dma-channel : Should contain the dma channel it uses.
Optional properties:
- nand-on-flash-bbt: boolean to enable on flash bbt option if not
present false
The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail.
......
NXP LPC32xx SoC NAND MLC controller
Required properties:
- compatible: "nxp,lpc3220-mlc"
- reg: Address and size of the controller
- interrupts: The NAND interrupt specification
- gpios: GPIO specification for NAND write protect
The following required properties are very controller specific. See the LPC32xx
User Manual 7.5.14 MLC NAND Timing Register (the values here are specified in
Hz, to make them independent of actual clock speed and to provide for good
accuracy:)
- nxp,tcea_delay: TCEA_DELAY
- nxp,busy_delay: BUSY_DELAY
- nxp,nand_ta: NAND_TA
- nxp,rd_high: RD_HIGH
- nxp,rd_low: RD_LOW
- nxp,wr_high: WR_HIGH
- nxp,wr_low: WR_LOW
Optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
Example:
mlc: flash@200A8000 {
compatible = "nxp,lpc3220-mlc";
reg = <0x200A8000 0x11000>;
interrupts = <11 0>;
#address-cells = <1>;
#size-cells = <1>;
nxp,tcea-delay = <333333333>;
nxp,busy-delay = <10000000>;
nxp,nand-ta = <18181818>;
nxp,rd-high = <31250000>;
nxp,rd-low = <45454545>;
nxp,wr-high = <40000000>;
nxp,wr-low = <83333333>;
gpios = <&gpio 5 19 1>; /* GPO_P3 19, active low */
mtd0@00000000 {
label = "boot";
reg = <0x00000000 0x00064000>;
read-only;
};
...
};
NXP LPC32xx SoC NAND SLC controller
Required properties:
- compatible: "nxp,lpc3220-slc"
- reg: Address and size of the controller
- nand-on-flash-bbt: Use bad block table on flash
- gpios: GPIO specification for NAND write protect
The following required properties are very controller specific. See the LPC32xx
User Manual:
- nxp,wdr-clks: Delay before Ready signal is tested on write (W_RDY)
- nxp,rdr-clks: Delay before Ready signal is tested on read (R_RDY)
(The following values are specified in Hz, to make them independent of actual
clock speed:)
- nxp,wwidth: Write pulse width (W_WIDTH)
- nxp,whold: Write hold time (W_HOLD)
- nxp,wsetup: Write setup time (W_SETUP)
- nxp,rwidth: Read pulse width (R_WIDTH)
- nxp,rhold: Read hold time (R_HOLD)
- nxp,rsetup: Read setup time (R_SETUP)
Optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
Example:
slc: flash@20020000 {
compatible = "nxp,lpc3220-slc";
reg = <0x20020000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
nxp,wdr-clks = <14>;
nxp,wwidth = <40000000>;
nxp,whold = <100000000>;
nxp,wsetup = <100000000>;
nxp,rdr-clks = <14>;
nxp,rwidth = <40000000>;
nxp,rhold = <66666666>;
nxp,rsetup = <100000000>;
nand-on-flash-bbt;
gpios = <&gpio 5 19 1>; /* GPO_P3 19, active low */
mtd0@00000000 {
label = "phy3250-boot";
reg = <0x00000000 0x00064000>;
read-only;
};
...
};
......@@ -16,6 +16,13 @@ file systems on embedded devices.
- #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.
- no-unaligned-direct-access: boolean to disable the default direct
mapping of the flash.
On some platforms (e.g. MPC5200) a direct 1:1 mapping may cause
problems with JFFS2 usage, as the local bus (LPB) doesn't support
unaligned accesses as implemented in the JFFS2 code via memcpy().
By defining "no-unaligned-direct-access", the flash will not be
exposed directly to the MTD users (e.g. JFFS2) any more.
For JEDEC compatible devices, the following additional properties
are defined:
......
......@@ -407,6 +407,13 @@ audmux@83fd0000 {
status = "disabled";
};
nand@83fdb000 {
compatible = "fsl,imx51-nand";
reg = <0x83fdb000 0x1000 0xcfff0000 0x10000>;
interrupts = <8>;
status = "disabled";
};
ssi3: ssi@83fe8000 {
compatible = "fsl,imx51-ssi", "fsl,imx21-ssi";
reg = <0x83fe8000 0x4000>;
......
......@@ -518,6 +518,13 @@ audmux@63fd0000 {
status = "disabled";
};
nand@63fdb000 {
compatible = "fsl,imx53-nand";
reg = <0x63fdb000 0x1000 0xf7ff0000 0x10000>;
interrupts = <8>;
status = "disabled";
};
ssi3: ssi@63fe8000 {
compatible = "fsl,imx53-ssi", "fsl,imx21-ssi";
reg = <0x63fe8000 0x4000>;
......
......@@ -49,7 +49,6 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_PLATRAM=m
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_ATMEL=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
......
......@@ -97,7 +97,6 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_ROM=y
CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_SHARPSL=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_IDE=y
......
......@@ -61,7 +61,6 @@ CONFIG_MTD_CFI_STAA=y
CONFIG_MTD_ROM=y
CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_BLK_DEV_NBD=y
CONFIG_EEPROM_LEGACY=y
CONFIG_SCSI=y
......
......@@ -102,7 +102,6 @@ CONFIG_MTD_CFI_STAA=y
CONFIG_MTD_RAM=y
CONFIG_MTD_ROM=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_S3C2410=y
CONFIG_MTD_NAND_PLATFORM=y
CONFIG_MTD_LPDDR=y
......
......@@ -49,7 +49,6 @@ CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_ORION=y
CONFIG_BLK_DEV_LOOP=y
# CONFIG_SCSI_PROC_FS is not set
......
......@@ -57,7 +57,6 @@ CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ECC_SMC=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_NOMADIK=y
CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y
......
......@@ -72,7 +72,6 @@ CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PLATFORM=y
CONFIG_MTD_NAND_ORION=y
CONFIG_BLK_DEV_LOOP=y
......
......@@ -36,7 +36,6 @@ CONFIG_MTD_CONCAT=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PXA3xx=y
CONFIG_MTD_NAND_PXA3xx_BUILTIN=y
CONFIG_MTD_ONENAND=y
......
......@@ -94,7 +94,6 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_ROM=y
CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_SHARPSL=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_IDE=y
......
......@@ -23,6 +23,8 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <asm/sizes.h>
......@@ -62,9 +64,26 @@ void __init autcpu12_map_io(void)
iotable_init(autcpu12_io_desc, ARRAY_SIZE(autcpu12_io_desc));
}
static struct resource autcpu12_nvram_resource[] __initdata = {
DEFINE_RES_MEM_NAMED(AUTCPU12_PHYS_NVRAM, SZ_128K, "SRAM"),
};
static struct platform_device autcpu12_nvram_pdev __initdata = {
.name = "autcpu12_nvram",
.id = -1,
.resource = autcpu12_nvram_resource,
.num_resources = ARRAY_SIZE(autcpu12_nvram_resource),
};
static void __init autcpu12_init(void)
{
platform_device_register(&autcpu12_nvram_pdev);
}
MACHINE_START(AUTCPU12, "autronix autcpu12")
/* Maintainer: Thomas Gleixner */
.atag_offset = 0x20000,
.init_machine = autcpu12_init,
.map_io = autcpu12_map_io,
.init_irq = clps711x_init_irq,
.timer = &clps711x_timer,
......
......@@ -369,6 +369,7 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "83fcc000.ssi");
clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "70014000.ssi");
clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "83fe8000.ssi");
clk_register_clkdev(clk[nfc_gate], NULL, "83fdb000.nand");
/* set the usboh3 parent to pll2_sw */
clk_set_parent(clk[usboh3_sel], clk[pll2_sw]);
......@@ -461,6 +462,7 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "63fcc000.ssi");
clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "50014000.ssi");
clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "63fd0000.ssi");
clk_register_clkdev(clk[nfc_gate], NULL, "63fdb000.nand");
clk_register_clkdev(clk[can1_ipg_gate], "ipg", "53fc8000.can");
clk_register_clkdev(clk[can1_serial_gate], "per", "53fc8000.can");
clk_register_clkdev(clk[can2_ipg_gate], "ipg", "53fcc000.can");
......
......@@ -63,10 +63,6 @@ struct platform_device *__init imx_add_mxc_nand(
/* AXI has to come first, that's how the mxc_nand driver expect it */
struct resource res[] = {
{
.start = data->axibase,
.end = data->axibase + SZ_16K - 1,
.flags = IORESOURCE_MEM,
}, {
.start = data->iobase,
.end = data->iobase + data->iosize - 1,
.flags = IORESOURCE_MEM,
......@@ -74,10 +70,13 @@ struct platform_device *__init imx_add_mxc_nand(
.start = data->irq,
.end = data->irq,
.flags = IORESOURCE_IRQ,
}, {
.start = data->axibase,
.end = data->axibase + SZ_16K - 1,
.flags = IORESOURCE_MEM,
},
};
return imx_add_platform_device("mxc_nand", data->id,
res + !data->axibase,
ARRAY_SIZE(res) - !data->axibase,
res, ARRAY_SIZE(res) - !data->axibase,
pdata, sizeof(*pdata));
}
......@@ -57,7 +57,6 @@ CONFIG_MTD_PLATRAM=y
CONFIG_MTD_PHRAM=y
CONFIG_MTD_BLOCK2MTD=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PLATFORM=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
......
......@@ -119,7 +119,6 @@ CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_BLOCK2MTD=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PLATFORM=y
CONFIG_ATA=y
# CONFIG_ATA_VERBOSE_ERROR is not set
......
......@@ -38,7 +38,6 @@ CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_FSL_ELBC=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
......
......@@ -37,7 +37,6 @@ CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
......
......@@ -50,7 +50,6 @@ CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_FSL_ELBC=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
......
......@@ -148,6 +148,13 @@ config MTD_BCM63XX_PARTS
This provides partions parsing for BCM63xx devices with CFE
bootloaders.
config MTD_BCM47XX_PARTS
tristate "BCM47XX partitioning support"
depends on BCM47XX
help
This provides partitions parser for devices based on BCM47xx
boards.
comment "User Modules And Translation Layers"
config MTD_CHAR
......
......@@ -12,6 +12,7 @@ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
......
/*
* BCM47XX MTD partitioning
*
* Copyright © 2012 Rafał Miłecki <zajec5@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <asm/mach-bcm47xx/nvram.h>
/* 10 parts were found on sflash on Netgear WNDR4500 */
#define BCM47XXPART_MAX_PARTS 12
/*
* Amount of bytes we read when analyzing each block of flash memory.
* Set it big enough to allow detecting partition and reading important data.
*/
#define BCM47XXPART_BYTES_TO_READ 0x404
/* Magics */
#define BOARD_DATA_MAGIC 0x5246504D /* MPFR */
#define POT_MAGIC1 0x54544f50 /* POTT */
#define POT_MAGIC2 0x504f /* OP */
#define ML_MAGIC1 0x39685a42
#define ML_MAGIC2 0x26594131
#define TRX_MAGIC 0x30524448
struct trx_header {
uint32_t magic;
uint32_t length;
uint32_t crc32;
uint16_t flags;
uint16_t version;
uint32_t offset[3];
} __packed;
static void bcm47xxpart_add_part(struct mtd_partition *part, char *name,
u64 offset, uint32_t mask_flags)
{
part->name = name;
part->offset = offset;
part->mask_flags = mask_flags;
}
static int bcm47xxpart_parse(struct mtd_info *master,
struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
uint8_t i, curr_part = 0;
uint32_t *buf;
size_t bytes_read;
uint32_t offset;
uint32_t blocksize = 0x10000;
struct trx_header *trx;
/* Alloc */
parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,
GFP_KERNEL);
buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL);
/* Parse block by block looking for magics */
for (offset = 0; offset <= master->size - blocksize;
offset += blocksize) {
/* Nothing more in higher memory */
if (offset >= 0x2000000)
break;
if (curr_part > BCM47XXPART_MAX_PARTS) {
pr_warn("Reached maximum number of partitions, scanning stopped!\n");
break;
}
/* Read beginning of the block */
if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
&bytes_read, (uint8_t *)buf) < 0) {
pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
offset);
continue;
}
/* CFE has small NVRAM at 0x400 */
if (buf[0x400 / 4] == NVRAM_HEADER) {
bcm47xxpart_add_part(&parts[curr_part++], "boot",
offset, MTD_WRITEABLE);
continue;
}
/* Standard NVRAM */
if (buf[0x000 / 4] == NVRAM_HEADER) {
bcm47xxpart_add_part(&parts[curr_part++], "nvram",
offset, 0);
continue;
}
/*
* board_data starts with board_id which differs across boards,
* but we can use 'MPFR' (hopefully) magic at 0x100
*/
if (buf[0x100 / 4] == BOARD_DATA_MAGIC) {
bcm47xxpart_add_part(&parts[curr_part++], "board_data",
offset, MTD_WRITEABLE);
continue;
}
/* POT(TOP) */
if (buf[0x000 / 4] == POT_MAGIC1 &&
(buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) {
bcm47xxpart_add_part(&parts[curr_part++], "POT", offset,
MTD_WRITEABLE);
continue;
}
/* ML */
if (buf[0x010 / 4] == ML_MAGIC1 &&
buf[0x014 / 4] == ML_MAGIC2) {
bcm47xxpart_add_part(&parts[curr_part++], "ML", offset,
MTD_WRITEABLE);
continue;
}
/* TRX */
if (buf[0x000 / 4] == TRX_MAGIC) {
trx = (struct trx_header *)buf;
i = 0;
/* We have LZMA loader if offset[2] points to sth */
if (trx->offset[2]) {
bcm47xxpart_add_part(&parts[curr_part++],
"loader",
offset + trx->offset[i],
0);
i++;
}
bcm47xxpart_add_part(&parts[curr_part++], "linux",
offset + trx->offset[i], 0);
i++;
/*
* Pure rootfs size is known and can be calculated as:
* trx->length - trx->offset[i]. We don't fill it as
* we want to have jffs2 (overlay) in the same mtd.
*/
bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
offset + trx->offset[i], 0);
i++;
/*
* We have whole TRX scanned, skip to the next part. Use
* roundown (not roundup), as the loop will increase
* offset in next step.
*/
offset = rounddown(offset + trx->length, blocksize);
continue;
}
}
kfree(buf);
/*
* Assume that partitions end at the beginning of the one they are
* followed by.
*/
for (i = 0; i < curr_part - 1; i++)
parts[i].size = parts[i + 1].offset - parts[i].offset;
if (curr_part > 0)
parts[curr_part - 1].size =
master->size - parts[curr_part - 1].offset;
*pparts = parts;
return curr_part;
};
static struct mtd_part_parser bcm47xxpart_mtd_parser = {
.owner = THIS_MODULE,
.parse_fn = bcm47xxpart_parse,
.name = "bcm47xxpart",
};
static int __init bcm47xxpart_init(void)
{
return register_mtd_parser(&bcm47xxpart_mtd_parser);
}
static void __exit bcm47xxpart_exit(void)
{
deregister_mtd_parser(&bcm47xxpart_mtd_parser);
}
module_init(bcm47xxpart_init);
module_exit(bcm47xxpart_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MTD partitioning for BCM47XX flash memories");
......@@ -43,9 +43,6 @@ choice
prompt "Flash cmd/query data swapping"
depends on MTD_CFI_ADV_OPTIONS
default MTD_CFI_NOSWAP
config MTD_CFI_NOSWAP
bool "NO"
---help---
This option defines the way in which the CPU attempts to arrange
data bits when writing the 'magic' commands to the chips. Saying
......@@ -55,12 +52,8 @@ config MTD_CFI_NOSWAP
Specific arrangements are possible with the BIG_ENDIAN_BYTE and
LITTLE_ENDIAN_BYTE, if the bytes are reversed.
If you have a LART, on which the data (and address) lines were
connected in a fashion which ensured that the nets were as short
as possible, resulting in a bit-shuffling which seems utterly
random to the untrained eye, you need the LART_ENDIAN_BYTE option.
Yes, there really exists something sicker than PDP-endian :)
config MTD_CFI_NOSWAP
bool "NO"
config MTD_CFI_BE_BYTE_SWAP
bool "BIG_ENDIAN_BYTE"
......
......@@ -2043,7 +2043,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
{
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
int udelay;
int mdelay;
int ret;
adr += chip->start;
......@@ -2072,9 +2072,17 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
* If Instant Individual Block Locking supported then no need
* to delay.
*/
udelay = (!extp || !(extp->FeatureSupport & (1 << 5))) ? 1000000/HZ : 0;
/*
* Unlocking may take up to 1.4 seconds on some Intel flashes. So
* lets use a max of 1.5 seconds (1500ms) as timeout.
*
* See "Clear Block Lock-Bits Time" on page 40 in
* "3 Volt Intel StrataFlash Memory" 28F128J3,28F640J3,28F320J3 manual
* from February 2003
*/
mdelay = (!extp || !(extp->FeatureSupport & (1 << 5))) ? 1500 : 0;
ret = WAIT_TIMEOUT(map, chip, adr, udelay, udelay * 100);
ret = WAIT_TIMEOUT(map, chip, adr, mdelay, mdelay * 1000);
if (ret) {
map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS;
......
......@@ -431,6 +431,68 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi,
}
}
static int is_m29ew(struct cfi_private *cfi)
{
if (cfi->mfr == CFI_MFR_INTEL &&
((cfi->device_type == CFI_DEVICETYPE_X8 && (cfi->id & 0xff) == 0x7e) ||
(cfi->device_type == CFI_DEVICETYPE_X16 && cfi->id == 0x227e)))
return 1;
return 0;
}
/*
* From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 20:
* Some revisions of the M29EW suffer from erase suspend hang ups. In
* particular, it can occur when the sequence
* Erase Confirm -> Suspend -> Program -> Resume
* causes a lockup due to internal timing issues. The consequence is that the
* erase cannot be resumed without inserting a dummy command after programming
* and prior to resuming. [...] The work-around is to issue a dummy write cycle
* that writes an F0 command code before the RESUME command.
*/
static void cfi_fixup_m29ew_erase_suspend(struct map_info *map,
unsigned long adr)
{
struct cfi_private *cfi = map->fldrv_priv;
/* before resume, insert a dummy 0xF0 cycle for Micron M29EW devices */
if (is_m29ew(cfi))
map_write(map, CMD(0xF0), adr);
}
/*
* From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 22:
*
* Some revisions of the M29EW (for example, A1 and A2 step revisions)
* are affected by a problem that could cause a hang up when an ERASE SUSPEND
* command is issued after an ERASE RESUME operation without waiting for a
* minimum delay. The result is that once the ERASE seems to be completed
* (no bits are toggling), the contents of the Flash memory block on which
* the erase was ongoing could be inconsistent with the expected values
* (typically, the array value is stuck to the 0xC0, 0xC4, 0x80, or 0x84
* values), causing a consequent failure of the ERASE operation.
* The occurrence of this issue could be high, especially when file system
* operations on the Flash are intensive. As a result, it is recommended
* that a patch be applied. Intensive file system operations can cause many
* calls to the garbage routine to free Flash space (also by erasing physical
* Flash blocks) and as a result, many consecutive SUSPEND and RESUME
* commands can occur. The problem disappears when a delay is inserted after
* the RESUME command by using the udelay() function available in Linux.
* The DELAY value must be tuned based on the customer's platform.
* The maximum value that fixes the problem in all cases is 500us.
* But, in our experience, a delay of 30 µs to 50 µs is sufficient
* in most cases.
* We have chosen 500µs because this latency is acceptable.
*/
static void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi)
{
/*
* Resolving the Delay After Resume Issue see Micron TN-13-07
* Worst case delay must be 500µs but 30-50µs should be ok as well
*/
if (is_m29ew(cfi))
cfi_udelay(500);
}
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
......@@ -776,7 +838,10 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
switch(chip->oldstate) {
case FL_ERASING:
cfi_fixup_m29ew_erase_suspend(map,
chip->in_progress_block_addr);
map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
cfi_fixup_m29ew_delay_after_resume(cfi);
chip->oldstate = FL_READY;
chip->state = FL_ERASING;
break;
......@@ -916,6 +981,8 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
/* Disallow XIP again */
local_irq_disable();
/* Correct Erase Suspend Hangups for M29EW */
cfi_fixup_m29ew_erase_suspend(map, adr);
/* Resume the write or erase operation */
map_write(map, cfi->sector_erase_cmd, adr);
chip->state = oldstate;
......
......@@ -39,11 +39,10 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/bootmem.h>
#include <linux/module.h>
#include <linux/err.h>
/* error message prefix */
#define ERRP "mtd: "
......@@ -72,7 +71,7 @@ static struct cmdline_mtd_partition *partitions;
/* the command line passed to mtdpart_setup() */
static char *cmdline;
static int cmdline_parsed = 0;
static int cmdline_parsed;
/*
* Parse one partition definition for an MTD. Since there can be many
......@@ -83,15 +82,14 @@ static int cmdline_parsed = 0;
* syntax has been verified ok.
*/
static struct mtd_partition * newpart(char *s,
char **retptr,
int *num_parts,
int this_part,
unsigned char **extra_mem_ptr,
int extra_mem_size)
char **retptr,
int *num_parts,
int this_part,
unsigned char **extra_mem_ptr,
int extra_mem_size)
{
struct mtd_partition *parts;
unsigned long size;
unsigned long offset = OFFSET_CONTINUOUS;
unsigned long size, offset = OFFSET_CONTINUOUS;
char *name;
int name_len;
unsigned char *extra_mem;
......@@ -99,124 +97,106 @@ static struct mtd_partition * newpart(char *s,
unsigned int mask_flags;
/* fetch the partition size */
if (*s == '-')
{ /* assign all remaining space to this partition */
if (*s == '-') {
/* assign all remaining space to this partition */
size = SIZE_REMAINING;
s++;
}
else
{
} else {
size = memparse(s, &s);
if (size < PAGE_SIZE)
{
if (size < PAGE_SIZE) {
printk(KERN_ERR ERRP "partition size too small (%lx)\n", size);
return NULL;
return ERR_PTR(-EINVAL);
}
}
/* fetch partition name and flags */
mask_flags = 0; /* this is going to be a regular partition */
delim = 0;
/* check for offset */
if (*s == '@')
{
s++;
offset = memparse(s, &s);
}
/* now look for name */
/* check for offset */
if (*s == '@') {
s++;
offset = memparse(s, &s);
}
/* now look for name */
if (*s == '(')
{
delim = ')';
}
if (delim)
{
if (delim) {
char *p;
name = ++s;
name = ++s;
p = strchr(name, delim);
if (!p)
{
if (!p) {
printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim);
return NULL;
return ERR_PTR(-EINVAL);
}
name_len = p - name;
s = p + 1;
}
else
{
name = NULL;
} else {
name = NULL;
name_len = 13; /* Partition_000 */
}
/* record name length for memory allocation later */
extra_mem_size += name_len + 1;
/* test for options */
if (strncmp(s, "ro", 2) == 0)
{
/* test for options */
if (strncmp(s, "ro", 2) == 0) {
mask_flags |= MTD_WRITEABLE;
s += 2;
}
}
/* if lk is found do NOT unlock the MTD partition*/
if (strncmp(s, "lk", 2) == 0)
{
/* if lk is found do NOT unlock the MTD partition*/
if (strncmp(s, "lk", 2) == 0) {
mask_flags |= MTD_POWERUP_LOCK;
s += 2;
}
}
/* test if more partitions are following */
if (*s == ',')
{
if (size == SIZE_REMAINING)
{
if (*s == ',') {
if (size == SIZE_REMAINING) {
printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n");
return NULL;
return ERR_PTR(-EINVAL);
}
/* more partitions follow, parse them */
parts = newpart(s + 1, &s, num_parts, this_part + 1,
&extra_mem, extra_mem_size);
if (!parts)
return NULL;
}
else
{ /* this is the last partition: allocate space for all */
if (IS_ERR(parts))
return parts;
} else {
/* this is the last partition: allocate space for all */
int alloc_size;
*num_parts = this_part + 1;
alloc_size = *num_parts * sizeof(struct mtd_partition) +
extra_mem_size;
parts = kzalloc(alloc_size, GFP_KERNEL);
if (!parts)
return NULL;
return ERR_PTR(-ENOMEM);
extra_mem = (unsigned char *)(parts + *num_parts);
}
/* enter this partition (offset will be calculated later if it is zero at this point) */
parts[this_part].size = size;
parts[this_part].offset = offset;
parts[this_part].mask_flags = mask_flags;
if (name)
{
strlcpy(extra_mem, name, name_len + 1);
}
else
{
sprintf(extra_mem, "Partition_%03d", this_part);
}
parts[this_part].name = extra_mem;
extra_mem += name_len + 1;
dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
this_part,
parts[this_part].name,
parts[this_part].offset,
parts[this_part].size,
parts[this_part].mask_flags));
this_part, parts[this_part].name, parts[this_part].offset,
parts[this_part].size, parts[this_part].mask_flags));
/* return (updated) pointer to extra_mem memory */
if (extra_mem_ptr)
*extra_mem_ptr = extra_mem;
*extra_mem_ptr = extra_mem;
/* return (updated) pointer command line string */
*retptr = s;
......@@ -236,16 +216,16 @@ static int mtdpart_setup_real(char *s)
{
struct cmdline_mtd_partition *this_mtd;
struct mtd_partition *parts;
int mtd_id_len;
int num_parts;
int mtd_id_len, num_parts;
char *p, *mtd_id;
mtd_id = s;
mtd_id = s;
/* fetch <mtd-id> */
if (!(p = strchr(s, ':')))
{
p = strchr(s, ':');
if (!p) {
printk(KERN_ERR ERRP "no mtd-id\n");
return 0;
return -EINVAL;
}
mtd_id_len = p - mtd_id;
......@@ -262,8 +242,7 @@ static int mtdpart_setup_real(char *s)
(unsigned char**)&this_mtd, /* out: extra mem */
mtd_id_len + 1 + sizeof(*this_mtd) +
sizeof(void*)-1 /*alignment*/);
if(!parts)
{
if (IS_ERR(parts)) {
/*
* An error occurred. We're either:
* a) out of memory, or
......@@ -271,12 +250,12 @@ static int mtdpart_setup_real(char *s)
* Either way, this mtd is hosed and we're
* unlikely to succeed in parsing any more
*/
return 0;
return PTR_ERR(parts);
}
/* align this_mtd */
this_mtd = (struct cmdline_mtd_partition *)
ALIGN((unsigned long)this_mtd, sizeof(void*));
ALIGN((unsigned long)this_mtd, sizeof(void *));
/* enter results */
this_mtd->parts = parts;
this_mtd->num_parts = num_parts;
......@@ -296,14 +275,14 @@ static int mtdpart_setup_real(char *s)
break;
/* does another spec follow? */
if (*s != ';')
{
if (*s != ';') {
printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s);
return 0;
return -EINVAL;
}
s++;
}
return 1;
return 0;
}
/*
......@@ -318,44 +297,58 @@ static int parse_cmdline_partitions(struct mtd_info *master,
struct mtd_part_parser_data *data)
{
unsigned long offset;
int i;
int i, err;
struct cmdline_mtd_partition *part;
const char *mtd_id = master->name;
/* parse command line */
if (!cmdline_parsed)
mtdpart_setup_real(cmdline);
if (!cmdline_parsed) {
err = mtdpart_setup_real(cmdline);
if (err)
return err;
}
for(part = partitions; part; part = part->next)
{
if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
{
for(i = 0, offset = 0; i < part->num_parts; i++)
{
for (part = partitions; part; part = part->next) {
if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) {
for (i = 0, offset = 0; i < part->num_parts; i++) {
if (part->parts[i].offset == OFFSET_CONTINUOUS)
part->parts[i].offset = offset;
part->parts[i].offset = offset;
else
offset = part->parts[i].offset;
offset = part->parts[i].offset;
if (part->parts[i].size == SIZE_REMAINING)
part->parts[i].size = master->size - offset;
if (offset + part->parts[i].size > master->size)
{
part->parts[i].size = master->size - offset;
if (part->parts[i].size == 0) {
printk(KERN_WARNING ERRP
"%s: skipping zero sized partition\n",
part->mtd_id);
part->num_parts--;
memmove(&part->parts[i],
&part->parts[i + 1],
sizeof(*part->parts) * (part->num_parts - i));
continue;
}
if (offset + part->parts[i].size > master->size) {
printk(KERN_WARNING ERRP
"%s: partitioning exceeds flash size, truncating\n",
part->mtd_id);
part->parts[i].size = master->size - offset;
part->num_parts = i;
}
offset += part->parts[i].size;
}
*pparts = kmemdup(part->parts,
sizeof(*part->parts) * part->num_parts,
GFP_KERNEL);
if (!*pparts)
return -ENOMEM;
return part->num_parts;
}
}
return 0;
}
......
......@@ -97,7 +97,7 @@ config MTD_M25P80
doesn't support the JEDEC ID instruction.
config M25PXX_USE_FAST_READ
bool "Use FAST_READ OPCode allowing SPI CLK <= 50MHz"
bool "Use FAST_READ OPCode allowing SPI CLK >= 50MHz"
depends on MTD_M25P80
default y
help
......@@ -120,6 +120,14 @@ config MTD_SST25L
Set up your spi devices with the right board-specific platform data,
if you want to specify device partitioning.
config MTD_BCM47XXSFLASH
tristate "R/O support for serial flash on BCMA bus"
depends on BCMA_SFLASH
help
BCMA bus can have various flash memories attached, they are
registered by bcma as platform devices. This enables driver for
serial flash memories (only read-only mode is implemented).
config MTD_SLRAM
tristate "Uncached system RAM"
help
......
......@@ -19,5 +19,6 @@ obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
CFLAGS_docg3.o += -I$(src)
\ No newline at end of file
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/platform_device.h>
#include <linux/bcma/bcma.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Serial flash driver for BCMA bus");
static const char *probes[] = { "bcm47xxpart", NULL };
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct bcma_sflash *sflash = mtd->priv;
/* Check address range */
if ((from + len) > mtd->size)
return -EINVAL;
memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(sflash->window + from),
len);
return len;
}
static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash,
struct mtd_info *mtd)
{
mtd->priv = sflash;
mtd->name = "bcm47xxsflash";
mtd->owner = THIS_MODULE;
mtd->type = MTD_ROM;
mtd->size = sflash->size;
mtd->_read = bcm47xxsflash_read;
/* TODO: implement writing support and verify/change following code */
mtd->flags = MTD_CAP_ROM;
mtd->writebufsize = mtd->writesize = 1;
}
static int bcm47xxsflash_probe(struct platform_device *pdev)
{
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
int err;
sflash->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!sflash->mtd) {
err = -ENOMEM;
goto out;
}
bcm47xxsflash_fill_mtd(sflash, sflash->mtd);
err = mtd_device_parse_register(sflash->mtd, probes, NULL, NULL, 0);
if (err) {
pr_err("Failed to register MTD device: %d\n", err);
goto err_dev_reg;
}
return 0;
err_dev_reg:
kfree(sflash->mtd);
out:
return err;
}
static int __devexit bcm47xxsflash_remove(struct platform_device *pdev)
{
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
mtd_device_unregister(sflash->mtd);
kfree(sflash->mtd);
return 0;
}
static struct platform_driver bcma_sflash_driver = {
.remove = __devexit_p(bcm47xxsflash_remove),
.driver = {
.name = "bcma_sflash",
.owner = THIS_MODULE,
},
};
static int __init bcm47xxsflash_init(void)
{
int err;
err = platform_driver_probe(&bcma_sflash_driver, bcm47xxsflash_probe);
if (err)
pr_err("Failed to register BCMA serial flash driver: %d\n",
err);
return err;
}
static void __exit bcm47xxsflash_exit(void)
{
platform_driver_unregister(&bcma_sflash_driver);
}
module_init(bcm47xxsflash_init);
module_exit(bcm47xxsflash_exit);
......@@ -659,23 +659,15 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
#ifdef ECC_DEBUG
printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n",
__FILE__, __LINE__, (int)from);
printk(" syndrome= %02x:%02x:%02x:%02x:%02x:"
"%02x\n",
syndrome[0], syndrome[1], syndrome[2],
syndrome[3], syndrome[4], syndrome[5]);
printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:"
"%02x\n",
eccbuf[0], eccbuf[1], eccbuf[2],
eccbuf[3], eccbuf[4], eccbuf[5]);
printk(" syndrome= %*phC\n", 6, syndrome);
printk(" eccbuf= %*phC\n", 6, eccbuf);
#endif
ret = -EIO;
}
}
#ifdef PSYCHO_DEBUG
printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
(long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
eccbuf[4], eccbuf[5]);
printk("ECC DATA at %lx: %*ph\n", (long)from, 6, eccbuf);
#endif
/* disable the ECC engine */
WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf);
......
......@@ -919,19 +919,13 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
if (nboob >= DOC_LAYOUT_OOB_SIZE) {
doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
oobbuf[0], oobbuf[1], oobbuf[2], oobbuf[3],
oobbuf[4], oobbuf[5], oobbuf[6]);
doc_dbg("OOB - INFO: %*phC\n", 7, oobbuf);
doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]);
doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
oobbuf[8], oobbuf[9], oobbuf[10], oobbuf[11],
oobbuf[12], oobbuf[13], oobbuf[14]);
doc_dbg("OOB - BCH_ECC: %*phC\n", 7, oobbuf + 8);
doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]);
}
doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1);
doc_dbg("ECC HW_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
hwecc[0], hwecc[1], hwecc[2], hwecc[3], hwecc[4],
hwecc[5], hwecc[6]);
doc_dbg("ECC HW_ECC: %*phC\n", 7, hwecc);
ret = -EIO;
if (is_prot_seq_error(docg3))
......
......@@ -633,11 +633,14 @@ static const struct spi_device_id m25p_ids[] = {
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
/* EON -- en25xxx */
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
/* Everspin */
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) },
......@@ -646,6 +649,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
/* Macronix */
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
......@@ -659,15 +663,15 @@ static const struct spi_device_id m25p_ids[] = {
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
/* Micron */
{ "n25q128", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) },
/* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
*/
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SECT_4K) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) },
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) },
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
{ "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, 0) },
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) },
......@@ -676,6 +680,11 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
......@@ -699,6 +708,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) },
{ "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
{ "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) },
......@@ -714,6 +724,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) },
{ "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) },
{ "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) },
{ "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
{ "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) },
......@@ -730,6 +741,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
......
......@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/param.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spear_smi.h>
......@@ -240,8 +241,8 @@ static int spear_smi_read_sr(struct spear_smi *dev, u32 bank)
/* copy dev->status (lower 16 bits) in order to release lock */
if (ret > 0)
ret = dev->status & 0xffff;
else
ret = -EIO;
else if (ret == 0)
ret = -ETIMEDOUT;
/* restore the ctrl regs state */
writel(ctrlreg1, dev->io_base + SMI_CR1);
......@@ -269,16 +270,19 @@ static int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank,
finish = jiffies + timeout;
do {
status = spear_smi_read_sr(dev, bank);
if (status < 0)
continue; /* try till timeout */
else if (!(status & SR_WIP))
if (status < 0) {
if (status == -ETIMEDOUT)
continue; /* try till finish */
return status;
} else if (!(status & SR_WIP)) {
return 0;
}
cond_resched();
} while (!time_after_eq(jiffies, finish));
dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n");
return status;
return -EBUSY;
}
/**
......@@ -335,6 +339,9 @@ static void spear_smi_hw_init(struct spear_smi *dev)
val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8);
mutex_lock(&dev->lock);
/* clear all interrupt conditions */
writel(0, dev->io_base + SMI_SR);
writel(val, dev->io_base + SMI_CR1);
mutex_unlock(&dev->lock);
}
......@@ -391,11 +398,11 @@ static int spear_smi_write_enable(struct spear_smi *dev, u32 bank)
writel(ctrlreg1, dev->io_base + SMI_CR1);
writel(0, dev->io_base + SMI_CR2);
if (ret <= 0) {
if (ret == 0) {
ret = -EIO;
dev_err(&dev->pdev->dev,
"smi controller failed on write enable\n");
} else {
} else if (ret > 0) {
/* check whether write mode status is set for required bank */
if (dev->status & (1 << (bank + WM_SHIFT)))
ret = 0;
......@@ -462,10 +469,10 @@ static int spear_smi_erase_sector(struct spear_smi *dev,
ret = wait_event_interruptible_timeout(dev->cmd_complete,
dev->status & TFF, SMI_CMD_TIMEOUT);
if (ret <= 0) {
if (ret == 0) {
ret = -EIO;
dev_err(&dev->pdev->dev, "sector erase failed\n");
} else
} else if (ret > 0)
ret = 0; /* success */
/* restore ctrl regs */
......@@ -820,7 +827,7 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
if (!flash_info)
return -ENODEV;
flash = kzalloc(sizeof(*flash), GFP_ATOMIC);
flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_ATOMIC);
if (!flash)
return -ENOMEM;
flash->bank = bank;
......@@ -831,15 +838,13 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
flash_index = spear_smi_probe_flash(dev, bank);
if (flash_index < 0) {
dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank);
ret = flash_index;
goto err_probe;
return flash_index;
}
/* map the memory for nor flash chip */
flash->base_addr = ioremap(flash_info->mem_base, flash_info->size);
if (!flash->base_addr) {
ret = -EIO;
goto err_probe;
}
flash->base_addr = devm_ioremap(&pdev->dev, flash_info->mem_base,
flash_info->size);
if (!flash->base_addr)
return -EIO;
dev->flash[bank] = flash;
flash->mtd.priv = dev;
......@@ -881,17 +886,10 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
count);
if (ret) {
dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret);
goto err_map;
return ret;
}
return 0;
err_map:
iounmap(flash->base_addr);
err_probe:
kfree(flash);
return ret;
}
/**
......@@ -928,20 +926,13 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
}
} else {
pdata = dev_get_platdata(&pdev->dev);
if (pdata < 0) {
if (!pdata) {
ret = -ENODEV;
dev_err(&pdev->dev, "no platform data\n");
goto err;
}
}
smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!smi_base) {
ret = -ENODEV;
dev_err(&pdev->dev, "invalid smi base address\n");
goto err;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = -ENODEV;
......@@ -949,32 +940,26 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
goto err;
}
dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
if (!dev) {
ret = -ENOMEM;
dev_err(&pdev->dev, "mem alloc fail\n");
goto err;
}
smi_base = request_mem_region(smi_base->start, resource_size(smi_base),
pdev->name);
if (!smi_base) {
ret = -EBUSY;
dev_err(&pdev->dev, "request mem region fail\n");
goto err_mem;
}
smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev->io_base = ioremap(smi_base->start, resource_size(smi_base));
dev->io_base = devm_request_and_ioremap(&pdev->dev, smi_base);
if (!dev->io_base) {
ret = -EIO;
dev_err(&pdev->dev, "ioremap fail\n");
goto err_ioremap;
dev_err(&pdev->dev, "devm_request_and_ioremap fail\n");
goto err;
}
dev->pdev = pdev;
dev->clk_rate = pdata->clk_rate;
if (dev->clk_rate < 0 || dev->clk_rate > SMI_MAX_CLOCK_FREQ)
if (dev->clk_rate > SMI_MAX_CLOCK_FREQ)
dev->clk_rate = SMI_MAX_CLOCK_FREQ;
dev->num_flashes = pdata->num_flashes;
......@@ -984,17 +969,18 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
dev->num_flashes = MAX_NUM_FLASH_CHIP;
}
dev->clk = clk_get(&pdev->dev, NULL);
dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = PTR_ERR(dev->clk);
goto err_clk;
goto err;
}
ret = clk_prepare_enable(dev->clk);
if (ret)
goto err_clk_prepare_enable;
goto err;
ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev);
ret = devm_request_irq(&pdev->dev, irq, spear_smi_int_handler, 0,
pdev->name, dev);
if (ret) {
dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n");
goto err_irq;
......@@ -1017,18 +1003,9 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
return 0;
err_bank_setup:
free_irq(irq, dev);
platform_set_drvdata(pdev, NULL);
err_irq:
clk_disable_unprepare(dev->clk);
err_clk_prepare_enable:
clk_put(dev->clk);
err_clk:
iounmap(dev->io_base);
err_ioremap:
release_mem_region(smi_base->start, resource_size(smi_base));
err_mem:
kfree(dev);
err:
return ret;
}
......@@ -1042,11 +1019,8 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
static int __devexit spear_smi_remove(struct platform_device *pdev)
{
struct spear_smi *dev;
struct spear_smi_plat_data *pdata;
struct spear_snor_flash *flash;
struct resource *smi_base;
int ret;
int i, irq;
int ret, i;
dev = platform_get_drvdata(pdev);
if (!dev) {
......@@ -1054,8 +1028,6 @@ static int __devexit spear_smi_remove(struct platform_device *pdev)
return -ENODEV;
}
pdata = dev_get_platdata(&pdev->dev);
/* clean up for all nor flash */
for (i = 0; i < dev->num_flashes; i++) {
flash = dev->flash[i];
......@@ -1066,49 +1038,41 @@ static int __devexit spear_smi_remove(struct platform_device *pdev)
ret = mtd_device_unregister(&flash->mtd);
if (ret)
dev_err(&pdev->dev, "error removing mtd\n");
iounmap(flash->base_addr);
kfree(flash);
}
irq = platform_get_irq(pdev, 0);
free_irq(irq, dev);
clk_disable_unprepare(dev->clk);
clk_put(dev->clk);
iounmap(dev->io_base);
kfree(dev);
smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(smi_base->start, resource_size(smi_base));
platform_set_drvdata(pdev, NULL);
return 0;
}
int spear_smi_suspend(struct platform_device *pdev, pm_message_t state)
#ifdef CONFIG_PM
static int spear_smi_suspend(struct device *dev)
{
struct spear_smi *dev = platform_get_drvdata(pdev);
struct spear_smi *sdev = dev_get_drvdata(dev);
if (dev && dev->clk)
clk_disable_unprepare(dev->clk);
if (sdev && sdev->clk)
clk_disable_unprepare(sdev->clk);
return 0;
}
int spear_smi_resume(struct platform_device *pdev)
static int spear_smi_resume(struct device *dev)
{
struct spear_smi *dev = platform_get_drvdata(pdev);
struct spear_smi *sdev = dev_get_drvdata(dev);
int ret = -EPERM;
if (dev && dev->clk)
ret = clk_prepare_enable(dev->clk);
if (sdev && sdev->clk)
ret = clk_prepare_enable(sdev->clk);
if (!ret)
spear_smi_hw_init(dev);
spear_smi_hw_init(sdev);
return ret;
}
static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume);
#endif
#ifdef CONFIG_OF
static const struct of_device_id spear_smi_id_table[] = {
{ .compatible = "st,spear600-smi" },
......@@ -1123,11 +1087,12 @@ static struct platform_driver spear_smi_driver = {
.bus = &platform_bus_type,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(spear_smi_id_table),
#ifdef CONFIG_PM
.pm = &spear_smi_pm_ops,
#endif
},
.probe = spear_smi_probe,
.remove = __devexit_p(spear_smi_remove),
.suspend = spear_smi_suspend,
.resume = spear_smi_resume,
};
static int spear_smi_init(void)
......
......@@ -373,7 +373,7 @@ config MTD_FORTUNET
have such a board, say 'Y'.
config MTD_AUTCPU12
tristate "NV-RAM mapping AUTCPU12 board"
bool "NV-RAM mapping AUTCPU12 board"
depends on ARCH_AUTCPU12
help
This enables access to the NV-RAM on autronix autcpu12 board.
......@@ -443,22 +443,10 @@ config MTD_GPIO_ADDR
config MTD_UCLINUX
bool "Generic uClinux RAM/ROM filesystem support"
depends on MTD_RAM=y && !MMU
depends on MTD_RAM=y && (!MMU || COLDFIRE)
help
Map driver to support image based filesystems for uClinux.
config MTD_WRSBC8260
tristate "Map driver for WindRiver PowerQUICC II MPC82xx board"
depends on (SBC82xx || SBC8560)
select MTD_MAP_BANK_WIDTH_4
select MTD_MAP_BANK_WIDTH_1
select MTD_CFI_I1
select MTD_CFI_I4
help
Map driver for WindRiver PowerQUICC II MPC82xx board. Drives
all three flash regions on CS0, CS1 and CS6 if they are configured
correctly by the boot loader.
config MTD_DMV182
tristate "Map driver for Dy-4 SVME/DMV-182 board."
depends on DMV182
......
......@@ -47,7 +47,6 @@ obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_H720X) += h720x-flash.o
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o
obj-$(CONFIG_MTD_DMV182) += dmv182.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
......
......@@ -15,43 +15,54 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/sizes.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/sizes.h>
#include <mach/hardware.h>
#include <mach/autcpu12.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
static struct mtd_info *sram_mtd;
struct map_info autcpu12_sram_map = {
.name = "SRAM",
.size = 32768,
.bankwidth = 4,
.phys = 0x12000000,
struct autcpu12_nvram_priv {
struct mtd_info *mtd;
struct map_info map;
};
static int __init init_autcpu12_sram (void)
static int __devinit autcpu12_nvram_probe(struct platform_device *pdev)
{
int err, save0, save1;
map_word tmp, save0, save1;
struct resource *res;
struct autcpu12_nvram_priv *priv;
autcpu12_sram_map.virt = ioremap(0x12000000, SZ_128K);
if (!autcpu12_sram_map.virt) {
printk("Failed to ioremap autcpu12 NV-RAM space\n");
err = -EIO;
goto out;
priv = devm_kzalloc(&pdev->dev,
sizeof(struct autcpu12_nvram_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get memory resource\n");
return -ENOENT;
}
priv->map.bankwidth = 4;
priv->map.phys = res->start;
priv->map.size = resource_size(res);
priv->map.virt = devm_request_and_ioremap(&pdev->dev, res);
strcpy((char *)priv->map.name, res->name);
if (!priv->map.virt) {
dev_err(&pdev->dev, "failed to remap mem resource\n");
return -EBUSY;
}
simple_map_init(&autcpu_sram_map);
simple_map_init(&priv->map);
/*
* Check for 32K/128K
......@@ -61,65 +72,59 @@ static int __init init_autcpu12_sram (void)
* Read and check result on ofs 0x0
* Restore contents
*/
save0 = map_read32(&autcpu12_sram_map,0);
save1 = map_read32(&autcpu12_sram_map,0x10000);
map_write32(&autcpu12_sram_map,~save0,0x10000);
/* if we find this pattern on 0x0, we have 32K size
* restore contents and exit
*/
if ( map_read32(&autcpu12_sram_map,0) != save0) {
map_write32(&autcpu12_sram_map,save0,0x0);
goto map;
save0 = map_read(&priv->map, 0);
save1 = map_read(&priv->map, 0x10000);
tmp.x[0] = ~save0.x[0];
map_write(&priv->map, tmp, 0x10000);
tmp = map_read(&priv->map, 0);
/* if we find this pattern on 0x0, we have 32K size */
if (!map_word_equal(&priv->map, tmp, save0)) {
map_write(&priv->map, save0, 0x0);
priv->map.size = SZ_32K;
} else
map_write(&priv->map, save1, 0x10000);
priv->mtd = do_map_probe("map_ram", &priv->map);
if (!priv->mtd) {
dev_err(&pdev->dev, "probing failed\n");
return -ENXIO;
}
/* We have a 128K found, restore 0x10000 and set size
* to 128K
*/
map_write32(&autcpu12_sram_map,save1,0x10000);
autcpu12_sram_map.size = SZ_128K;
map:
sram_mtd = do_map_probe("map_ram", &autcpu12_sram_map);
if (!sram_mtd) {
printk("NV-RAM probe failed\n");
err = -ENXIO;
goto out_ioremap;
}
sram_mtd->owner = THIS_MODULE;
sram_mtd->erasesize = 16;
if (mtd_device_register(sram_mtd, NULL, 0)) {
printk("NV-RAM device addition failed\n");
err = -ENOMEM;
goto out_probe;
priv->mtd->owner = THIS_MODULE;
priv->mtd->erasesize = 16;
priv->mtd->dev.parent = &pdev->dev;
if (!mtd_device_register(priv->mtd, NULL, 0)) {
dev_info(&pdev->dev,
"NV-RAM device size %ldKiB registered on AUTCPU12\n",
priv->map.size / SZ_1K);
return 0;
}
printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K);
return 0;
out_probe:
map_destroy(sram_mtd);
sram_mtd = 0;
out_ioremap:
iounmap((void *)autcpu12_sram_map.virt);
out:
return err;
map_destroy(priv->mtd);
dev_err(&pdev->dev, "NV-RAM device addition failed\n");
return -ENOMEM;
}
static void __exit cleanup_autcpu12_maps(void)
static int __devexit autcpu12_nvram_remove(struct platform_device *pdev)
{
if (sram_mtd) {
mtd_device_unregister(sram_mtd);
map_destroy(sram_mtd);
iounmap((void *)autcpu12_sram_map.virt);
}
struct autcpu12_nvram_priv *priv = platform_get_drvdata(pdev);
mtd_device_unregister(priv->mtd);
map_destroy(priv->mtd);
return 0;
}
module_init(init_autcpu12_sram);
module_exit(cleanup_autcpu12_maps);
static struct platform_driver autcpu12_nvram_driver = {
.driver = {
.name = "autcpu12_nvram",
.owner = THIS_MODULE,
},
.probe = autcpu12_nvram_probe,
.remove = __devexit_p(autcpu12_nvram_remove),
};
module_platform_driver(autcpu12_nvram_driver);
MODULE_AUTHOR("Thomas Gleixner");
MODULE_DESCRIPTION("autcpu12 NV-RAM map driver");
MODULE_DESCRIPTION("autcpu12 NVRAM map driver");
MODULE_LICENSE("GPL");
......@@ -43,26 +43,14 @@ static map_word mtd_pci_read8(struct map_info *_map, unsigned long ofs)
struct map_pci_info *map = (struct map_pci_info *)_map;
map_word val;
val.x[0]= readb(map->base + map->translate(map, ofs));
// printk("read8 : %08lx => %02x\n", ofs, val.x[0]);
return val;
}
#if 0
static map_word mtd_pci_read16(struct map_info *_map, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
map_word val;
val.x[0] = readw(map->base + map->translate(map, ofs));
// printk("read16: %08lx => %04x\n", ofs, val.x[0]);
return val;
}
#endif
static map_word mtd_pci_read32(struct map_info *_map, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
map_word val;
val.x[0] = readl(map->base + map->translate(map, ofs));
// printk("read32: %08lx => %08x\n", ofs, val.x[0]);
return val;
}
......@@ -75,22 +63,12 @@ static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from
static void mtd_pci_write8(struct map_info *_map, map_word val, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
// printk("write8 : %08lx <= %02x\n", ofs, val.x[0]);
writeb(val.x[0], map->base + map->translate(map, ofs));
}
#if 0
static void mtd_pci_write16(struct map_info *_map, map_word val, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
// printk("write16: %08lx <= %04x\n", ofs, val.x[0]);
writew(val.x[0], map->base + map->translate(map, ofs));
}
#endif
static void mtd_pci_write32(struct map_info *_map, map_word val, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
// printk("write32: %08lx <= %08x\n", ofs, val.x[0]);
writel(val.x[0], map->base + map->translate(map, ofs));
}
......@@ -358,4 +336,3 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("Generic PCI map driver");
MODULE_DEVICE_TABLE(pci, mtd_pci_ids);
......@@ -169,6 +169,7 @@ static int __devinit of_flash_probe(struct platform_device *dev)
struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
struct mtd_part_parser_data ppdata;
bool map_indirect;
match = of_match_device(of_flash_match, &dev->dev);
if (!match)
......@@ -192,6 +193,8 @@ static int __devinit of_flash_probe(struct platform_device *dev)
}
count /= reg_tuple_size;
map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
err = -ENOMEM;
info = kzalloc(sizeof(struct of_flash) +
sizeof(struct of_flash_list) * count, GFP_KERNEL);
......@@ -247,6 +250,17 @@ static int __devinit of_flash_probe(struct platform_device *dev)
simple_map_init(&info->list[i].map);
/*
* On some platforms (e.g. MPC5200) a direct 1:1 mapping
* may cause problems with JFFS2 usage, as the local bus (LPB)
* doesn't support unaligned accesses as implemented in the
* JFFS2 code via memcpy(). By setting NO_XIP, the
* flash will not be exposed directly to the MTD users
* (e.g. JFFS2) any more.
*/
if (map_indirect)
info->list[i].map.phys = NO_XIP;
if (probe_type) {
info->list[i].mtd = do_map_probe(probe_type,
&info->list[i].map);
......
......@@ -100,8 +100,6 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
goto err_out;
}
info->mtd->owner = THIS_MODULE;
if (err)
goto err_out;
err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
pdata->nr_parts);
......
......@@ -67,10 +67,16 @@ static int __init uclinux_mtd_init(void)
printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",
(int) mapp->phys, (int) mapp->size);
mapp->virt = ioremap_nocache(mapp->phys, mapp->size);
/*
* The filesystem is guaranteed to be in direct mapped memory. It is
* directly following the kernels own bss region. Following the same
* mechanism used by architectures setting up traditional initrds we
* use phys_to_virt to get the virtual address of its start.
*/
mapp->virt = phys_to_virt(mapp->phys);
if (mapp->virt == 0) {
printk("uclinux[mtd]: ioremap_nocache() failed\n");
printk("uclinux[mtd]: no virtual mapping?\n");
return(-EIO);
}
......@@ -79,7 +85,6 @@ static int __init uclinux_mtd_init(void)
mtd = do_map_probe("map_ram", mapp);
if (!mtd) {
printk("uclinux[mtd]: failed to find a mapping?\n");
iounmap(mapp->virt);
return(-ENXIO);
}
......@@ -102,10 +107,8 @@ static void __exit uclinux_mtd_cleanup(void)
map_destroy(uclinux_ram_mtdinfo);
uclinux_ram_mtdinfo = NULL;
}
if (uclinux_ram_map.virt) {
iounmap((void *) uclinux_ram_map.virt);
if (uclinux_ram_map.virt)
uclinux_ram_map.virt = 0;
}
}
/****************************************************************************/
......
/*
* Map for flash chips on Wind River PowerQUICC II SBC82xx board.
*
* Copyright (C) 2004 Red Hat, Inc.
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/immap_cpm2.h>
static struct mtd_info *sbcmtd[3];
struct map_info sbc82xx_flash_map[3] = {
{.name = "Boot flash"},
{.name = "Alternate boot flash"},
{.name = "User flash"}
};
static struct mtd_partition smallflash_parts[] = {
{
.name = "space",
.size = 0x100000,
.offset = 0,
}, {
.name = "bootloader",
.size = MTDPART_SIZ_FULL,
.offset = MTDPART_OFS_APPEND,
}
};
static struct mtd_partition bigflash_parts[] = {
{
.name = "bootloader",
.size = 0x00100000,
.offset = 0,
}, {
.name = "file system",
.size = 0x01f00000,
.offset = MTDPART_OFS_APPEND,
}, {
.name = "boot config",
.size = 0x00100000,
.offset = MTDPART_OFS_APPEND,
}, {
.name = "space",
.size = 0x01f00000,
.offset = MTDPART_OFS_APPEND,
}
};
static const char *part_probes[] __initconst = {"cmdlinepart", "RedBoot", NULL};
#define init_sbc82xx_one_flash(map, br, or) \
do { \
(map).phys = (br & 1) ? (br & 0xffff8000) : 0; \
(map).size = (br & 1) ? (~(or & 0xffff8000) + 1) : 0; \
switch (br & 0x00001800) { \
case 0x00000000: \
case 0x00000800: (map).bankwidth = 1; break; \
case 0x00001000: (map).bankwidth = 2; break; \
case 0x00001800: (map).bankwidth = 4; break; \
} \
} while (0);
static int __init init_sbc82xx_flash(void)
{
volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl;
int bigflash;
int i;
#ifdef CONFIG_SBC8560
mc = ioremap(0xff700000 + 0x5000, sizeof(memctl_cpm2_t));
#else
mc = &cpm2_immr->im_memctl;
#endif
bigflash = 1;
if ((mc->memc_br0 & 0x00001800) == 0x00001800)
bigflash = 0;
init_sbc82xx_one_flash(sbc82xx_flash_map[0], mc->memc_br0, mc->memc_or0);
init_sbc82xx_one_flash(sbc82xx_flash_map[1], mc->memc_br6, mc->memc_or6);
init_sbc82xx_one_flash(sbc82xx_flash_map[2], mc->memc_br1, mc->memc_or1);
#ifdef CONFIG_SBC8560
iounmap((void *) mc);
#endif
for (i=0; i<3; i++) {
int8_t flashcs[3] = { 0, 6, 1 };
int nr_parts;
struct mtd_partition *defparts;
printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d",
sbc82xx_flash_map[i].name,
(sbc82xx_flash_map[i].size >> 20),
flashcs[i]);
if (!sbc82xx_flash_map[i].phys) {
/* We know it can't be at zero. */
printk("): disabled by bootloader.\n");
continue;
}
printk(" at %08lx)\n", sbc82xx_flash_map[i].phys);
sbc82xx_flash_map[i].virt = ioremap(sbc82xx_flash_map[i].phys,
sbc82xx_flash_map[i].size);
if (!sbc82xx_flash_map[i].virt) {
printk("Failed to ioremap\n");
continue;
}
simple_map_init(&sbc82xx_flash_map[i]);
sbcmtd[i] = do_map_probe("cfi_probe", &sbc82xx_flash_map[i]);
if (!sbcmtd[i])
continue;
sbcmtd[i]->owner = THIS_MODULE;
/* No partitioning detected. Use default */
if (i == 2) {
defparts = NULL;
nr_parts = 0;
} else if (i == bigflash) {
defparts = bigflash_parts;
nr_parts = ARRAY_SIZE(bigflash_parts);
} else {
defparts = smallflash_parts;
nr_parts = ARRAY_SIZE(smallflash_parts);
}
mtd_device_parse_register(sbcmtd[i], part_probes, NULL,
defparts, nr_parts);
}
return 0;
}
static void __exit cleanup_sbc82xx_flash(void)
{
int i;
for (i=0; i<3; i++) {
if (!sbcmtd[i])
continue;
mtd_device_unregister(sbcmtd[i]);
map_destroy(sbcmtd[i]);
iounmap((void *)sbc82xx_flash_map[i].virt);
sbc82xx_flash_map[i].virt = 0;
}
}
module_init(init_sbc82xx_flash);
module_exit(cleanup_sbc82xx_flash);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Flash map driver for WindRiver PowerQUICC II");
......@@ -1162,7 +1162,11 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
resource_size_t start, off;
unsigned long len, vma_len;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
/* This is broken because it assumes the MTD device is map-based
and that mtd->priv is a valid struct map_info. It should be
replaced with something that uses the mtd_get_unmapped_area()
operation properly. */
if (0 /*mtd->type == MTD_RAM || mtd->type == MTD_ROM*/) {
off = get_vm_offset(vma);
start = map->phys;
len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
......
......@@ -858,6 +858,27 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
}
EXPORT_SYMBOL_GPL(mtd_panic_write);
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
{
int ret_code;
ops->retlen = ops->oobretlen = 0;
if (!mtd->_read_oob)
return -EOPNOTSUPP;
/*
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
* similar to mtd->_read(), returning a non-negative integer
* representing max bitflips. In other cases, mtd->_read_oob() may
* return -EUCLEAN. In all cases, perform similar logic to mtd_read().
*/
ret_code = mtd->_read_oob(mtd, from, ops);
if (unlikely(ret_code < 0))
return ret_code;
if (mtd->ecc_strength == 0)
return 0; /* device lacks ecc */
return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
}
EXPORT_SYMBOL_GPL(mtd_read_oob);
/*
* Method to access the protection register area, present in some flash
* devices. The user data is one time programmable but the factory data is read
......
......@@ -169,14 +169,7 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
cxt->nextpage = 0;
}
while (1) {
ret = mtd_block_isbad(mtd, cxt->nextpage * record_size);
if (!ret)
break;
if (ret < 0) {
printk(KERN_ERR "mtdoops: block_isbad failed, aborting\n");
return;
}
while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) {
badblock:
printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
cxt->nextpage * record_size);
......@@ -190,6 +183,11 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
}
}
if (ret < 0) {
printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n");
return;
}
for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
......
......@@ -711,6 +711,8 @@ static const char *default_mtd_part_types[] = {
* partition parsers, specified in @types. However, if @types is %NULL, then
* the default list of parsers is used. The default list contains only the
* "cmdlinepart" and "ofpart" parsers ATM.
* Note: If there are more then one parser in @types, the kernel only takes the
* partitions parsed out by the first parser.
*
* This function may return:
* o a negative error code in case of failure
......@@ -735,11 +737,12 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
if (!parser)
continue;
ret = (*parser->parse_fn)(master, pparts, data);
put_partition_parser(parser);
if (ret > 0) {
printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
ret, parser->name, master->name);
break;
}
put_partition_parser(parser);
}
return ret;
}
......
......@@ -22,15 +22,6 @@ menuconfig MTD_NAND
if MTD_NAND
config MTD_NAND_VERIFY_WRITE
bool "Verify NAND page writes"
help
This adds an extra check when data is written to the flash. The
NAND flash device internally checks only bits transitioning
from 1 to 0. There is a rare possibility that even though the
device thinks the write was successful, a bit could have been
flipped accidentally due to device wear or something else.
config MTD_NAND_BCH
tristate
select BCH
......@@ -267,22 +258,6 @@ config MTD_NAND_S3C2410_CLKSTOP
when the is NAND chip selected or released, but will save
approximately 5mA of power when there is nothing happening.
config MTD_NAND_BCM_UMI
tristate "NAND Flash support for BCM Reference Boards"
depends on ARCH_BCMRING
help
This enables the NAND flash controller on the BCM UMI block.
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_BCM_UMI_HWCS
bool "BCM UMI NAND Hardware CS"
depends on MTD_NAND_BCM_UMI
help
Enable the use of the BCM UMI block's internal CS using NAND.
This should only be used if you know the external NAND CS can toggle.
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
depends on EXPERIMENTAL
......@@ -356,7 +331,7 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
config MTD_NAND_DOCG4
tristate "Support for DiskOnChip G4 (EXPERIMENTAL)"
depends on EXPERIMENTAL
depends on EXPERIMENTAL && HAS_IOMEM
select BCH
select BITREVERSE
help
......@@ -414,6 +389,28 @@ config MTD_NAND_PXA3xx
This enables the driver for the NAND flash device found on
PXA3xx processors
config MTD_NAND_SLC_LPC32XX
tristate "NXP LPC32xx SLC Controller"
depends on ARCH_LPC32XX
help
Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
chips) NAND controller. This is the default for the PHYTEC 3250
reference board which contains a NAND256R3A2CZA6 chip.
Please check the actual NAND chip connected and its support
by the SLC NAND controller.
config MTD_NAND_MLC_LPC32XX
tristate "NXP LPC32xx MLC Controller"
depends on ARCH_LPC32XX
help
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
controller. This is the default for the WORK92105 controller
board.
Please check the actual NAND chip connected and its support
by the MLC NAND controller.
config MTD_NAND_CM_X270
tristate "Support for NAND Flash on CM-X270 modules"
depends on MACH_ARMCORE
......@@ -439,10 +436,10 @@ config MTD_NAND_NANDSIM
MTD nand layer.
config MTD_NAND_GPMI_NAND
bool "GPMI NAND Flash Controller driver"
tristate "GPMI NAND Flash Controller driver"
depends on MTD_NAND && MXS_DMA
help
Enables NAND Flash support for IMX23 or IMX28.
Enables NAND Flash support for IMX23, IMX28 or IMX6.
The GPMI controller is very powerful, with the help of BCH
module, it can do the hardware ECC. The GPMI supports several
NAND flashs at the same time. The GPMI may conflicts with other
......@@ -510,7 +507,7 @@ config MTD_NAND_MPC5121_NFC
config MTD_NAND_MXC
tristate "MXC NAND support"
depends on IMX_HAVE_PLATFORM_MXC_NAND
depends on ARCH_MXC
help
This enables the driver for the NAND flash controller on the
MXC processors.
......@@ -567,4 +564,12 @@ config MTD_NAND_FSMC
Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC)
config MTD_NAND_XWAY
tristate "Support for NAND on Lantiq XWAY SoC"
depends on LANTIQ && SOC_TYPE_XWAY
select MTD_NAND_PLATFORM
help
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
to the External Bus Unit (EBU).
endif # MTD_NAND
......@@ -40,16 +40,18 @@ obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o
obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
nand-objs := nand_base.o nand_bbt.o
......@@ -107,18 +107,6 @@ static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
buf[i] = ams_delta_read_byte(mtd);
}
static int ams_delta_verify_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
int i;
for (i=0; i<len; i++)
if (buf[i] != ams_delta_read_byte(mtd))
return -EFAULT;
return 0;
}
/*
* Command control function
*
......@@ -237,7 +225,6 @@ static int __devinit ams_delta_init(struct platform_device *pdev)
this->read_byte = ams_delta_read_byte;
this->write_buf = ams_delta_write_buf;
this->read_buf = ams_delta_read_buf;
this->verify_buf = ams_delta_verify_buf;
this->cmd_ctrl = ams_delta_hwcontrol;
if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) {
this->dev_ready = ams_delta_nand_ready;
......
This diff is collapsed.
......@@ -3,7 +3,7 @@
* Based on AT91SAM9260 datasheet revision B.
*
* Copyright (C) 2007 Andrew Victor
* Copyright (C) 2007 Atmel Corporation.
* Copyright (C) 2007 - 2012 Atmel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
......@@ -36,4 +36,116 @@
#define ATMEL_ECC_NPR 0x10 /* NParity register */
#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
/* PMECC Register Definitions */
#define ATMEL_PMECC_CFG 0x000 /* Configuration Register */
#define PMECC_CFG_BCH_ERR2 (0 << 0)
#define PMECC_CFG_BCH_ERR4 (1 << 0)
#define PMECC_CFG_BCH_ERR8 (2 << 0)
#define PMECC_CFG_BCH_ERR12 (3 << 0)
#define PMECC_CFG_BCH_ERR24 (4 << 0)
#define PMECC_CFG_SECTOR512 (0 << 4)
#define PMECC_CFG_SECTOR1024 (1 << 4)
#define PMECC_CFG_PAGE_1SECTOR (0 << 8)
#define PMECC_CFG_PAGE_2SECTORS (1 << 8)
#define PMECC_CFG_PAGE_4SECTORS (2 << 8)
#define PMECC_CFG_PAGE_8SECTORS (3 << 8)
#define PMECC_CFG_READ_OP (0 << 12)
#define PMECC_CFG_WRITE_OP (1 << 12)
#define PMECC_CFG_SPARE_ENABLE (1 << 16)
#define PMECC_CFG_SPARE_DISABLE (0 << 16)
#define PMECC_CFG_AUTO_ENABLE (1 << 20)
#define PMECC_CFG_AUTO_DISABLE (0 << 20)
#define ATMEL_PMECC_SAREA 0x004 /* Spare area size */
#define ATMEL_PMECC_SADDR 0x008 /* PMECC starting address */
#define ATMEL_PMECC_EADDR 0x00c /* PMECC ending address */
#define ATMEL_PMECC_CLK 0x010 /* PMECC clock control */
#define PMECC_CLK_133MHZ (2 << 0)
#define ATMEL_PMECC_CTRL 0x014 /* PMECC control register */
#define PMECC_CTRL_RST (1 << 0)
#define PMECC_CTRL_DATA (1 << 1)
#define PMECC_CTRL_USER (1 << 2)
#define PMECC_CTRL_ENABLE (1 << 4)
#define PMECC_CTRL_DISABLE (1 << 5)
#define ATMEL_PMECC_SR 0x018 /* PMECC status register */
#define PMECC_SR_BUSY (1 << 0)
#define PMECC_SR_ENABLE (1 << 4)
#define ATMEL_PMECC_IER 0x01c /* PMECC interrupt enable */
#define PMECC_IER_ENABLE (1 << 0)
#define ATMEL_PMECC_IDR 0x020 /* PMECC interrupt disable */
#define PMECC_IER_DISABLE (1 << 0)
#define ATMEL_PMECC_IMR 0x024 /* PMECC interrupt mask */
#define PMECC_IER_MASK (1 << 0)
#define ATMEL_PMECC_ISR 0x028 /* PMECC interrupt status */
#define ATMEL_PMECC_ECCx 0x040 /* PMECC ECC x */
#define ATMEL_PMECC_REMx 0x240 /* PMECC REM x */
/* PMERRLOC Register Definitions */
#define ATMEL_PMERRLOC_ELCFG 0x000 /* Error location config */
#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
#define ATMEL_PMERRLOC_ELPRIM 0x004 /* Error location primitive */
#define ATMEL_PMERRLOC_ELEN 0x008 /* Error location enable */
#define ATMEL_PMERRLOC_ELDIS 0x00c /* Error location disable */
#define PMERRLOC_DISABLE (1 << 0)
#define ATMEL_PMERRLOC_ELSR 0x010 /* Error location status */
#define PMERRLOC_ELSR_BUSY (1 << 0)
#define ATMEL_PMERRLOC_ELIER 0x014 /* Error location int enable */
#define ATMEL_PMERRLOC_ELIDR 0x018 /* Error location int disable */
#define ATMEL_PMERRLOC_ELIMR 0x01c /* Error location int mask */
#define ATMEL_PMERRLOC_ELISR 0x020 /* Error location int status */
#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
#define PMERRLOC_CALC_DONE (1 << 0)
#define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */
#define ATMEL_PMERRLOC_ELx 0x08c /* Error location x */
/* Register access macros for PMECC */
#define pmecc_readl_relaxed(addr, reg) \
readl_relaxed((addr) + ATMEL_PMECC_##reg)
#define pmecc_writel(addr, reg, value) \
writel((value), (addr) + ATMEL_PMECC_##reg)
#define pmecc_readb_ecc_relaxed(addr, sector, n) \
readb_relaxed((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n))
#define pmecc_readl_rem_relaxed(addr, sector, n) \
readl_relaxed((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4))
#define pmerrloc_readl_relaxed(addr, reg) \
readl_relaxed((addr) + ATMEL_PMERRLOC_##reg)
#define pmerrloc_writel(addr, reg, value) \
writel((value), (addr) + ATMEL_PMERRLOC_##reg)
#define pmerrloc_writel_sigma_relaxed(addr, n, value) \
writel_relaxed((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
#define pmerrloc_readl_sigma_relaxed(addr, n) \
readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
#define pmerrloc_readl_el_relaxed(addr, n) \
readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4))
/* Galois field dimension */
#define PMECC_GF_DIMENSION_13 13
#define PMECC_GF_DIMENSION_14 14
#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
/* Time out value for reading PMECC status register */
#define PMECC_MAX_TIMEOUT_MS 100
#endif
......@@ -140,28 +140,6 @@ static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
}
}
/**
* au_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
* verify function for 8bit buswidth
*/
static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++) {
if (buf[i] != readb(this->IO_ADDR_R))
return -EFAULT;
au_sync();
}
return 0;
}
/**
* au_write_buf16 - write buffer to chip
* @mtd: MTD device structure
......@@ -205,29 +183,6 @@ static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
}
}
/**
* au_verify_buf16 - Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
* verify function for 16bit buswidth
*/
static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i = 0; i < len; i++) {
if (p[i] != readw(this->IO_ADDR_R))
return -EFAULT;
au_sync();
}
return 0;
}
/* Select the chip by setting nCE to low */
#define NAND_CTL_SETNCE 1
/* Deselect the chip by setting nCE to high */
......@@ -516,7 +471,6 @@ static int __devinit au1550nd_probe(struct platform_device *pdev)
this->read_word = au_read_word;
this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf;
this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf;
this->verify_buf = (pd->devwidth) ? au_verify_buf16 : au_verify_buf;
ret = nand_scan(&ctx->info, 1);
if (ret) {
......
/*****************************************************************************
* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2, available at
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a
* license other than the GPL, without Broadcom's express prior written
* consent.
*****************************************************************************/
/* ---- Include Files ---------------------------------------------------- */
#include "nand_bcm_umi.h"
/* ---- External Variable Declarations ----------------------------------- */
/* ---- External Function Prototypes ------------------------------------- */
/* ---- Public Variables ------------------------------------------------- */
/* ---- Private Constants and Types -------------------------------------- */
/* ---- Private Function Prototypes -------------------------------------- */
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required);
/* ---- Private Variables ------------------------------------------------ */
/*
** nand_hw_eccoob
** New oob placement block for use with hardware ecc generation.
*/
static struct nand_ecclayout nand_hw_eccoob_512 = {
/* Reserve 5 for BI indicator */
.oobfree = {
#if (NAND_ECC_NUM_BYTES > 3)
{.offset = 0, .length = 2}
#else
{.offset = 0, .length = 5},
{.offset = 6, .length = 7}
#endif
}
};
/*
** We treat the OOB for a 2K page as if it were 4 512 byte oobs,
** except the BI is at byte 0.
*/
static struct nand_ecclayout nand_hw_eccoob_2048 = {
/* Reserve 0 as BI indicator */
.oobfree = {
#if (NAND_ECC_NUM_BYTES > 10)
{.offset = 1, .length = 2},
#elif (NAND_ECC_NUM_BYTES > 7)
{.offset = 1, .length = 5},
{.offset = 16, .length = 6},
{.offset = 32, .length = 6},
{.offset = 48, .length = 6}
#else
{.offset = 1, .length = 8},
{.offset = 16, .length = 9},
{.offset = 32, .length = 9},
{.offset = 48, .length = 9}
#endif
}
};
/* We treat the OOB for a 4K page as if it were 8 512 byte oobs,
* except the BI is at byte 0. */
static struct nand_ecclayout nand_hw_eccoob_4096 = {
/* Reserve 0 as BI indicator */
.oobfree = {
#if (NAND_ECC_NUM_BYTES > 10)
{.offset = 1, .length = 2},
{.offset = 16, .length = 3},
{.offset = 32, .length = 3},
{.offset = 48, .length = 3},
{.offset = 64, .length = 3},
{.offset = 80, .length = 3},
{.offset = 96, .length = 3},
{.offset = 112, .length = 3}
#else
{.offset = 1, .length = 5},
{.offset = 16, .length = 6},
{.offset = 32, .length = 6},
{.offset = 48, .length = 6},
{.offset = 64, .length = 6},
{.offset = 80, .length = 6},
{.offset = 96, .length = 6},
{.offset = 112, .length = 6}
#endif
}
};
/* ---- Private Functions ------------------------------------------------ */
/* ==== Public Functions ================================================= */
/****************************************************************************
*
* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
* @oob_required: caller expects OOB data read to chip->oob_poi
*
***************************************************************************/
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t * buf,
int oob_required, int page)
{
int sectorIdx = 0;
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps;
uint8_t *datap = buf;
uint8_t eccCalc[NAND_ECC_NUM_BYTES];
int sectorOobSize = mtd->oobsize / eccsteps;
int stat;
unsigned int max_bitflips = 0;
for (sectorIdx = 0; sectorIdx < eccsteps;
sectorIdx++, datap += eccsize) {
if (sectorIdx > 0) {
/* Seek to page location within sector */
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize,
-1);
}
/* Enable hardware ECC before reading the buf */
nand_bcm_umi_bch_enable_read_hwecc();
/* Read in data */
bcm_umi_nand_read_buf(mtd, datap, eccsize);
/* Pause hardware ECC after reading the buf */
nand_bcm_umi_bch_pause_read_ecc_calc();
/* Read the OOB ECC */
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + sectorIdx * sectorOobSize, -1);
nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc,
NAND_ECC_NUM_BYTES,
chip->oob_poi +
sectorIdx * sectorOobSize);
/* Correct any ECC detected errors */
stat =
nand_bcm_umi_bch_correct_page(datap, eccCalc,
NAND_ECC_NUM_BYTES);
/* Update Stats */
if (stat < 0) {
#if defined(NAND_BCM_UMI_DEBUG)
printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n",
__func__, sectorIdx);
printk(KERN_WARNING
"%s data %02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
__func__, datap[0], datap[1], datap[2], datap[3],
datap[4], datap[5], datap[6], datap[7]);
printk(KERN_WARNING
"%s ecc %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x\n",
__func__, eccCalc[0], eccCalc[1], eccCalc[2],
eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6],
eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10],
eccCalc[11], eccCalc[12]);
BUG();
#endif
mtd->ecc_stats.failed++;
} else {
#if defined(NAND_BCM_UMI_DEBUG)
if (stat > 0) {
printk(KERN_INFO
"%s %d correctable_errors detected\n",
__func__, stat);
}
#endif
mtd->ecc_stats.corrected += stat;
max_bitflips = max_t(unsigned int, max_bitflips, stat);
}
}
return max_bitflips;
}
/****************************************************************************
*
* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
*
***************************************************************************/
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
int sectorIdx = 0;
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps;
const uint8_t *datap = buf;
uint8_t *oobp = chip->oob_poi;
int sectorOobSize = mtd->oobsize / eccsteps;
for (sectorIdx = 0; sectorIdx < eccsteps;
sectorIdx++, datap += eccsize, oobp += sectorOobSize) {
/* Enable hardware ECC before writing the buf */
nand_bcm_umi_bch_enable_write_hwecc();
bcm_umi_nand_write_buf(mtd, datap, eccsize);
nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp,
NAND_ECC_NUM_BYTES);
}
bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
}
This diff is collapsed.
......@@ -566,11 +566,13 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip
return 0;
}
static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
bf5xx_nand_write_buf(mtd, buf, mtd->writesize);
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
/*
......
......@@ -377,7 +377,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
* @buf: buffer to store read data
* @oob_required: caller expects OOB data read to chip->oob_poi
*
* The hw generator calculates the error syndrome automatically. Therefor
* The hw generator calculates the error syndrome automatically. Therefore
* we need a special oob layout and handling.
*/
static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
......@@ -520,7 +520,7 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
};
static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
......@@ -531,6 +531,8 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
/* Set up ECC autogeneration */
cafe->ctl2 |= (1<<30);
return 0;
}
static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
......@@ -542,9 +544,12 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
else
chip->ecc.write_page(mtd, chip, buf, oob_required);
status = chip->ecc.write_page(mtd, chip, buf, oob_required);
if (status < 0)
return status;
/*
* Cached progamming disabled for now, Not sure if its worth the
......@@ -571,13 +576,6 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
status = chip->waitfunc(mtd, chip);
}
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/* Send command to read back the data */
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
if (chip->verify_buf(mtd, buf, mtd->writesize))
return -EIO;
#endif
return 0;
}
......
......@@ -76,18 +76,6 @@ static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len)
*buf++ = readl(this->IO_ADDR_R) >> 16;
}
static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
if (buf[i] != (u_char)(readl(this->IO_ADDR_R) >> 16))
return -EFAULT;
return 0;
}
static inline void nand_cs_on(void)
{
gpio_set_value(GPIO_NAND_CS, 0);
......@@ -209,7 +197,6 @@ static int __init cmx270_init(void)
this->read_byte = cmx270_read_byte;
this->read_buf = cmx270_read_buf;
this->write_buf = cmx270_write_buf;
this->verify_buf = cmx270_verify_buf;
/* Scan to find existence of the device */
if (nand_scan (cmx270_nand_mtd, 1)) {
......
......@@ -33,6 +33,7 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/platform_data/mtd-davinci.h>
#include <linux/platform_data/mtd-davinci-aemif.h>
......@@ -518,9 +519,75 @@ static struct nand_ecclayout hwecc4_2048 __initconst = {
},
};
#if defined(CONFIG_OF)
static const struct of_device_id davinci_nand_of_match[] = {
{.compatible = "ti,davinci-nand", },
{},
}
MODULE_DEVICE_TABLE(of, davinci_nand_of_match);
static struct davinci_nand_pdata
*nand_davinci_get_pdata(struct platform_device *pdev)
{
if (!pdev->dev.platform_data && pdev->dev.of_node) {
struct davinci_nand_pdata *pdata;
const char *mode;
u32 prop;
int len;
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct davinci_nand_pdata),
GFP_KERNEL);
pdev->dev.platform_data = pdata;
if (!pdata)
return NULL;
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-chipselect", &prop))
pdev->id = prop;
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-mask-ale", &prop))
pdata->mask_ale = prop;
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-mask-cle", &prop))
pdata->mask_cle = prop;
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-mask-chipsel", &prop))
pdata->mask_chipsel = prop;
if (!of_property_read_string(pdev->dev.of_node,
"ti,davinci-ecc-mode", &mode)) {
if (!strncmp("none", mode, 4))
pdata->ecc_mode = NAND_ECC_NONE;
if (!strncmp("soft", mode, 4))
pdata->ecc_mode = NAND_ECC_SOFT;
if (!strncmp("hw", mode, 2))
pdata->ecc_mode = NAND_ECC_HW;
}
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-ecc-bits", &prop))
pdata->ecc_bits = prop;
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-nand-buswidth", &prop))
if (prop == 16)
pdata->options |= NAND_BUSWIDTH_16;
if (of_find_property(pdev->dev.of_node,
"ti,davinci-nand-use-bbt", &len))
pdata->bbt_options = NAND_BBT_USE_FLASH;
}
return pdev->dev.platform_data;
}
#else
#define davinci_nand_of_match NULL
static struct davinci_nand_pdata
*nand_davinci_get_pdata(struct platform_device *pdev)
{
return pdev->dev.platform_data;
}
#endif
static int __init nand_davinci_probe(struct platform_device *pdev)
{
struct davinci_nand_pdata *pdata = pdev->dev.platform_data;
struct davinci_nand_pdata *pdata;
struct davinci_nand_info *info;
struct resource *res1;
struct resource *res2;
......@@ -530,6 +597,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
uint32_t val;
nand_ecc_modes_t ecc_mode;
pdata = nand_davinci_get_pdata(pdev);
/* insist on board-specific configuration */
if (!pdata)
return -ENODEV;
......@@ -656,7 +724,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
goto err_clk;
}
ret = clk_enable(info->clk);
ret = clk_prepare_enable(info->clk);
if (ret < 0) {
dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
ret);
......@@ -767,7 +835,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
err_scan:
err_timing:
clk_disable(info->clk);
clk_disable_unprepare(info->clk);
err_clk_enable:
clk_put(info->clk);
......@@ -804,7 +872,7 @@ static int __exit nand_davinci_remove(struct platform_device *pdev)
nand_release(&info->mtd);
clk_disable(info->clk);
clk_disable_unprepare(info->clk);
clk_put(info->clk);
kfree(info);
......@@ -816,6 +884,8 @@ static struct platform_driver nand_davinci_driver = {
.remove = __exit_p(nand_davinci_remove),
.driver = {
.name = "davinci_nand",
.owner = THIS_MODULE,
.of_match_table = davinci_nand_of_match,
},
};
MODULE_ALIAS("platform:davinci_nand");
......
......@@ -1028,7 +1028,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
/* writes a page. user specifies type, and this function handles the
* configuration details. */
static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, bool raw_xfer)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
......@@ -1078,6 +1078,8 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
denali_enable_dma(denali, false);
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
return 0;
}
/* NAND core entry points */
......@@ -1086,24 +1088,24 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
* writing a page with ECC or without is similar, all the work is done
* by write_page above.
* */
static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
/* for regular page writes, we let HW handle all the ECC
* data written to the device. */
write_page(mtd, chip, buf, false);
return write_page(mtd, chip, buf, false);
}
/* This is the callback that the NAND core calls to write a page without ECC.
* raw access is similar to ECC page writes, so all the work is done in the
* write_page() function above.
*/
static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
/* for raw page writes, we want to disable ECC and simply write
whatever data is in the buffer. */
write_page(mtd, chip, buf, true);
return write_page(mtd, chip, buf, true);
}
static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
......
......@@ -376,19 +376,6 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len)
}
}
static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr;
int i;
for (i = 0; i < len; i++)
if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
return -EFAULT;
return 0;
}
static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
{
struct nand_chip *this = mtd->priv;
......@@ -526,26 +513,6 @@ static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)
buf[i] = ReadDOC(docptr, LastDataRead);
}
static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr;
int i;
/* Start read pipeline */
ReadDOC(docptr, ReadPipeInit);
for (i = 0; i < len - 1; i++)
if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
ReadDOC(docptr, LastDataRead);
return i;
}
if (buf[i] != ReadDOC(docptr, LastDataRead))
return i;
return 0;
}
static u_char doc2001plus_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
......@@ -610,33 +577,6 @@ static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)
printk("\n");
}
static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr;
int i;
if (debug)
printk("verifybuf of %d bytes: ", len);
/* Start read pipeline */
ReadDOC(docptr, Mplus_ReadPipeInit);
ReadDOC(docptr, Mplus_ReadPipeInit);
for (i = 0; i < len - 2; i++)
if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
ReadDOC(docptr, Mplus_LastDataRead);
ReadDOC(docptr, Mplus_LastDataRead);
return i;
}
if (buf[len - 2] != ReadDOC(docptr, Mplus_LastDataRead))
return len - 2;
if (buf[len - 1] != ReadDOC(docptr, Mplus_LastDataRead))
return len - 1;
return 0;
}
static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
......@@ -1432,7 +1372,6 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
this->read_byte = doc2000_read_byte;
this->write_buf = doc2000_writebuf;
this->read_buf = doc2000_readbuf;
this->verify_buf = doc2000_verifybuf;
this->scan_bbt = nftl_scan_bbt;
doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
......@@ -1449,7 +1388,6 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
this->read_byte = doc2001_read_byte;
this->write_buf = doc2001_writebuf;
this->read_buf = doc2001_readbuf;
this->verify_buf = doc2001_verifybuf;
ReadDOC(doc->virtadr, ChipID);
ReadDOC(doc->virtadr, ChipID);
......@@ -1480,7 +1418,6 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
this->read_byte = doc2001plus_read_byte;
this->write_buf = doc2001plus_writebuf;
this->read_buf = doc2001plus_readbuf;
this->verify_buf = doc2001plus_verifybuf;
this->scan_bbt = inftl_scan_bbt;
this->cmd_ctrl = NULL;
this->select_chip = doc2001plus_select_chip;
......
......@@ -378,9 +378,9 @@ static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
* bit flips(s) are not reported in stats.
*/
if (doc->oob_buf[15]) {
if (nand->oob_poi[15]) {
int bit, numsetbits = 0;
unsigned long written_flag = doc->oob_buf[15];
unsigned long written_flag = nand->oob_poi[15];
for_each_set_bit(bit, &written_flag, 8)
numsetbits++;
if (numsetbits > 4) { /* assume blank */
......@@ -428,7 +428,7 @@ static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
/* if error within oob area preceeding ecc bytes... */
if (errpos[i] > DOCG4_PAGE_SIZE * 8)
change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8,
(unsigned long *)doc->oob_buf);
(unsigned long *)nand->oob_poi);
else /* error in page data */
change_bit(errpos[i], (unsigned long *)buf);
......@@ -748,18 +748,12 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */
/*
* Diskonchips read oob immediately after a page read. Mtd
* infrastructure issues a separate command for reading oob after the
* page is read. So we save the oob bytes in a local buffer and just
* copy it if the next command reads oob from the same page.
*/
/* this device always reads oob after page data */
/* first 14 oob bytes read from I/O reg */
docg4_read_buf(mtd, doc->oob_buf, 14);
docg4_read_buf(mtd, nand->oob_poi, 14);
/* last 2 read from another reg */
buf16 = (uint16_t *)(doc->oob_buf + 14);
buf16 = (uint16_t *)(nand->oob_poi + 14);
*buf16 = readw(docptr + DOCG4_MYSTERY_REG);
write_nop(docptr);
......@@ -782,6 +776,8 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
}
writew(0, docptr + DOC_DATAEND);
if (bits_corrected == -EBADMSG) /* uncorrectable errors */
return 0;
return bits_corrected;
}
......@@ -807,21 +803,6 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
dev_dbg(doc->dev, "%s: page %x\n", __func__, page);
/*
* Oob bytes are read as part of a normal page read. If the previous
* nand command was a read of the page whose oob is now being read, just
* copy the oob bytes that we saved in a local buffer and avoid a
* separate oob read.
*/
if (doc->last_command.command == NAND_CMD_READ0 &&
doc->last_command.page == page) {
memcpy(nand->oob_poi, doc->oob_buf, 16);
return 0;
}
/*
* Separate read of oob data only.
*/
docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page);
writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0);
......@@ -898,7 +879,7 @@ static void docg4_erase_block(struct mtd_info *mtd, int page)
write_nop(docptr);
}
static void write_page(struct mtd_info *mtd, struct nand_chip *nand,
static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
const uint8_t *buf, bool use_ecc)
{
struct docg4_priv *doc = nand->priv;
......@@ -950,15 +931,17 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *nand,
write_nop(docptr);
writew(0, docptr + DOC_DATAEND);
write_nop(docptr);
return 0;
}
static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
const uint8_t *buf, int oob_required)
{
return write_page(mtd, nand, buf, false);
}
static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
const uint8_t *buf, int oob_required)
{
return write_page(mtd, nand, buf, true);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -108,6 +108,15 @@
#define HW_GPMI_CTRL1_CLR 0x00000068
#define HW_GPMI_CTRL1_TOG 0x0000006c
#define BP_GPMI_CTRL1_WRN_DLY_SEL 22
#define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL)
#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \
(((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL)
#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0
#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1
#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2
#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3
#define BM_GPMI_CTRL1_BCH_MODE (1 << 18)
#define BP_GPMI_CTRL1_DLL_ENABLE 17
......@@ -154,6 +163,9 @@
#define HW_GPMI_TIMING1 0x00000080
#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16
#define BM_GPMI_TIMING1_BUSY_TIMEOUT (0xffff << BP_GPMI_TIMING1_BUSY_TIMEOUT)
#define BF_GPMI_TIMING1_BUSY_TIMEOUT(v) \
(((v) << BP_GPMI_TIMING1_BUSY_TIMEOUT) & BM_GPMI_TIMING1_BUSY_TIMEOUT)
#define HW_GPMI_TIMING2 0x00000090
#define HW_GPMI_DATA 0x000000a0
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -6,3 +6,4 @@ obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o
obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o
obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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