- 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)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iEYEABECAAYFAlB0OosACgkQdwG7hYl686OSPACeKLrlHmyG8KXgAqcGZwAj1RM+
 X9YAoI2Kd6Sz8v6sLbJidnxUBr/oJVa8
 =/kFV
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2012-10-10 10:51:35 +09:00
Родитель 72055425e5 f5cf8f0742
Коммит 10f39f04b2
119 изменённых файлов: 6368 добавлений и 3246 удалений

Просмотреть файл

@ -1216,8 +1216,6 @@ in this page</entry>
#define NAND_BBT_LASTBLOCK 0x00000010 #define NAND_BBT_LASTBLOCK 0x00000010
/* The bbt is at the given page, else we must scan for the bbt */ /* The bbt is at the given page, else we must scan for the bbt */
#define NAND_BBT_ABSPAGE 0x00000020 #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 */ /* bbt is stored per chip on multichip devices */
#define NAND_BBT_PERCHIP 0x00000080 #define NAND_BBT_PERCHIP 0x00000080
/* bbt has a version counter at offset veroffs */ /* bbt has a version counter at offset veroffs */

Просмотреть файл

@ -0,0 +1,51 @@
* 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: Required properties:
- compatible : "atmel,at91rm9200-nand". - compatible : "atmel,at91rm9200-nand".
- reg : should specify localbus address and size used for the chip, - 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-addr-offset : offset for the address latch.
- atmel,nand-cmd-offset : offset for the command latch. - atmel,nand-cmd-offset : offset for the command latch.
- #address-cells, #size-cells : Must be present if the device has sub-nodes - #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. - 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", Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
"soft_bch". "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-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 - 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"; - interrupt-names : The interrupt names "gpmi-dma", "bch";
- fsl,gpmi-dma-channel : Should contain the dma channel it uses. - 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 The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail. address space. See partition.txt for more detail.

Просмотреть файл

@ -0,0 +1,50 @@
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;
};
...
};

Просмотреть файл

@ -0,0 +1,52 @@
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 - #address-cells, #size-cells : Must be present if the device has
sub-nodes representing partitions (see below). In this case sub-nodes representing partitions (see below). In this case
both #address-cells and #size-cells must be equal to 1. 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 For JEDEC compatible devices, the following additional properties
are defined: are defined:

Просмотреть файл

@ -407,6 +407,13 @@
status = "disabled"; status = "disabled";
}; };
nand@83fdb000 {
compatible = "fsl,imx51-nand";
reg = <0x83fdb000 0x1000 0xcfff0000 0x10000>;
interrupts = <8>;
status = "disabled";
};
ssi3: ssi@83fe8000 { ssi3: ssi@83fe8000 {
compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; compatible = "fsl,imx51-ssi", "fsl,imx21-ssi";
reg = <0x83fe8000 0x4000>; reg = <0x83fe8000 0x4000>;

Просмотреть файл

@ -518,6 +518,13 @@
status = "disabled"; status = "disabled";
}; };
nand@63fdb000 {
compatible = "fsl,imx53-nand";
reg = <0x63fdb000 0x1000 0xf7ff0000 0x10000>;
interrupts = <8>;
status = "disabled";
};
ssi3: ssi@63fe8000 { ssi3: ssi@63fe8000 {
compatible = "fsl,imx53-ssi", "fsl,imx21-ssi"; compatible = "fsl,imx53-ssi", "fsl,imx21-ssi";
reg = <0x63fe8000 0x4000>; reg = <0x63fe8000 0x4000>;

Просмотреть файл

@ -49,7 +49,6 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_PLATRAM=m CONFIG_MTD_PLATRAM=m
CONFIG_MTD_DATAFLASH=y CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_ATMEL=y CONFIG_MTD_NAND_ATMEL=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM=y

Просмотреть файл

@ -97,7 +97,6 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_ROM=y CONFIG_MTD_ROM=y
CONFIG_MTD_COMPLEX_MAPPINGS=y CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_SHARPSL=y CONFIG_MTD_NAND_SHARPSL=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y
CONFIG_IDE=y CONFIG_IDE=y

Просмотреть файл

@ -61,7 +61,6 @@ CONFIG_MTD_CFI_STAA=y
CONFIG_MTD_ROM=y CONFIG_MTD_ROM=y
CONFIG_MTD_PHYSMAP=y CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_BLK_DEV_NBD=y CONFIG_BLK_DEV_NBD=y
CONFIG_EEPROM_LEGACY=y CONFIG_EEPROM_LEGACY=y
CONFIG_SCSI=y CONFIG_SCSI=y

Просмотреть файл

@ -102,7 +102,6 @@ CONFIG_MTD_CFI_STAA=y
CONFIG_MTD_RAM=y CONFIG_MTD_RAM=y
CONFIG_MTD_ROM=y CONFIG_MTD_ROM=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_S3C2410=y CONFIG_MTD_NAND_S3C2410=y
CONFIG_MTD_NAND_PLATFORM=y CONFIG_MTD_NAND_PLATFORM=y
CONFIG_MTD_LPDDR=y CONFIG_MTD_LPDDR=y

Просмотреть файл

@ -49,7 +49,6 @@ CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP=y CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_ORION=y CONFIG_MTD_NAND_ORION=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y
# CONFIG_SCSI_PROC_FS is not set # CONFIG_SCSI_PROC_FS is not set

Просмотреть файл

@ -57,7 +57,6 @@ CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y CONFIG_MTD_BLOCK=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ECC_SMC=y CONFIG_MTD_NAND_ECC_SMC=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_NOMADIK=y CONFIG_MTD_NAND_NOMADIK=y
CONFIG_MTD_ONENAND=y CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y CONFIG_MTD_ONENAND_VERIFY_WRITE=y

Просмотреть файл

@ -72,7 +72,6 @@ CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP=y CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PLATFORM=y CONFIG_MTD_NAND_PLATFORM=y
CONFIG_MTD_NAND_ORION=y CONFIG_MTD_NAND_ORION=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y

Просмотреть файл

@ -36,7 +36,6 @@ CONFIG_MTD_CONCAT=y
CONFIG_MTD_CHAR=y CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y CONFIG_MTD_BLOCK=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PXA3xx=y CONFIG_MTD_NAND_PXA3xx=y
CONFIG_MTD_NAND_PXA3xx_BUILTIN=y CONFIG_MTD_NAND_PXA3xx_BUILTIN=y
CONFIG_MTD_ONENAND=y CONFIG_MTD_ONENAND=y

Просмотреть файл

@ -94,7 +94,6 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_ROM=y CONFIG_MTD_ROM=y
CONFIG_MTD_COMPLEX_MAPPINGS=y CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_SHARPSL=y CONFIG_MTD_NAND_SHARPSL=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y
CONFIG_IDE=y CONFIG_IDE=y

Просмотреть файл

@ -23,6 +23,8 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <mach/hardware.h> #include <mach/hardware.h>
#include <asm/sizes.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)); 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") MACHINE_START(AUTCPU12, "autronix autcpu12")
/* Maintainer: Thomas Gleixner */ /* Maintainer: Thomas Gleixner */
.atag_offset = 0x20000, .atag_offset = 0x20000,
.init_machine = autcpu12_init,
.map_io = autcpu12_map_io, .map_io = autcpu12_map_io,
.init_irq = clps711x_init_irq, .init_irq = clps711x_init_irq,
.timer = &clps711x_timer, .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[ssi1_ipg_gate], NULL, "83fcc000.ssi");
clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "70014000.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[ssi3_ipg_gate], NULL, "83fe8000.ssi");
clk_register_clkdev(clk[nfc_gate], NULL, "83fdb000.nand");
/* set the usboh3 parent to pll2_sw */ /* set the usboh3 parent to pll2_sw */
clk_set_parent(clk[usboh3_sel], clk[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[ssi1_ipg_gate], NULL, "63fcc000.ssi");
clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "50014000.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[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_ipg_gate], "ipg", "53fc8000.can");
clk_register_clkdev(clk[can1_serial_gate], "per", "53fc8000.can"); clk_register_clkdev(clk[can1_serial_gate], "per", "53fc8000.can");
clk_register_clkdev(clk[can2_ipg_gate], "ipg", "53fcc000.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 */ /* AXI has to come first, that's how the mxc_nand driver expect it */
struct resource res[] = { struct resource res[] = {
{ {
.start = data->axibase,
.end = data->axibase + SZ_16K - 1,
.flags = IORESOURCE_MEM,
}, {
.start = data->iobase, .start = data->iobase,
.end = data->iobase + data->iosize - 1, .end = data->iobase + data->iosize - 1,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
@ -74,10 +70,13 @@ struct platform_device *__init imx_add_mxc_nand(
.start = data->irq, .start = data->irq,
.end = data->irq, .end = data->irq,
.flags = IORESOURCE_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, return imx_add_platform_device("mxc_nand", data->id,
res + !data->axibase, res, ARRAY_SIZE(res) - !data->axibase,
ARRAY_SIZE(res) - !data->axibase,
pdata, sizeof(*pdata)); pdata, sizeof(*pdata));
} }

Просмотреть файл

@ -57,7 +57,6 @@ CONFIG_MTD_PLATRAM=y
CONFIG_MTD_PHRAM=y CONFIG_MTD_PHRAM=y
CONFIG_MTD_BLOCK2MTD=y CONFIG_MTD_BLOCK2MTD=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PLATFORM=y CONFIG_MTD_NAND_PLATFORM=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM=y

Просмотреть файл

@ -119,7 +119,6 @@ CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y CONFIG_MTD_BLOCK=y
CONFIG_MTD_BLOCK2MTD=y CONFIG_MTD_BLOCK2MTD=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_PLATFORM=y CONFIG_MTD_NAND_PLATFORM=y
CONFIG_ATA=y CONFIG_ATA=y
# CONFIG_ATA_VERBOSE_ERROR is not set # CONFIG_ATA_VERBOSE_ERROR is not set

Просмотреть файл

@ -38,7 +38,6 @@ CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_FSL_ELBC=y CONFIG_MTD_NAND_FSL_ELBC=y
CONFIG_PROC_DEVICETREE=y CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y

Просмотреть файл

@ -37,7 +37,6 @@ CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_PROC_DEVICETREE=y CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM=y

Просмотреть файл

@ -50,7 +50,6 @@ CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_FSL_ELBC=y CONFIG_MTD_NAND_FSL_ELBC=y
CONFIG_PROC_DEVICETREE=y CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_LOOP=y

Просмотреть файл

@ -148,6 +148,13 @@ config MTD_BCM63XX_PARTS
This provides partions parsing for BCM63xx devices with CFE This provides partions parsing for BCM63xx devices with CFE
bootloaders. 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" comment "User Modules And Translation Layers"
config MTD_CHAR config MTD_CHAR

Просмотреть файл

@ -12,6 +12,7 @@ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
# 'Users' - code which presents functionality to userspace. # 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o obj-$(CONFIG_MTD_CHAR) += mtdchar.o

202
drivers/mtd/bcm47xxpart.c Normal file
Просмотреть файл

@ -0,0 +1,202 @@
/*
* 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" prompt "Flash cmd/query data swapping"
depends on MTD_CFI_ADV_OPTIONS depends on MTD_CFI_ADV_OPTIONS
default MTD_CFI_NOSWAP default MTD_CFI_NOSWAP
config MTD_CFI_NOSWAP
bool "NO"
---help--- ---help---
This option defines the way in which the CPU attempts to arrange This option defines the way in which the CPU attempts to arrange
data bits when writing the 'magic' commands to the chips. Saying 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 Specific arrangements are possible with the BIG_ENDIAN_BYTE and
LITTLE_ENDIAN_BYTE, if the bytes are reversed. LITTLE_ENDIAN_BYTE, if the bytes are reversed.
If you have a LART, on which the data (and address) lines were config MTD_CFI_NOSWAP
connected in a fashion which ensured that the nets were as short bool "NO"
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_BE_BYTE_SWAP config MTD_CFI_BE_BYTE_SWAP
bool "BIG_ENDIAN_BYTE" 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_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp = cfi->cmdset_priv; struct cfi_pri_intelext *extp = cfi->cmdset_priv;
int udelay; int mdelay;
int ret; int ret;
adr += chip->start; 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 * If Instant Individual Block Locking supported then no need
* to delay. * 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) { if (ret) {
map_write(map, CMD(0x70), adr); map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS; 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 mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
{ {
struct cfi_private *cfi = map->fldrv_priv; 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) { switch(chip->oldstate) {
case FL_ERASING: 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); map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
cfi_fixup_m29ew_delay_after_resume(cfi);
chip->oldstate = FL_READY; chip->oldstate = FL_READY;
chip->state = FL_ERASING; chip->state = FL_ERASING;
break; break;
@ -916,6 +981,8 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
/* Disallow XIP again */ /* Disallow XIP again */
local_irq_disable(); local_irq_disable();
/* Correct Erase Suspend Hangups for M29EW */
cfi_fixup_m29ew_erase_suspend(map, adr);
/* Resume the write or erase operation */ /* Resume the write or erase operation */
map_write(map, cfi->sector_erase_cmd, adr); map_write(map, cfi->sector_erase_cmd, adr);
chip->state = oldstate; chip->state = oldstate;

Просмотреть файл

@ -39,11 +39,10 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/bootmem.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h>
/* error message prefix */ /* error message prefix */
#define ERRP "mtd: " #define ERRP "mtd: "
@ -72,7 +71,7 @@ static struct cmdline_mtd_partition *partitions;
/* the command line passed to mtdpart_setup() */ /* the command line passed to mtdpart_setup() */
static char *cmdline; static char *cmdline;
static int cmdline_parsed = 0; static int cmdline_parsed;
/* /*
* Parse one partition definition for an MTD. Since there can be many * 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. * syntax has been verified ok.
*/ */
static struct mtd_partition * newpart(char *s, static struct mtd_partition * newpart(char *s,
char **retptr, char **retptr,
int *num_parts, int *num_parts,
int this_part, int this_part,
unsigned char **extra_mem_ptr, unsigned char **extra_mem_ptr,
int extra_mem_size) int extra_mem_size)
{ {
struct mtd_partition *parts; struct mtd_partition *parts;
unsigned long size; unsigned long size, offset = OFFSET_CONTINUOUS;
unsigned long offset = OFFSET_CONTINUOUS;
char *name; char *name;
int name_len; int name_len;
unsigned char *extra_mem; unsigned char *extra_mem;
@ -99,124 +97,106 @@ static struct mtd_partition * newpart(char *s,
unsigned int mask_flags; unsigned int mask_flags;
/* fetch the partition size */ /* fetch the partition size */
if (*s == '-') if (*s == '-') {
{ /* assign all remaining space to this partition */ /* assign all remaining space to this partition */
size = SIZE_REMAINING; size = SIZE_REMAINING;
s++; s++;
} } else {
else
{
size = memparse(s, &s); size = memparse(s, &s);
if (size < PAGE_SIZE) if (size < PAGE_SIZE) {
{
printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); printk(KERN_ERR ERRP "partition size too small (%lx)\n", size);
return NULL; return ERR_PTR(-EINVAL);
} }
} }
/* fetch partition name and flags */ /* fetch partition name and flags */
mask_flags = 0; /* this is going to be a regular partition */ mask_flags = 0; /* this is going to be a regular partition */
delim = 0; delim = 0;
/* check for offset */
if (*s == '@') /* check for offset */
{ if (*s == '@') {
s++; s++;
offset = memparse(s, &s); offset = memparse(s, &s);
}
/* now look for name */
if (*s == '(')
{
delim = ')';
} }
if (delim) /* now look for name */
{ if (*s == '(')
delim = ')';
if (delim) {
char *p; char *p;
name = ++s; name = ++s;
p = strchr(name, delim); p = strchr(name, delim);
if (!p) if (!p) {
{
printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim);
return NULL; return ERR_PTR(-EINVAL);
} }
name_len = p - name; name_len = p - name;
s = p + 1; s = p + 1;
} } else {
else name = NULL;
{
name = NULL;
name_len = 13; /* Partition_000 */ name_len = 13; /* Partition_000 */
} }
/* record name length for memory allocation later */ /* record name length for memory allocation later */
extra_mem_size += name_len + 1; extra_mem_size += name_len + 1;
/* test for options */ /* test for options */
if (strncmp(s, "ro", 2) == 0) if (strncmp(s, "ro", 2) == 0) {
{
mask_flags |= MTD_WRITEABLE; mask_flags |= MTD_WRITEABLE;
s += 2; s += 2;
} }
/* if lk is found do NOT unlock the MTD partition*/ /* if lk is found do NOT unlock the MTD partition*/
if (strncmp(s, "lk", 2) == 0) if (strncmp(s, "lk", 2) == 0) {
{
mask_flags |= MTD_POWERUP_LOCK; mask_flags |= MTD_POWERUP_LOCK;
s += 2; s += 2;
} }
/* test if more partitions are following */ /* test if more partitions are following */
if (*s == ',') if (*s == ',') {
{ if (size == SIZE_REMAINING) {
if (size == SIZE_REMAINING)
{
printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n");
return NULL; return ERR_PTR(-EINVAL);
} }
/* more partitions follow, parse them */ /* more partitions follow, parse them */
parts = newpart(s + 1, &s, num_parts, this_part + 1, parts = newpart(s + 1, &s, num_parts, this_part + 1,
&extra_mem, extra_mem_size); &extra_mem, extra_mem_size);
if (!parts) if (IS_ERR(parts))
return NULL; return parts;
} } else {
else /* this is the last partition: allocate space for all */
{ /* this is the last partition: allocate space for all */
int alloc_size; int alloc_size;
*num_parts = this_part + 1; *num_parts = this_part + 1;
alloc_size = *num_parts * sizeof(struct mtd_partition) + alloc_size = *num_parts * sizeof(struct mtd_partition) +
extra_mem_size; extra_mem_size;
parts = kzalloc(alloc_size, GFP_KERNEL); parts = kzalloc(alloc_size, GFP_KERNEL);
if (!parts) if (!parts)
return NULL; return ERR_PTR(-ENOMEM);
extra_mem = (unsigned char *)(parts + *num_parts); extra_mem = (unsigned char *)(parts + *num_parts);
} }
/* enter this partition (offset will be calculated later if it is zero at this point) */ /* enter this partition (offset will be calculated later if it is zero at this point) */
parts[this_part].size = size; parts[this_part].size = size;
parts[this_part].offset = offset; parts[this_part].offset = offset;
parts[this_part].mask_flags = mask_flags; parts[this_part].mask_flags = mask_flags;
if (name) if (name)
{
strlcpy(extra_mem, name, name_len + 1); strlcpy(extra_mem, name, name_len + 1);
}
else else
{
sprintf(extra_mem, "Partition_%03d", this_part); sprintf(extra_mem, "Partition_%03d", this_part);
}
parts[this_part].name = extra_mem; parts[this_part].name = extra_mem;
extra_mem += name_len + 1; extra_mem += name_len + 1;
dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n", dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
this_part, this_part, parts[this_part].name, parts[this_part].offset,
parts[this_part].name, parts[this_part].size, parts[this_part].mask_flags));
parts[this_part].offset,
parts[this_part].size,
parts[this_part].mask_flags));
/* return (updated) pointer to extra_mem memory */ /* return (updated) pointer to extra_mem memory */
if (extra_mem_ptr) if (extra_mem_ptr)
*extra_mem_ptr = extra_mem; *extra_mem_ptr = extra_mem;
/* return (updated) pointer command line string */ /* return (updated) pointer command line string */
*retptr = s; *retptr = s;
@ -236,16 +216,16 @@ static int mtdpart_setup_real(char *s)
{ {
struct cmdline_mtd_partition *this_mtd; struct cmdline_mtd_partition *this_mtd;
struct mtd_partition *parts; struct mtd_partition *parts;
int mtd_id_len; int mtd_id_len, num_parts;
int num_parts;
char *p, *mtd_id; char *p, *mtd_id;
mtd_id = s; mtd_id = s;
/* fetch <mtd-id> */ /* fetch <mtd-id> */
if (!(p = strchr(s, ':'))) p = strchr(s, ':');
{ if (!p) {
printk(KERN_ERR ERRP "no mtd-id\n"); printk(KERN_ERR ERRP "no mtd-id\n");
return 0; return -EINVAL;
} }
mtd_id_len = p - mtd_id; mtd_id_len = p - mtd_id;
@ -262,8 +242,7 @@ static int mtdpart_setup_real(char *s)
(unsigned char**)&this_mtd, /* out: extra mem */ (unsigned char**)&this_mtd, /* out: extra mem */
mtd_id_len + 1 + sizeof(*this_mtd) + mtd_id_len + 1 + sizeof(*this_mtd) +
sizeof(void*)-1 /*alignment*/); sizeof(void*)-1 /*alignment*/);
if(!parts) if (IS_ERR(parts)) {
{
/* /*
* An error occurred. We're either: * An error occurred. We're either:
* a) out of memory, or * 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 * Either way, this mtd is hosed and we're
* unlikely to succeed in parsing any more * unlikely to succeed in parsing any more
*/ */
return 0; return PTR_ERR(parts);
} }
/* align this_mtd */ /* align this_mtd */
this_mtd = (struct cmdline_mtd_partition *) this_mtd = (struct cmdline_mtd_partition *)
ALIGN((unsigned long)this_mtd, sizeof(void*)); ALIGN((unsigned long)this_mtd, sizeof(void *));
/* enter results */ /* enter results */
this_mtd->parts = parts; this_mtd->parts = parts;
this_mtd->num_parts = num_parts; this_mtd->num_parts = num_parts;
@ -296,14 +275,14 @@ static int mtdpart_setup_real(char *s)
break; break;
/* does another spec follow? */ /* does another spec follow? */
if (*s != ';') if (*s != ';') {
{
printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s); printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s);
return 0; return -EINVAL;
} }
s++; s++;
} }
return 1;
return 0;
} }
/* /*
@ -318,44 +297,58 @@ static int parse_cmdline_partitions(struct mtd_info *master,
struct mtd_part_parser_data *data) struct mtd_part_parser_data *data)
{ {
unsigned long offset; unsigned long offset;
int i; int i, err;
struct cmdline_mtd_partition *part; struct cmdline_mtd_partition *part;
const char *mtd_id = master->name; const char *mtd_id = master->name;
/* parse command line */ /* parse command line */
if (!cmdline_parsed) if (!cmdline_parsed) {
mtdpart_setup_real(cmdline); err = mtdpart_setup_real(cmdline);
if (err)
return err;
}
for(part = partitions; part; part = part->next) for (part = partitions; part; part = part->next) {
{ if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) {
if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) for (i = 0, offset = 0; i < part->num_parts; i++) {
{
for(i = 0, offset = 0; i < part->num_parts; i++)
{
if (part->parts[i].offset == OFFSET_CONTINUOUS) if (part->parts[i].offset == OFFSET_CONTINUOUS)
part->parts[i].offset = offset; part->parts[i].offset = offset;
else else
offset = part->parts[i].offset; offset = part->parts[i].offset;
if (part->parts[i].size == SIZE_REMAINING) if (part->parts[i].size == SIZE_REMAINING)
part->parts[i].size = master->size - offset; part->parts[i].size = master->size - offset;
if (offset + part->parts[i].size > master->size)
{ 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 printk(KERN_WARNING ERRP
"%s: partitioning exceeds flash size, truncating\n", "%s: partitioning exceeds flash size, truncating\n",
part->mtd_id); part->mtd_id);
part->parts[i].size = master->size - offset; part->parts[i].size = master->size - offset;
part->num_parts = i;
} }
offset += part->parts[i].size; offset += part->parts[i].size;
} }
*pparts = kmemdup(part->parts, *pparts = kmemdup(part->parts,
sizeof(*part->parts) * part->num_parts, sizeof(*part->parts) * part->num_parts,
GFP_KERNEL); GFP_KERNEL);
if (!*pparts) if (!*pparts)
return -ENOMEM; return -ENOMEM;
return part->num_parts; return part->num_parts;
} }
} }
return 0; return 0;
} }

Просмотреть файл

@ -97,7 +97,7 @@ config MTD_M25P80
doesn't support the JEDEC ID instruction. doesn't support the JEDEC ID instruction.
config M25PXX_USE_FAST_READ 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 depends on MTD_M25P80
default y default y
help help
@ -120,6 +120,14 @@ config MTD_SST25L
Set up your spi devices with the right board-specific platform data, Set up your spi devices with the right board-specific platform data,
if you want to specify device partitioning. 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 config MTD_SLRAM
tristate "Uncached system RAM" tristate "Uncached system RAM"
help help

Просмотреть файл

@ -19,5 +19,6 @@ obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
CFLAGS_docg3.o += -I$(src) CFLAGS_docg3.o += -I$(src)

Просмотреть файл

@ -0,0 +1,105 @@
#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 #ifdef ECC_DEBUG
printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n",
__FILE__, __LINE__, (int)from); __FILE__, __LINE__, (int)from);
printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" printk(" syndrome= %*phC\n", 6, syndrome);
"%02x\n", printk(" eccbuf= %*phC\n", 6, eccbuf);
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]);
#endif #endif
ret = -EIO; ret = -EIO;
} }
} }
#ifdef PSYCHO_DEBUG #ifdef PSYCHO_DEBUG
printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", printk("ECC DATA at %lx: %*ph\n", (long)from, 6, eccbuf);
(long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
eccbuf[4], eccbuf[5]);
#endif #endif
/* disable the ECC engine */ /* disable the ECC engine */
WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); 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); eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
if (nboob >= DOC_LAYOUT_OOB_SIZE) { if (nboob >= DOC_LAYOUT_OOB_SIZE) {
doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", doc_dbg("OOB - INFO: %*phC\n", 7, oobbuf);
oobbuf[0], oobbuf[1], oobbuf[2], oobbuf[3],
oobbuf[4], oobbuf[5], oobbuf[6]);
doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]); doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]);
doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", doc_dbg("OOB - BCH_ECC: %*phC\n", 7, oobbuf + 8);
oobbuf[8], oobbuf[9], oobbuf[10], oobbuf[11],
oobbuf[12], oobbuf[13], oobbuf[14]);
doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]);
} }
doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1);
doc_dbg("ECC HW_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", doc_dbg("ECC HW_ECC: %*phC\n", 7, hwecc);
hwecc[0], hwecc[1], hwecc[2], hwecc[3], hwecc[4],
hwecc[5], hwecc[6]);
ret = -EIO; ret = -EIO;
if (is_prot_seq_error(docg3)) 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) }, { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
/* EON -- en25xxx */ /* EON -- en25xxx */
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
/* Everspin */ /* Everspin */
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, { "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) }, { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
/* Macronix */ /* Macronix */
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, { "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) }, { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
{ "mx25l25655e", INFO(0xc22619, 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 /* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors). * for the chips listed here (without boot sectors).
*/ */
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) },
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 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) },
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
{ "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, 0) }, { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, 0) },
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 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) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 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) }, { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, 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) }, { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 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) }, { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
{ "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 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) }, { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) },
{ "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 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) }, { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
{ "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, { "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) }, { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32", INFO(0xef4016, 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) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },

Просмотреть файл

@ -26,6 +26,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/param.h> #include <linux/param.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/spear_smi.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 */ /* copy dev->status (lower 16 bits) in order to release lock */
if (ret > 0) if (ret > 0)
ret = dev->status & 0xffff; ret = dev->status & 0xffff;
else else if (ret == 0)
ret = -EIO; ret = -ETIMEDOUT;
/* restore the ctrl regs state */ /* restore the ctrl regs state */
writel(ctrlreg1, dev->io_base + SMI_CR1); 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; finish = jiffies + timeout;
do { do {
status = spear_smi_read_sr(dev, bank); status = spear_smi_read_sr(dev, bank);
if (status < 0) if (status < 0) {
continue; /* try till timeout */ if (status == -ETIMEDOUT)
else if (!(status & SR_WIP)) continue; /* try till finish */
return status;
} else if (!(status & SR_WIP)) {
return 0; return 0;
}
cond_resched(); cond_resched();
} while (!time_after_eq(jiffies, finish)); } while (!time_after_eq(jiffies, finish));
dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n"); 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); val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8);
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
/* clear all interrupt conditions */
writel(0, dev->io_base + SMI_SR);
writel(val, dev->io_base + SMI_CR1); writel(val, dev->io_base + SMI_CR1);
mutex_unlock(&dev->lock); 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(ctrlreg1, dev->io_base + SMI_CR1);
writel(0, dev->io_base + SMI_CR2); writel(0, dev->io_base + SMI_CR2);
if (ret <= 0) { if (ret == 0) {
ret = -EIO; ret = -EIO;
dev_err(&dev->pdev->dev, dev_err(&dev->pdev->dev,
"smi controller failed on write enable\n"); "smi controller failed on write enable\n");
} else { } else if (ret > 0) {
/* check whether write mode status is set for required bank */ /* check whether write mode status is set for required bank */
if (dev->status & (1 << (bank + WM_SHIFT))) if (dev->status & (1 << (bank + WM_SHIFT)))
ret = 0; ret = 0;
@ -462,10 +469,10 @@ static int spear_smi_erase_sector(struct spear_smi *dev,
ret = wait_event_interruptible_timeout(dev->cmd_complete, ret = wait_event_interruptible_timeout(dev->cmd_complete,
dev->status & TFF, SMI_CMD_TIMEOUT); dev->status & TFF, SMI_CMD_TIMEOUT);
if (ret <= 0) { if (ret == 0) {
ret = -EIO; ret = -EIO;
dev_err(&dev->pdev->dev, "sector erase failed\n"); dev_err(&dev->pdev->dev, "sector erase failed\n");
} else } else if (ret > 0)
ret = 0; /* success */ ret = 0; /* success */
/* restore ctrl regs */ /* restore ctrl regs */
@ -820,7 +827,7 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
if (!flash_info) if (!flash_info)
return -ENODEV; return -ENODEV;
flash = kzalloc(sizeof(*flash), GFP_ATOMIC); flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_ATOMIC);
if (!flash) if (!flash)
return -ENOMEM; return -ENOMEM;
flash->bank = bank; 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); flash_index = spear_smi_probe_flash(dev, bank);
if (flash_index < 0) { if (flash_index < 0) {
dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank); dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank);
ret = flash_index; return flash_index;
goto err_probe;
} }
/* map the memory for nor flash chip */ /* map the memory for nor flash chip */
flash->base_addr = ioremap(flash_info->mem_base, flash_info->size); flash->base_addr = devm_ioremap(&pdev->dev, flash_info->mem_base,
if (!flash->base_addr) { flash_info->size);
ret = -EIO; if (!flash->base_addr)
goto err_probe; return -EIO;
}
dev->flash[bank] = flash; dev->flash[bank] = flash;
flash->mtd.priv = dev; flash->mtd.priv = dev;
@ -881,17 +886,10 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
count); count);
if (ret) { if (ret) {
dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret); dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret);
goto err_map; return ret;
} }
return 0; 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 { } else {
pdata = dev_get_platdata(&pdev->dev); pdata = dev_get_platdata(&pdev->dev);
if (pdata < 0) { if (!pdata) {
ret = -ENODEV; ret = -ENODEV;
dev_err(&pdev->dev, "no platform data\n"); dev_err(&pdev->dev, "no platform data\n");
goto err; 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); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
ret = -ENODEV; ret = -ENODEV;
@ -949,32 +940,26 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
goto err; goto err;
} }
dev = kzalloc(sizeof(*dev), GFP_ATOMIC); dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
if (!dev) { if (!dev) {
ret = -ENOMEM; ret = -ENOMEM;
dev_err(&pdev->dev, "mem alloc fail\n"); dev_err(&pdev->dev, "mem alloc fail\n");
goto err; goto err;
} }
smi_base = request_mem_region(smi_base->start, resource_size(smi_base), smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pdev->name);
if (!smi_base) {
ret = -EBUSY;
dev_err(&pdev->dev, "request mem region fail\n");
goto err_mem;
}
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) { if (!dev->io_base) {
ret = -EIO; ret = -EIO;
dev_err(&pdev->dev, "ioremap fail\n"); dev_err(&pdev->dev, "devm_request_and_ioremap fail\n");
goto err_ioremap; goto err;
} }
dev->pdev = pdev; dev->pdev = pdev;
dev->clk_rate = pdata->clk_rate; 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->clk_rate = SMI_MAX_CLOCK_FREQ;
dev->num_flashes = pdata->num_flashes; 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->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)) { if (IS_ERR(dev->clk)) {
ret = PTR_ERR(dev->clk); ret = PTR_ERR(dev->clk);
goto err_clk; goto err;
} }
ret = clk_prepare_enable(dev->clk); ret = clk_prepare_enable(dev->clk);
if (ret) 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) { if (ret) {
dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n"); dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n");
goto err_irq; goto err_irq;
@ -1017,18 +1003,9 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
return 0; return 0;
err_bank_setup: err_bank_setup:
free_irq(irq, dev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
err_irq: err_irq:
clk_disable_unprepare(dev->clk); 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: err:
return ret; return ret;
} }
@ -1042,11 +1019,8 @@ err:
static int __devexit spear_smi_remove(struct platform_device *pdev) static int __devexit spear_smi_remove(struct platform_device *pdev)
{ {
struct spear_smi *dev; struct spear_smi *dev;
struct spear_smi_plat_data *pdata;
struct spear_snor_flash *flash; struct spear_snor_flash *flash;
struct resource *smi_base; int ret, i;
int ret;
int i, irq;
dev = platform_get_drvdata(pdev); dev = platform_get_drvdata(pdev);
if (!dev) { if (!dev) {
@ -1054,8 +1028,6 @@ static int __devexit spear_smi_remove(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
pdata = dev_get_platdata(&pdev->dev);
/* clean up for all nor flash */ /* clean up for all nor flash */
for (i = 0; i < dev->num_flashes; i++) { for (i = 0; i < dev->num_flashes; i++) {
flash = dev->flash[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); ret = mtd_device_unregister(&flash->mtd);
if (ret) if (ret)
dev_err(&pdev->dev, "error removing mtd\n"); 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_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); platform_set_drvdata(pdev, NULL);
return 0; 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) if (sdev && sdev->clk)
clk_disable_unprepare(dev->clk); clk_disable_unprepare(sdev->clk);
return 0; 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; int ret = -EPERM;
if (dev && dev->clk) if (sdev && sdev->clk)
ret = clk_prepare_enable(dev->clk); ret = clk_prepare_enable(sdev->clk);
if (!ret) if (!ret)
spear_smi_hw_init(dev); spear_smi_hw_init(sdev);
return ret; return ret;
} }
static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume);
#endif
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id spear_smi_id_table[] = { static const struct of_device_id spear_smi_id_table[] = {
{ .compatible = "st,spear600-smi" }, { .compatible = "st,spear600-smi" },
@ -1123,11 +1087,12 @@ static struct platform_driver spear_smi_driver = {
.bus = &platform_bus_type, .bus = &platform_bus_type,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(spear_smi_id_table), .of_match_table = of_match_ptr(spear_smi_id_table),
#ifdef CONFIG_PM
.pm = &spear_smi_pm_ops,
#endif
}, },
.probe = spear_smi_probe, .probe = spear_smi_probe,
.remove = __devexit_p(spear_smi_remove), .remove = __devexit_p(spear_smi_remove),
.suspend = spear_smi_suspend,
.resume = spear_smi_resume,
}; };
static int spear_smi_init(void) static int spear_smi_init(void)

Просмотреть файл

@ -373,7 +373,7 @@ config MTD_FORTUNET
have such a board, say 'Y'. have such a board, say 'Y'.
config MTD_AUTCPU12 config MTD_AUTCPU12
tristate "NV-RAM mapping AUTCPU12 board" bool "NV-RAM mapping AUTCPU12 board"
depends on ARCH_AUTCPU12 depends on ARCH_AUTCPU12
help help
This enables access to the NV-RAM on autronix autcpu12 board. This enables access to the NV-RAM on autronix autcpu12 board.
@ -443,22 +443,10 @@ config MTD_GPIO_ADDR
config MTD_UCLINUX config MTD_UCLINUX
bool "Generic uClinux RAM/ROM filesystem support" bool "Generic uClinux RAM/ROM filesystem support"
depends on MTD_RAM=y && !MMU depends on MTD_RAM=y && (!MMU || COLDFIRE)
help help
Map driver to support image based filesystems for uClinux. 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 config MTD_DMV182
tristate "Map driver for Dy-4 SVME/DMV-182 board." tristate "Map driver for Dy-4 SVME/DMV-182 board."
depends on DMV182 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_H720X) += h720x-flash.o
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_IXP2000) += ixp2000.o obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o
obj-$(CONFIG_MTD_DMV182) += dmv182.o obj-$(CONFIG_MTD_DMV182) += dmv182.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.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 * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 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/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/init.h> #include <linux/init.h>
#include <asm/io.h> #include <linux/device.h>
#include <asm/sizes.h> #include <linux/module.h>
#include <mach/hardware.h> #include <linux/platform_device.h>
#include <mach/autcpu12.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
struct autcpu12_nvram_priv {
static struct mtd_info *sram_mtd; struct mtd_info *mtd;
struct map_info map;
struct map_info autcpu12_sram_map = {
.name = "SRAM",
.size = 32768,
.bankwidth = 4,
.phys = 0x12000000,
}; };
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); priv = devm_kzalloc(&pdev->dev,
if (!autcpu12_sram_map.virt) { sizeof(struct autcpu12_nvram_priv), GFP_KERNEL);
printk("Failed to ioremap autcpu12 NV-RAM space\n"); if (!priv)
err = -EIO; return -ENOMEM;
goto out;
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;
} }
simple_map_init(&autcpu_sram_map);
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(&priv->map);
/* /*
* Check for 32K/128K * Check for 32K/128K
@ -61,65 +72,59 @@ static int __init init_autcpu12_sram (void)
* Read and check result on ofs 0x0 * Read and check result on ofs 0x0
* Restore contents * Restore contents
*/ */
save0 = map_read32(&autcpu12_sram_map,0); save0 = map_read(&priv->map, 0);
save1 = map_read32(&autcpu12_sram_map,0x10000); save1 = map_read(&priv->map, 0x10000);
map_write32(&autcpu12_sram_map,~save0,0x10000); tmp.x[0] = ~save0.x[0];
/* if we find this pattern on 0x0, we have 32K size map_write(&priv->map, tmp, 0x10000);
* restore contents and exit tmp = map_read(&priv->map, 0);
*/ /* if we find this pattern on 0x0, we have 32K size */
if ( map_read32(&autcpu12_sram_map,0) != save0) { if (!map_word_equal(&priv->map, tmp, save0)) {
map_write32(&autcpu12_sram_map,save0,0x0); map_write(&priv->map, save0, 0x0);
goto map; priv->map.size = SZ_32K;
} } else
/* We have a 128K found, restore 0x10000 and set size map_write(&priv->map, save1, 0x10000);
* to 128K
*/
map_write32(&autcpu12_sram_map,save1,0x10000);
autcpu12_sram_map.size = SZ_128K;
map: priv->mtd = do_map_probe("map_ram", &priv->map);
sram_mtd = do_map_probe("map_ram", &autcpu12_sram_map); if (!priv->mtd) {
if (!sram_mtd) { dev_err(&pdev->dev, "probing failed\n");
printk("NV-RAM probe failed\n"); return -ENXIO;
err = -ENXIO;
goto out_ioremap;
} }
sram_mtd->owner = THIS_MODULE; priv->mtd->owner = THIS_MODULE;
sram_mtd->erasesize = 16; priv->mtd->erasesize = 16;
priv->mtd->dev.parent = &pdev->dev;
if (mtd_device_register(sram_mtd, NULL, 0)) { if (!mtd_device_register(priv->mtd, NULL, 0)) {
printk("NV-RAM device addition failed\n"); dev_info(&pdev->dev,
err = -ENOMEM; "NV-RAM device size %ldKiB registered on AUTCPU12\n",
goto out_probe; priv->map.size / SZ_1K);
return 0;
} }
printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); map_destroy(priv->mtd);
dev_err(&pdev->dev, "NV-RAM device addition failed\n");
return -ENOMEM;
}
static int __devexit autcpu12_nvram_remove(struct platform_device *pdev)
{
struct autcpu12_nvram_priv *priv = platform_get_drvdata(pdev);
mtd_device_unregister(priv->mtd);
map_destroy(priv->mtd);
return 0; return 0;
out_probe:
map_destroy(sram_mtd);
sram_mtd = 0;
out_ioremap:
iounmap((void *)autcpu12_sram_map.virt);
out:
return err;
} }
static void __exit cleanup_autcpu12_maps(void) static struct platform_driver autcpu12_nvram_driver = {
{ .driver = {
if (sram_mtd) { .name = "autcpu12_nvram",
mtd_device_unregister(sram_mtd); .owner = THIS_MODULE,
map_destroy(sram_mtd); },
iounmap((void *)autcpu12_sram_map.virt); .probe = autcpu12_nvram_probe,
} .remove = __devexit_p(autcpu12_nvram_remove),
} };
module_platform_driver(autcpu12_nvram_driver);
module_init(init_autcpu12_sram);
module_exit(cleanup_autcpu12_maps);
MODULE_AUTHOR("Thomas Gleixner"); MODULE_AUTHOR("Thomas Gleixner");
MODULE_DESCRIPTION("autcpu12 NV-RAM map driver"); MODULE_DESCRIPTION("autcpu12 NVRAM map driver");
MODULE_LICENSE("GPL"); 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; struct map_pci_info *map = (struct map_pci_info *)_map;
map_word val; map_word val;
val.x[0]= readb(map->base + map->translate(map, ofs)); val.x[0]= readb(map->base + map->translate(map, ofs));
// printk("read8 : %08lx => %02x\n", ofs, val.x[0]);
return val; 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) static map_word mtd_pci_read32(struct map_info *_map, unsigned long ofs)
{ {
struct map_pci_info *map = (struct map_pci_info *)_map; struct map_pci_info *map = (struct map_pci_info *)_map;
map_word val; map_word val;
val.x[0] = readl(map->base + map->translate(map, ofs)); val.x[0] = readl(map->base + map->translate(map, ofs));
// printk("read32: %08lx => %08x\n", ofs, val.x[0]);
return val; 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) 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; 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)); 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) 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; 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)); 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_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("Generic PCI map driver"); MODULE_DESCRIPTION("Generic PCI map driver");
MODULE_DEVICE_TABLE(pci, mtd_pci_ids); 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; struct mtd_info **mtd_list = NULL;
resource_size_t res_size; resource_size_t res_size;
struct mtd_part_parser_data ppdata; struct mtd_part_parser_data ppdata;
bool map_indirect;
match = of_match_device(of_flash_match, &dev->dev); match = of_match_device(of_flash_match, &dev->dev);
if (!match) if (!match)
@ -192,6 +193,8 @@ static int __devinit of_flash_probe(struct platform_device *dev)
} }
count /= reg_tuple_size; count /= reg_tuple_size;
map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
err = -ENOMEM; err = -ENOMEM;
info = kzalloc(sizeof(struct of_flash) + info = kzalloc(sizeof(struct of_flash) +
sizeof(struct of_flash_list) * count, GFP_KERNEL); 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); 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) { if (probe_type) {
info->list[i].mtd = do_map_probe(probe_type, info->list[i].mtd = do_map_probe(probe_type,
&info->list[i].map); &info->list[i].map);

Просмотреть файл

@ -100,8 +100,6 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
goto err_out; goto err_out;
} }
info->mtd->owner = THIS_MODULE; info->mtd->owner = THIS_MODULE;
if (err)
goto err_out;
err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts, err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
pdata->nr_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", printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",
(int) mapp->phys, (int) mapp->size); (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) { if (mapp->virt == 0) {
printk("uclinux[mtd]: ioremap_nocache() failed\n"); printk("uclinux[mtd]: no virtual mapping?\n");
return(-EIO); return(-EIO);
} }
@ -79,7 +85,6 @@ static int __init uclinux_mtd_init(void)
mtd = do_map_probe("map_ram", mapp); mtd = do_map_probe("map_ram", mapp);
if (!mtd) { if (!mtd) {
printk("uclinux[mtd]: failed to find a mapping?\n"); printk("uclinux[mtd]: failed to find a mapping?\n");
iounmap(mapp->virt);
return(-ENXIO); return(-ENXIO);
} }
@ -102,10 +107,8 @@ static void __exit uclinux_mtd_cleanup(void)
map_destroy(uclinux_ram_mtdinfo); map_destroy(uclinux_ram_mtdinfo);
uclinux_ram_mtdinfo = NULL; uclinux_ram_mtdinfo = NULL;
} }
if (uclinux_ram_map.virt) { if (uclinux_ram_map.virt)
iounmap((void *) uclinux_ram_map.virt);
uclinux_ram_map.virt = 0; uclinux_ram_map.virt = 0;
}
} }
/****************************************************************************/ /****************************************************************************/

Просмотреть файл

@ -1,174 +0,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; resource_size_t start, off;
unsigned long len, vma_len; 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); off = get_vm_offset(vma);
start = map->phys; start = map->phys;
len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size); 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); 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 * 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 * 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; cxt->nextpage = 0;
} }
while (1) { while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) {
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;
}
badblock: badblock:
printk(KERN_WARNING "mtdoops: bad block at %08lx\n", printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
cxt->nextpage * record_size); cxt->nextpage * record_size);
@ -190,6 +183,11 @@ badblock:
} }
} }
if (ret < 0) {
printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n");
return;
}
for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size); 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 * partition parsers, specified in @types. However, if @types is %NULL, then
* the default list of parsers is used. The default list contains only the * the default list of parsers is used. The default list contains only the
* "cmdlinepart" and "ofpart" parsers ATM. * "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: * This function may return:
* o a negative error code in case of failure * 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) if (!parser)
continue; continue;
ret = (*parser->parse_fn)(master, pparts, data); ret = (*parser->parse_fn)(master, pparts, data);
put_partition_parser(parser);
if (ret > 0) { if (ret > 0) {
printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
ret, parser->name, master->name); ret, parser->name, master->name);
break;
} }
put_partition_parser(parser);
} }
return ret; return ret;
} }

Просмотреть файл

@ -22,15 +22,6 @@ menuconfig MTD_NAND
if 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 config MTD_NAND_BCH
tristate tristate
select BCH select BCH
@ -267,22 +258,6 @@ config MTD_NAND_S3C2410_CLKSTOP
when the is NAND chip selected or released, but will save when the is NAND chip selected or released, but will save
approximately 5mA of power when there is nothing happening. 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 config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
depends on EXPERIMENTAL depends on EXPERIMENTAL
@ -356,7 +331,7 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
config MTD_NAND_DOCG4 config MTD_NAND_DOCG4
tristate "Support for DiskOnChip G4 (EXPERIMENTAL)" tristate "Support for DiskOnChip G4 (EXPERIMENTAL)"
depends on EXPERIMENTAL depends on EXPERIMENTAL && HAS_IOMEM
select BCH select BCH
select BITREVERSE select BITREVERSE
help help
@ -414,6 +389,28 @@ config MTD_NAND_PXA3xx
This enables the driver for the NAND flash device found on This enables the driver for the NAND flash device found on
PXA3xx processors 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 config MTD_NAND_CM_X270
tristate "Support for NAND Flash on CM-X270 modules" tristate "Support for NAND Flash on CM-X270 modules"
depends on MACH_ARMCORE depends on MACH_ARMCORE
@ -439,10 +436,10 @@ config MTD_NAND_NANDSIM
MTD nand layer. MTD nand layer.
config MTD_NAND_GPMI_NAND config MTD_NAND_GPMI_NAND
bool "GPMI NAND Flash Controller driver" tristate "GPMI NAND Flash Controller driver"
depends on MTD_NAND && MXS_DMA depends on MTD_NAND && MXS_DMA
help 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 The GPMI controller is very powerful, with the help of BCH
module, it can do the hardware ECC. The GPMI supports several module, it can do the hardware ECC. The GPMI supports several
NAND flashs at the same time. The GPMI may conflicts with other 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 config MTD_NAND_MXC
tristate "MXC NAND support" tristate "MXC NAND support"
depends on IMX_HAVE_PLATFORM_MXC_NAND depends on ARCH_MXC
help help
This enables the driver for the NAND flash controller on the This enables the driver for the NAND flash controller on the
MXC processors. MXC processors.
@ -567,4 +564,12 @@ config MTD_NAND_FSMC
Enables support for NAND Flash chips on the ST Microelectronics Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC) 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 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_ELBC) += fsl_elbc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_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_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_SH_FLCTL) += sh_flctl.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_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_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
nand-objs := nand_base.o nand_bbt.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); 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 * 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->read_byte = ams_delta_read_byte;
this->write_buf = ams_delta_write_buf; this->write_buf = ams_delta_write_buf;
this->read_buf = ams_delta_read_buf; this->read_buf = ams_delta_read_buf;
this->verify_buf = ams_delta_verify_buf;
this->cmd_ctrl = ams_delta_hwcontrol; this->cmd_ctrl = ams_delta_hwcontrol;
if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) { if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) {
this->dev_ready = ams_delta_nand_ready; this->dev_ready = ams_delta_nand_ready;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -3,7 +3,7 @@
* Based on AT91SAM9260 datasheet revision B. * Based on AT91SAM9260 datasheet revision B.
* *
* Copyright (C) 2007 Andrew Victor * 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 * 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 * 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_NPR 0x10 /* NParity register */
#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */ #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 #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 * au_write_buf16 - write buffer to chip
* @mtd: MTD device structure * @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 */ /* Select the chip by setting nCE to low */
#define NAND_CTL_SETNCE 1 #define NAND_CTL_SETNCE 1
/* Deselect the chip by setting nCE to high */ /* 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->read_word = au_read_word;
this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf; this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf;
this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_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); ret = nand_scan(&ctx->info, 1);
if (ret) { if (ret) {

Просмотреть файл

@ -1,217 +0,0 @@
/*****************************************************************************
* 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);
}

Просмотреть файл

@ -1,555 +0,0 @@
/*****************************************************************************
* 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 <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/mach-types.h>
#include <mach/reg_nand.h>
#include <mach/reg_umi.h>
#include "nand_bcm_umi.h"
#include <mach/memory_settings.h>
#define USE_DMA 1
#include <mach/dma.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
/* ---- External Variable Declarations ----------------------------------- */
/* ---- External Function Prototypes ------------------------------------- */
/* ---- Public Variables ------------------------------------------------- */
/* ---- Private Constants and Types -------------------------------------- */
static const __devinitconst char gBanner[] = KERN_INFO \
"BCM UMI MTD NAND Driver: 1.00\n";
#if NAND_ECC_BCH
static uint8_t scan_ff_pattern[] = { 0xff };
static struct nand_bbt_descr largepage_bbt = {
.options = 0,
.offs = 0,
.len = 1,
.pattern = scan_ff_pattern
};
#endif
/*
** Preallocate a buffer to avoid having to do this every dma operation.
** This is the size of the preallocated coherent DMA buffer.
*/
#if USE_DMA
#define DMA_MIN_BUFLEN 512
#define DMA_MAX_BUFLEN PAGE_SIZE
#define USE_DIRECT_IO(len) (((len) < DMA_MIN_BUFLEN) || \
((len) > DMA_MAX_BUFLEN))
/*
* The current NAND data space goes from 0x80001900 to 0x80001FFF,
* which is only 0x700 = 1792 bytes long. This is too small for 2K, 4K page
* size NAND flash. Need to break the DMA down to multiple 1Ks.
*
* Need to make sure REG_NAND_DATA_PADDR + DMA_MAX_LEN < 0x80002000
*/
#define DMA_MAX_LEN 1024
#else /* !USE_DMA */
#define DMA_MIN_BUFLEN 0
#define DMA_MAX_BUFLEN 0
#define USE_DIRECT_IO(len) 1
#endif
/* ---- Private Function Prototypes -------------------------------------- */
static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len);
static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf,
int len);
/* ---- Private Variables ------------------------------------------------ */
static struct mtd_info *board_mtd;
static void __iomem *bcm_umi_io_base;
static void *virtPtr;
static dma_addr_t physPtr;
static struct completion nand_comp;
/* ---- Private Functions ------------------------------------------------ */
#if NAND_ECC_BCH
#include "bcm_umi_bch.c"
#else
#include "bcm_umi_hamming.c"
#endif
#if USE_DMA
/* Handler called when the DMA finishes. */
static void nand_dma_handler(DMA_Device_t dev, int reason, void *userData)
{
complete(&nand_comp);
}
static int nand_dma_init(void)
{
int rc;
rc = dma_set_device_handler(DMA_DEVICE_NAND_MEM_TO_MEM,
nand_dma_handler, NULL);
if (rc != 0) {
printk(KERN_ERR "dma_set_device_handler failed: %d\n", rc);
return rc;
}
virtPtr =
dma_alloc_coherent(NULL, DMA_MAX_BUFLEN, &physPtr, GFP_KERNEL);
if (virtPtr == NULL) {
printk(KERN_ERR "NAND - Failed to allocate memory for DMA buffer\n");
return -ENOMEM;
}
return 0;
}
static void nand_dma_term(void)
{
if (virtPtr != NULL)
dma_free_coherent(NULL, DMA_MAX_BUFLEN, virtPtr, physPtr);
}
static void nand_dma_read(void *buf, int len)
{
int offset = 0;
int tmp_len = 0;
int len_left = len;
DMA_Handle_t hndl;
if (virtPtr == NULL)
panic("nand_dma_read: virtPtr == NULL\n");
if ((void *)physPtr == NULL)
panic("nand_dma_read: physPtr == NULL\n");
hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM);
if (hndl < 0) {
printk(KERN_ERR
"nand_dma_read: unable to allocate dma channel: %d\n",
(int)hndl);
panic("\n");
}
while (len_left > 0) {
if (len_left > DMA_MAX_LEN) {
tmp_len = DMA_MAX_LEN;
len_left -= DMA_MAX_LEN;
} else {
tmp_len = len_left;
len_left = 0;
}
init_completion(&nand_comp);
dma_transfer_mem_to_mem(hndl, REG_NAND_DATA_PADDR,
physPtr + offset, tmp_len);
wait_for_completion(&nand_comp);
offset += tmp_len;
}
dma_free_channel(hndl);
if (buf != NULL)
memcpy(buf, virtPtr, len);
}
static void nand_dma_write(const void *buf, int len)
{
int offset = 0;
int tmp_len = 0;
int len_left = len;
DMA_Handle_t hndl;
if (buf == NULL)
panic("nand_dma_write: buf == NULL\n");
if (virtPtr == NULL)
panic("nand_dma_write: virtPtr == NULL\n");
if ((void *)physPtr == NULL)
panic("nand_dma_write: physPtr == NULL\n");
memcpy(virtPtr, buf, len);
hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM);
if (hndl < 0) {
printk(KERN_ERR
"nand_dma_write: unable to allocate dma channel: %d\n",
(int)hndl);
panic("\n");
}
while (len_left > 0) {
if (len_left > DMA_MAX_LEN) {
tmp_len = DMA_MAX_LEN;
len_left -= DMA_MAX_LEN;
} else {
tmp_len = len_left;
len_left = 0;
}
init_completion(&nand_comp);
dma_transfer_mem_to_mem(hndl, physPtr + offset,
REG_NAND_DATA_PADDR, tmp_len);
wait_for_completion(&nand_comp);
offset += tmp_len;
}
dma_free_channel(hndl);
}
#endif
static int nand_dev_ready(struct mtd_info *mtd)
{
return nand_bcm_umi_dev_ready();
}
/****************************************************************************
*
* bcm_umi_nand_inithw
*
* This routine does the necessary hardware (board-specific)
* initializations. This includes setting up the timings, etc.
*
***************************************************************************/
int bcm_umi_nand_inithw(void)
{
/* Configure nand timing parameters */
writel(readl(&REG_UMI_NAND_TCR) & ~0x7ffff, &REG_UMI_NAND_TCR);
writel(readl(&REG_UMI_NAND_TCR) | HW_CFG_NAND_TCR, &REG_UMI_NAND_TCR);
#if !defined(CONFIG_MTD_NAND_BCM_UMI_HWCS)
/* enable software control of CS */
writel(readl(&REG_UMI_NAND_TCR) | REG_UMI_NAND_TCR_CS_SWCTRL, &REG_UMI_NAND_TCR);
#endif
/* keep NAND chip select asserted */
writel(readl(&REG_UMI_NAND_RCSR) | REG_UMI_NAND_RCSR_CS_ASSERTED, &REG_UMI_NAND_RCSR);
writel(readl(&REG_UMI_NAND_TCR) & ~REG_UMI_NAND_TCR_WORD16, &REG_UMI_NAND_TCR);
/* enable writes to flash */
writel(readl(&REG_UMI_MMD_ICR) | REG_UMI_MMD_ICR_FLASH_WP, &REG_UMI_MMD_ICR);
writel(NAND_CMD_RESET, bcm_umi_io_base + REG_NAND_CMD_OFFSET);
nand_bcm_umi_wait_till_ready();
#if NAND_ECC_BCH
nand_bcm_umi_bch_config_ecc(NAND_ECC_NUM_BYTES);
#endif
return 0;
}
/* Used to turn latch the proper register for access. */
static void bcm_umi_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
/* send command to hardware */
struct nand_chip *chip = mtd->priv;
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_CLE) {
chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_CMD_OFFSET;
goto CMD;
}
if (ctrl & NAND_ALE) {
chip->IO_ADDR_W =
bcm_umi_io_base + REG_NAND_ADDR_OFFSET;
goto CMD;
}
chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
}
CMD:
/* Send command to chip directly */
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
}
static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf,
int len)
{
if (USE_DIRECT_IO(len)) {
/* Do it the old way if the buffer is small or too large.
* Probably quicker than starting and checking dma. */
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++)
writeb(buf[i], this->IO_ADDR_W);
}
#if USE_DMA
else
nand_dma_write(buf, len);
#endif
}
static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
{
if (USE_DIRECT_IO(len)) {
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++)
buf[i] = readb(this->IO_ADDR_R);
}
#if USE_DMA
else
nand_dma_read(buf, len);
#endif
}
static uint8_t readbackbuf[NAND_MAX_PAGESIZE];
static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf,
int len)
{
/*
* Try to readback page with ECC correction. This is necessary
* for MLC parts which may have permanently stuck bits.
*/
struct nand_chip *chip = mtd->priv;
int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0, 0);
if (ret < 0)
return -EFAULT;
else {
if (memcmp(readbackbuf, buf, len) == 0)
return 0;
return -EFAULT;
}
return 0;
}
static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
{
struct nand_chip *this;
struct resource *r;
int err = 0;
printk(gBanner);
/* Allocate memory for MTD device structure and private data */
board_mtd =
kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
GFP_KERNEL);
if (!board_mtd) {
printk(KERN_WARNING
"Unable to allocate NAND MTD device structure.\n");
return -ENOMEM;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
err = -ENXIO;
goto out_free;
}
/* map physical address */
bcm_umi_io_base = ioremap(r->start, resource_size(r));
if (!bcm_umi_io_base) {
printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n");
err = -EIO;
goto out_free;
}
/* Get pointer to private data */
this = (struct nand_chip *)(&board_mtd[1]);
/* Initialize structures */
memset((char *)board_mtd, 0, sizeof(struct mtd_info));
memset((char *)this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
board_mtd->priv = this;
/* Initialize the NAND hardware. */
if (bcm_umi_nand_inithw() < 0) {
printk(KERN_ERR "BCM UMI NAND chip could not be initialized\n");
err = -EIO;
goto out_unmap;
}
/* Set address of NAND IO lines */
this->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
this->IO_ADDR_R = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
/* Set command delay time, see datasheet for correct value */
this->chip_delay = 0;
/* Assign the device ready function, if available */
this->dev_ready = nand_dev_ready;
this->options = 0;
this->write_buf = bcm_umi_nand_write_buf;
this->read_buf = bcm_umi_nand_read_buf;
this->verify_buf = bcm_umi_nand_verify_buf;
this->cmd_ctrl = bcm_umi_nand_hwcontrol;
this->ecc.mode = NAND_ECC_HW;
this->ecc.size = 512;
this->ecc.bytes = NAND_ECC_NUM_BYTES;
#if NAND_ECC_BCH
this->ecc.read_page = bcm_umi_bch_read_page_hwecc;
this->ecc.write_page = bcm_umi_bch_write_page_hwecc;
#else
this->ecc.correct = nand_correct_data512;
this->ecc.calculate = bcm_umi_hamming_get_hw_ecc;
this->ecc.hwctl = bcm_umi_hamming_enable_hwecc;
#endif
#if USE_DMA
err = nand_dma_init();
if (err != 0)
goto out_unmap;
#endif
/* Figure out the size of the device that we have.
* We need to do this to figure out which ECC
* layout we'll be using.
*/
err = nand_scan_ident(board_mtd, 1, NULL);
if (err) {
printk(KERN_ERR "nand_scan failed: %d\n", err);
goto out_unmap;
}
/* Now that we know the nand size, we can setup the ECC layout */
switch (board_mtd->writesize) { /* writesize is the pagesize */
case 4096:
this->ecc.layout = &nand_hw_eccoob_4096;
break;
case 2048:
this->ecc.layout = &nand_hw_eccoob_2048;
break;
case 512:
this->ecc.layout = &nand_hw_eccoob_512;
break;
default:
{
printk(KERN_ERR "NAND - Unrecognized pagesize: %d\n",
board_mtd->writesize);
err = -EINVAL;
goto out_unmap;
}
}
#if NAND_ECC_BCH
if (board_mtd->writesize > 512) {
if (this->bbt_options & NAND_BBT_USE_FLASH)
largepage_bbt.options = NAND_BBT_SCAN2NDPAGE;
this->badblock_pattern = &largepage_bbt;
}
this->ecc.strength = 8;
#endif
/* Now finish off the scan, now that ecc.layout has been initialized. */
err = nand_scan_tail(board_mtd);
if (err) {
printk(KERN_ERR "nand_scan failed: %d\n", err);
goto out_unmap;
}
/* Register the partitions */
board_mtd->name = "bcm_umi-nand";
mtd_device_parse_register(board_mtd, NULL, NULL, NULL, 0);
/* Return happy */
return 0;
out_unmap:
iounmap(bcm_umi_io_base);
out_free:
kfree(board_mtd);
return err;
}
static int bcm_umi_nand_remove(struct platform_device *pdev)
{
#if USE_DMA
nand_dma_term();
#endif
/* Release resources, unregister device */
nand_release(board_mtd);
/* unmap physical address */
iounmap(bcm_umi_io_base);
/* Free the MTD device structure */
kfree(board_mtd);
return 0;
}
#ifdef CONFIG_PM
static int bcm_umi_nand_suspend(struct platform_device *pdev,
pm_message_t state)
{
printk(KERN_ERR "MTD NAND suspend is being called\n");
return 0;
}
static int bcm_umi_nand_resume(struct platform_device *pdev)
{
printk(KERN_ERR "MTD NAND resume is being called\n");
return 0;
}
#else
#define bcm_umi_nand_suspend NULL
#define bcm_umi_nand_resume NULL
#endif
static struct platform_driver nand_driver = {
.driver = {
.name = "bcm-nand",
.owner = THIS_MODULE,
},
.probe = bcm_umi_nand_probe,
.remove = bcm_umi_nand_remove,
.suspend = bcm_umi_nand_suspend,
.resume = bcm_umi_nand_resume,
};
module_platform_driver(nand_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("BCM UMI MTD NAND driver");

Просмотреть файл

@ -566,11 +566,13 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip
return 0; return 0;
} }
static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
const uint8_t *buf, int oob_required) struct nand_chip *chip, const uint8_t *buf, int oob_required)
{ {
bf5xx_nand_write_buf(mtd, buf, mtd->writesize); bf5xx_nand_write_buf(mtd, buf, mtd->writesize);
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); 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 * @buf: buffer to store read data
* @oob_required: caller expects OOB data read to chip->oob_poi * @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. * we need a special oob layout and handling.
*/ */
static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, 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, struct nand_chip *chip,
const uint8_t *buf, int oob_required) 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 */ /* Set up ECC autogeneration */
cafe->ctl2 |= (1<<30); cafe->ctl2 |= (1<<30);
return 0;
} }
static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, 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); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw)) 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 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 * 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); 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; 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; *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) static inline void nand_cs_on(void)
{ {
gpio_set_value(GPIO_NAND_CS, 0); 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_byte = cmx270_read_byte;
this->read_buf = cmx270_read_buf; this->read_buf = cmx270_read_buf;
this->write_buf = cmx270_write_buf; this->write_buf = cmx270_write_buf;
this->verify_buf = cmx270_verify_buf;
/* Scan to find existence of the device */ /* Scan to find existence of the device */
if (nand_scan (cmx270_nand_mtd, 1)) { if (nand_scan (cmx270_nand_mtd, 1)) {

Просмотреть файл

@ -33,6 +33,7 @@
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/platform_data/mtd-davinci.h> #include <linux/platform_data/mtd-davinci.h>
#include <linux/platform_data/mtd-davinci-aemif.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) 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 davinci_nand_info *info;
struct resource *res1; struct resource *res1;
struct resource *res2; struct resource *res2;
@ -530,6 +597,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
uint32_t val; uint32_t val;
nand_ecc_modes_t ecc_mode; nand_ecc_modes_t ecc_mode;
pdata = nand_davinci_get_pdata(pdev);
/* insist on board-specific configuration */ /* insist on board-specific configuration */
if (!pdata) if (!pdata)
return -ENODEV; return -ENODEV;
@ -656,7 +724,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
goto err_clk; goto err_clk;
} }
ret = clk_enable(info->clk); ret = clk_prepare_enable(info->clk);
if (ret < 0) { if (ret < 0) {
dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
ret); ret);
@ -767,7 +835,7 @@ syndrome_done:
err_scan: err_scan:
err_timing: err_timing:
clk_disable(info->clk); clk_disable_unprepare(info->clk);
err_clk_enable: err_clk_enable:
clk_put(info->clk); clk_put(info->clk);
@ -804,7 +872,7 @@ static int __exit nand_davinci_remove(struct platform_device *pdev)
nand_release(&info->mtd); nand_release(&info->mtd);
clk_disable(info->clk); clk_disable_unprepare(info->clk);
clk_put(info->clk); clk_put(info->clk);
kfree(info); kfree(info);
@ -816,6 +884,8 @@ static struct platform_driver nand_davinci_driver = {
.remove = __exit_p(nand_davinci_remove), .remove = __exit_p(nand_davinci_remove),
.driver = { .driver = {
.name = "davinci_nand", .name = "davinci_nand",
.owner = THIS_MODULE,
.of_match_table = davinci_nand_of_match,
}, },
}; };
MODULE_ALIAS("platform:davinci_nand"); 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 /* writes a page. user specifies type, and this function handles the
* configuration details. */ * 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) const uint8_t *buf, bool raw_xfer)
{ {
struct denali_nand_info *denali = mtd_to_denali(mtd); 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); denali_enable_dma(denali, false);
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE); dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
return 0;
} }
/* NAND core entry points */ /* 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 * writing a page with ECC or without is similar, all the work is done
* by write_page above. * 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) const uint8_t *buf, int oob_required)
{ {
/* for regular page writes, we let HW handle all the ECC /* for regular page writes, we let HW handle all the ECC
* data written to the device. */ * 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. /* 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 * raw access is similar to ECC page writes, so all the work is done in the
* write_page() function above. * 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) const uint8_t *buf, int oob_required)
{ {
/* for raw page writes, we want to disable ECC and simply write /* for raw page writes, we want to disable ECC and simply write
whatever data is in the buffer. */ 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, 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) static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
{ {
struct nand_chip *this = mtd->priv; 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); 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) static u_char doc2001plus_read_byte(struct mtd_info *mtd)
{ {
struct nand_chip *this = mtd->priv; 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"); 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) static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
{ {
struct nand_chip *this = mtd->priv; 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->read_byte = doc2000_read_byte;
this->write_buf = doc2000_writebuf; this->write_buf = doc2000_writebuf;
this->read_buf = doc2000_readbuf; this->read_buf = doc2000_readbuf;
this->verify_buf = doc2000_verifybuf;
this->scan_bbt = nftl_scan_bbt; this->scan_bbt = nftl_scan_bbt;
doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; 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->read_byte = doc2001_read_byte;
this->write_buf = doc2001_writebuf; this->write_buf = doc2001_writebuf;
this->read_buf = doc2001_readbuf; this->read_buf = doc2001_readbuf;
this->verify_buf = doc2001_verifybuf;
ReadDOC(doc->virtadr, ChipID); ReadDOC(doc->virtadr, ChipID);
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->read_byte = doc2001plus_read_byte;
this->write_buf = doc2001plus_writebuf; this->write_buf = doc2001plus_writebuf;
this->read_buf = doc2001plus_readbuf; this->read_buf = doc2001plus_readbuf;
this->verify_buf = doc2001plus_verifybuf;
this->scan_bbt = inftl_scan_bbt; this->scan_bbt = inftl_scan_bbt;
this->cmd_ctrl = NULL; this->cmd_ctrl = NULL;
this->select_chip = doc2001plus_select_chip; 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. * bit flips(s) are not reported in stats.
*/ */
if (doc->oob_buf[15]) { if (nand->oob_poi[15]) {
int bit, numsetbits = 0; 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) for_each_set_bit(bit, &written_flag, 8)
numsetbits++; numsetbits++;
if (numsetbits > 4) { /* assume blank */ 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 error within oob area preceeding ecc bytes... */
if (errpos[i] > DOCG4_PAGE_SIZE * 8) if (errpos[i] > DOCG4_PAGE_SIZE * 8)
change_bit(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 */ else /* error in page data */
change_bit(errpos[i], (unsigned long *)buf); 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 */ docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */
/* /* this device always reads oob after 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.
*/
/* first 14 oob bytes read from I/O reg */ /* 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 */ /* 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); *buf16 = readw(docptr + DOCG4_MYSTERY_REG);
write_nop(docptr); write_nop(docptr);
@ -782,6 +776,8 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
} }
writew(0, docptr + DOC_DATAEND); writew(0, docptr + DOC_DATAEND);
if (bits_corrected == -EBADMSG) /* uncorrectable errors */
return 0;
return bits_corrected; 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); 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); docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page);
writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); 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); 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) const uint8_t *buf, bool use_ecc)
{ {
struct docg4_priv *doc = nand->priv; 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); write_nop(docptr);
writew(0, docptr + DOC_DATAEND); writew(0, docptr + DOC_DATAEND);
write_nop(docptr); 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) const uint8_t *buf, int oob_required)
{ {
return write_page(mtd, nand, buf, false); 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) const uint8_t *buf, int oob_required)
{ {
return write_page(mtd, nand, buf, true); return write_page(mtd, nand, buf, true);

Просмотреть файл

@ -614,41 +614,6 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
len, avail); len, avail);
} }
/*
* Verify buffer against the FCM Controller Data Buffer
*/
static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
int i;
if (len < 0) {
dev_err(priv->dev, "write_buf of %d bytes", len);
return -EINVAL;
}
if ((unsigned int)len >
elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) {
dev_err(priv->dev,
"verify_buf beyond end of buffer "
"(%d requested, %u available)\n",
len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes;
return -EINVAL;
}
for (i = 0; i < len; i++)
if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i])
!= buf[i])
break;
elbc_fcm_ctrl->index += len;
return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO;
}
/* This function is called after Program and Erase Operations to /* This function is called after Program and Erase Operations to
* check for success or failure. * check for success or failure.
*/ */
@ -766,11 +731,13 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* ECC will be calculated automatically, and errors will be detected in /* ECC will be calculated automatically, and errors will be detected in
* waitfunc. * waitfunc.
*/ */
static void fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, buf, mtd->writesize);
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
} }
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
@ -796,7 +763,6 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
chip->read_byte = fsl_elbc_read_byte; chip->read_byte = fsl_elbc_read_byte;
chip->write_buf = fsl_elbc_write_buf; chip->write_buf = fsl_elbc_write_buf;
chip->read_buf = fsl_elbc_read_buf; chip->read_buf = fsl_elbc_read_buf;
chip->verify_buf = fsl_elbc_verify_buf;
chip->select_chip = fsl_elbc_select_chip; chip->select_chip = fsl_elbc_select_chip;
chip->cmdfunc = fsl_elbc_cmdfunc; chip->cmdfunc = fsl_elbc_cmdfunc;
chip->waitfunc = fsl_elbc_wait; chip->waitfunc = fsl_elbc_wait;
@ -805,7 +771,6 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
chip->bbt_md = &bbt_mirror_descr; chip->bbt_md = &bbt_mirror_descr;
/* set up nand options */ /* set up nand options */
chip->options = NAND_NO_READRDY;
chip->bbt_options = NAND_BBT_USE_FLASH; chip->bbt_options = NAND_BBT_USE_FLASH;
chip->controller = &elbc_fcm_ctrl->controller; chip->controller = &elbc_fcm_ctrl->controller;
@ -916,7 +881,8 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
elbc_fcm_ctrl->chips[bank] = priv; elbc_fcm_ctrl->chips[bank] = priv;
priv->bank = bank; priv->bank = bank;
priv->ctrl = fsl_lbc_ctrl_dev; priv->ctrl = fsl_lbc_ctrl_dev;
priv->dev = dev; priv->dev = &pdev->dev;
dev_set_drvdata(priv->dev, priv);
priv->vbase = ioremap(res.start, resource_size(&res)); priv->vbase = ioremap(res.start, resource_size(&res));
if (!priv->vbase) { if (!priv->vbase) {
@ -963,11 +929,10 @@ err:
static int fsl_elbc_nand_remove(struct platform_device *pdev) static int fsl_elbc_nand_remove(struct platform_device *pdev)
{ {
int i;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
for (i = 0; i < MAX_BANKS; i++) struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev);
if (elbc_fcm_ctrl->chips[i])
fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]); fsl_elbc_chip_remove(priv);
mutex_lock(&fsl_elbc_nand_mutex); mutex_lock(&fsl_elbc_nand_mutex);
elbc_fcm_ctrl->counter--; elbc_fcm_ctrl->counter--;

Просмотреть файл

@ -194,7 +194,7 @@ static int is_blank(struct mtd_info *mtd, unsigned int bufnum)
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_ifc_mtd *priv = chip->priv; struct fsl_ifc_mtd *priv = chip->priv;
u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2); u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2);
u32 __iomem *mainarea = (u32 *)addr; u32 __iomem *mainarea = (u32 __iomem *)addr;
u8 __iomem *oob = addr + mtd->writesize; u8 __iomem *oob = addr + mtd->writesize;
int i; int i;
@ -592,8 +592,8 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
* next byte. * next byte.
*/ */
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
data = in_be16((uint16_t *)&ifc_nand_ctrl-> data = in_be16((uint16_t __iomem *)&ifc_nand_ctrl->
addr[ifc_nand_ctrl->index]); addr[ifc_nand_ctrl->index]);
ifc_nand_ctrl->index += 2; ifc_nand_ctrl->index += 2;
return (uint8_t) data; return (uint8_t) data;
} }
@ -627,46 +627,6 @@ static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
__func__, len, avail); __func__, len, avail);
} }
/*
* Verify buffer against the IFC Controller Data Buffer
*/
static int fsl_ifc_verify_buf(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct nand_chip *chip = mtd->priv;
struct fsl_ifc_mtd *priv = chip->priv;
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
int i;
if (len < 0) {
dev_err(priv->dev, "%s: write_buf of %d bytes", __func__, len);
return -EINVAL;
}
if ((unsigned int)len > nctrl->read_bytes - nctrl->index) {
dev_err(priv->dev,
"%s: beyond end of buffer (%d requested, %u available)\n",
__func__, len, nctrl->read_bytes - nctrl->index);
nctrl->index = nctrl->read_bytes;
return -EINVAL;
}
for (i = 0; i < len; i++)
if (in_8(&nctrl->addr[nctrl->index + i]) != buf[i])
break;
nctrl->index += len;
if (i != len)
return -EIO;
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
return -EIO;
return 0;
}
/* /*
* This function is called after Program and Erase Operations to * This function is called after Program and Erase Operations to
* check for success or failure. * check for success or failure.
@ -722,11 +682,13 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* ECC will be calculated automatically, and errors will be detected in /* ECC will be calculated automatically, and errors will be detected in
* waitfunc. * waitfunc.
*/ */
static void fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
fsl_ifc_write_buf(mtd, buf, mtd->writesize); fsl_ifc_write_buf(mtd, buf, mtd->writesize);
fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
} }
static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
@ -844,7 +806,6 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->write_buf = fsl_ifc_write_buf; chip->write_buf = fsl_ifc_write_buf;
chip->read_buf = fsl_ifc_read_buf; chip->read_buf = fsl_ifc_read_buf;
chip->verify_buf = fsl_ifc_verify_buf;
chip->select_chip = fsl_ifc_select_chip; chip->select_chip = fsl_ifc_select_chip;
chip->cmdfunc = fsl_ifc_cmdfunc; chip->cmdfunc = fsl_ifc_cmdfunc;
chip->waitfunc = fsl_ifc_wait; chip->waitfunc = fsl_ifc_wait;
@ -855,7 +816,6 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
out_be32(&ifc->ifc_nand.ncfgr, 0x0); out_be32(&ifc->ifc_nand.ncfgr, 0x0);
/* set up nand options */ /* set up nand options */
chip->options = NAND_NO_READRDY;
chip->bbt_options = NAND_BBT_USE_FLASH; chip->bbt_options = NAND_BBT_USE_FLASH;

Просмотреть файл

@ -100,23 +100,6 @@ static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len)
readsb(this->IO_ADDR_R, buf, len); readsb(this->IO_ADDR_R, buf, len);
} }
static int gpio_nand_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
unsigned char read, *p = (unsigned char *) buf;
int i, err = 0;
for (i = 0; i < len; i++) {
read = readb(this->IO_ADDR_R);
if (read != p[i]) {
pr_debug("%s: err at %d (read %04x vs %04x)\n",
__func__, i, read, p[i]);
err = -EFAULT;
}
}
return err;
}
static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf,
int len) int len)
{ {
@ -148,26 +131,6 @@ static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len)
} }
} }
static int gpio_nand_verifybuf16(struct mtd_info *mtd, const u_char *buf,
int len)
{
struct nand_chip *this = mtd->priv;
unsigned short read, *p = (unsigned short *) buf;
int i, err = 0;
len >>= 1;
for (i = 0; i < len; i++) {
read = readw(this->IO_ADDR_R);
if (read != p[i]) {
pr_debug("%s: err at %d (read %04x vs %04x)\n",
__func__, i, read, p[i]);
err = -EFAULT;
}
}
return err;
}
static int gpio_nand_devready(struct mtd_info *mtd) static int gpio_nand_devready(struct mtd_info *mtd)
{ {
struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd);
@ -391,11 +354,9 @@ static int __devinit gpio_nand_probe(struct platform_device *dev)
if (this->options & NAND_BUSWIDTH_16) { if (this->options & NAND_BUSWIDTH_16) {
this->read_buf = gpio_nand_readbuf16; this->read_buf = gpio_nand_readbuf16;
this->write_buf = gpio_nand_writebuf16; this->write_buf = gpio_nand_writebuf16;
this->verify_buf = gpio_nand_verifybuf16;
} else { } else {
this->read_buf = gpio_nand_readbuf; this->read_buf = gpio_nand_readbuf;
this->write_buf = gpio_nand_writebuf; this->write_buf = gpio_nand_writebuf;
this->verify_buf = gpio_nand_verifybuf;
} }
/* set the mtd private data for the nand driver */ /* set the mtd private data for the nand driver */
@ -456,20 +417,7 @@ static struct platform_driver gpio_nand_driver = {
}, },
}; };
static int __init gpio_nand_init(void) module_platform_driver(gpio_nand_driver);
{
printk(KERN_INFO "GPIO NAND driver, © 2004 Simtec Electronics\n");
return platform_driver_register(&gpio_nand_driver);
}
static void __exit gpio_nand_exit(void)
{
platform_driver_unregister(&gpio_nand_driver);
}
module_init(gpio_nand_init);
module_exit(gpio_nand_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");

Просмотреть файл

@ -26,7 +26,7 @@
#include "gpmi-regs.h" #include "gpmi-regs.h"
#include "bch-regs.h" #include "bch-regs.h"
struct timing_threshod timing_default_threshold = { static struct timing_threshod timing_default_threshold = {
.max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
BP_GPMI_TIMING0_DATA_SETUP), BP_GPMI_TIMING0_DATA_SETUP),
.internal_data_setup_in_ns = 0, .internal_data_setup_in_ns = 0,
@ -124,12 +124,42 @@ error:
return -ETIMEDOUT; return -ETIMEDOUT;
} }
static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
{
struct clk *clk;
int ret;
int i;
for (i = 0; i < GPMI_CLK_MAX; i++) {
clk = this->resources.clock[i];
if (!clk)
break;
if (v) {
ret = clk_prepare_enable(clk);
if (ret)
goto err_clk;
} else {
clk_disable_unprepare(clk);
}
}
return 0;
err_clk:
for (; i > 0; i--)
clk_disable_unprepare(this->resources.clock[i - 1]);
return ret;
}
#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true)
#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false)
int gpmi_init(struct gpmi_nand_data *this) int gpmi_init(struct gpmi_nand_data *this)
{ {
struct resources *r = &this->resources; struct resources *r = &this->resources;
int ret; int ret;
ret = clk_prepare_enable(r->clock); ret = gpmi_enable_clk(this);
if (ret) if (ret)
goto err_out; goto err_out;
ret = gpmi_reset_block(r->gpmi_regs, false); ret = gpmi_reset_block(r->gpmi_regs, false);
@ -149,7 +179,7 @@ int gpmi_init(struct gpmi_nand_data *this)
/* Select BCH ECC. */ /* Select BCH ECC. */
writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET);
clk_disable_unprepare(r->clock); gpmi_disable_clk(this);
return 0; return 0;
err_out: err_out:
return ret; return ret;
@ -205,7 +235,7 @@ int bch_set_geometry(struct gpmi_nand_data *this)
ecc_strength = bch_geo->ecc_strength >> 1; ecc_strength = bch_geo->ecc_strength >> 1;
page_size = bch_geo->page_size; page_size = bch_geo->page_size;
ret = clk_prepare_enable(r->clock); ret = gpmi_enable_clk(this);
if (ret) if (ret)
goto err_out; goto err_out;
@ -240,7 +270,7 @@ int bch_set_geometry(struct gpmi_nand_data *this)
writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
r->bch_regs + HW_BCH_CTRL_SET); r->bch_regs + HW_BCH_CTRL_SET);
clk_disable_unprepare(r->clock); gpmi_disable_clk(this);
return 0; return 0;
err_out: err_out:
return ret; return ret;
@ -263,6 +293,7 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
struct gpmi_nfc_hardware_timing *hw) struct gpmi_nfc_hardware_timing *hw)
{ {
struct timing_threshod *nfc = &timing_default_threshold; struct timing_threshod *nfc = &timing_default_threshold;
struct resources *r = &this->resources;
struct nand_chip *nand = &this->nand; struct nand_chip *nand = &this->nand;
struct nand_timing target = this->timing; struct nand_timing target = this->timing;
bool improved_timing_is_available; bool improved_timing_is_available;
@ -302,8 +333,9 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
(target.tRHOH_in_ns >= 0) ; (target.tRHOH_in_ns >= 0) ;
/* Inspect the clock. */ /* Inspect the clock. */
nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]);
clock_frequency_in_hz = nfc->clock_frequency_in_hz; clock_frequency_in_hz = nfc->clock_frequency_in_hz;
clock_period_in_ns = 1000000000 / clock_frequency_in_hz; clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz;
/* /*
* The NFC quantizes setup and hold parameters in terms of clock cycles. * The NFC quantizes setup and hold parameters in terms of clock cycles.
@ -698,17 +730,230 @@ return_results:
hw->address_setup_in_cycles = address_setup_in_cycles; hw->address_setup_in_cycles = address_setup_in_cycles;
hw->use_half_periods = dll_use_half_periods; hw->use_half_periods = dll_use_half_periods;
hw->sample_delay_factor = sample_delay_factor; hw->sample_delay_factor = sample_delay_factor;
hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT;
hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
/* Return success. */ /* Return success. */
return 0; return 0;
} }
/*
* <1> Firstly, we should know what's the GPMI-clock means.
* The GPMI-clock is the internal clock in the gpmi nand controller.
* If you set 100MHz to gpmi nand controller, the GPMI-clock's period
* is 10ns. Mark the GPMI-clock's period as GPMI-clock-period.
*
* <2> Secondly, we should know what's the frequency on the nand chip pins.
* The frequency on the nand chip pins is derived from the GPMI-clock.
* We can get it from the following equation:
*
* F = G / (DS + DH)
*
* F : the frequency on the nand chip pins.
* G : the GPMI clock, such as 100MHz.
* DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP
* DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD
*
* <3> Thirdly, when the frequency on the nand chip pins is above 33MHz,
* the nand EDO(extended Data Out) timing could be applied.
* The GPMI implements a feedback read strobe to sample the read data.
* The feedback read strobe can be delayed to support the nand EDO timing
* where the read strobe may deasserts before the read data is valid, and
* read data is valid for some time after read strobe.
*
* The following figure illustrates some aspects of a NAND Flash read:
*
* |<---tREA---->|
* | |
* | | |
* |<--tRP-->| |
* | | |
* __ ___|__________________________________
* RDN \________/ |
* |
* /---------\
* Read Data --------------< >---------
* \---------/
* | |
* |<-D->|
* FeedbackRDN ________ ____________
* \___________/
*
* D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY.
*
*
* <4> Now, we begin to describe how to compute the right RDN_DELAY.
*
* 4.1) From the aspect of the nand chip pins:
* Delay = (tREA + C - tRP) {1}
*
* tREA : the maximum read access time. From the ONFI nand standards,
* we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4.
* Please check it in : www.onfi.org
* C : a constant for adjust the delay. default is 4.
* tRP : the read pulse width.
* Specified by the HW_GPMI_TIMING0:DATA_SETUP:
* tRP = (GPMI-clock-period) * DATA_SETUP
*
* 4.2) From the aspect of the GPMI nand controller:
* Delay = RDN_DELAY * 0.125 * RP {2}
*
* RP : the DLL reference period.
* if (GPMI-clock-period > DLL_THRETHOLD)
* RP = GPMI-clock-period / 2;
* else
* RP = GPMI-clock-period;
*
* Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
* is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD
* is 16ns, but in mx6q, we use 12ns.
*
* 4.3) since {1} equals {2}, we get:
*
* (tREA + 4 - tRP) * 8
* RDN_DELAY = --------------------- {3}
* RP
*
* 4.4) We only support the fastest asynchronous mode of ONFI nand.
* For some ONFI nand, the mode 4 is the fastest mode;
* while for some ONFI nand, the mode 5 is the fastest mode.
* So we only support the mode 4 and mode 5. It is no need to
* support other modes.
*/
static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
struct gpmi_nfc_hardware_timing *hw)
{
struct resources *r = &this->resources;
unsigned long rate = clk_get_rate(r->clock[0]);
int mode = this->timing_mode;
int dll_threshold = 16; /* in ns */
unsigned long delay;
unsigned long clk_period;
int t_rea;
int c = 4;
int t_rp;
int rp;
/*
* [1] for GPMI_HW_GPMI_TIMING0:
* The async mode requires 40MHz for mode 4, 50MHz for mode 5.
* The GPMI can support 100MHz at most. So if we want to
* get the 40MHz or 50MHz, we have to set DS=1, DH=1.
* Set the ADDRESS_SETUP to 0 in mode 4.
*/
hw->data_setup_in_cycles = 1;
hw->data_hold_in_cycles = 1;
hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0);
/* [2] for GPMI_HW_GPMI_TIMING1 */
hw->device_busy_timeout = 0x9000;
/* [3] for GPMI_HW_GPMI_CTRL1 */
hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
if (GPMI_IS_MX6Q(this))
dll_threshold = 12;
/*
* Enlarge 10 times for the numerator and denominator in {3}.
* This make us to get more accurate result.
*/
clk_period = NSEC_PER_SEC / (rate / 10);
dll_threshold *= 10;
t_rea = ((mode == 5) ? 16 : 20) * 10;
c *= 10;
t_rp = clk_period * 1; /* DATA_SETUP is 1 */
if (clk_period > dll_threshold) {
hw->use_half_periods = 1;
rp = clk_period / 2;
} else {
hw->use_half_periods = 0;
rp = clk_period;
}
/*
* Multiply the numerator with 10, we could do a round off:
* 7.8 round up to 8; 7.4 round down to 7.
*/
delay = (((t_rea + c - t_rp) * 8) * 10) / rp;
delay = (delay + 5) / 10;
hw->sample_delay_factor = delay;
}
static int enable_edo_mode(struct gpmi_nand_data *this, int mode)
{
struct resources *r = &this->resources;
struct nand_chip *nand = &this->nand;
struct mtd_info *mtd = &this->mtd;
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
unsigned long rate;
int ret;
nand->select_chip(mtd, 0);
/* [1] send SET FEATURE commond to NAND */
feature[0] = mode;
ret = nand->onfi_set_features(mtd, nand,
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
if (ret)
goto err_out;
/* [2] send GET FEATURE command to double-check the timing mode */
memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN);
ret = nand->onfi_get_features(mtd, nand,
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
if (ret || feature[0] != mode)
goto err_out;
nand->select_chip(mtd, -1);
/* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */
rate = (mode == 5) ? 100000000 : 80000000;
clk_set_rate(r->clock[0], rate);
/* Let the gpmi_begin() re-compute the timing again. */
this->flags &= ~GPMI_TIMING_INIT_OK;
this->flags |= GPMI_ASYNC_EDO_ENABLED;
this->timing_mode = mode;
dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode);
return 0;
err_out:
nand->select_chip(mtd, -1);
dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode);
return -EINVAL;
}
int gpmi_extra_init(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
/* Enable the asynchronous EDO feature. */
if (GPMI_IS_MX6Q(this) && chip->onfi_version) {
int mode = onfi_get_async_timing_mode(chip);
/* We only support the timing mode 4 and mode 5. */
if (mode & ONFI_TIMING_MODE_5)
mode = 5;
else if (mode & ONFI_TIMING_MODE_4)
mode = 4;
else
return 0;
return enable_edo_mode(this, mode);
}
return 0;
}
/* Begin the I/O */ /* Begin the I/O */
void gpmi_begin(struct gpmi_nand_data *this) void gpmi_begin(struct gpmi_nand_data *this)
{ {
struct resources *r = &this->resources; struct resources *r = &this->resources;
struct timing_threshod *nfc = &timing_default_threshold; void __iomem *gpmi_regs = r->gpmi_regs;
unsigned char *gpmi_regs = r->gpmi_regs;
unsigned int clock_period_in_ns; unsigned int clock_period_in_ns;
uint32_t reg; uint32_t reg;
unsigned int dll_wait_time_in_us; unsigned int dll_wait_time_in_us;
@ -716,60 +961,66 @@ void gpmi_begin(struct gpmi_nand_data *this)
int ret; int ret;
/* Enable the clock. */ /* Enable the clock. */
ret = clk_prepare_enable(r->clock); ret = gpmi_enable_clk(this);
if (ret) { if (ret) {
pr_err("We failed in enable the clk\n"); pr_err("We failed in enable the clk\n");
goto err_out; goto err_out;
} }
/* set ready/busy timeout */ /* Only initialize the timing once */
writel(0x500 << BP_GPMI_TIMING1_BUSY_TIMEOUT, if (this->flags & GPMI_TIMING_INIT_OK)
gpmi_regs + HW_GPMI_TIMING1); return;
this->flags |= GPMI_TIMING_INIT_OK;
/* Get the timing information we need. */ if (this->flags & GPMI_ASYNC_EDO_ENABLED)
nfc->clock_frequency_in_hz = clk_get_rate(r->clock); gpmi_compute_edo_timing(this, &hw);
clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz; else
gpmi_nfc_compute_hardware_timing(this, &hw);
gpmi_nfc_compute_hardware_timing(this, &hw); /* [1] Set HW_GPMI_TIMING0 */
/* Set up all the simple timing parameters. */
reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) |
BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) |
BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles) ; BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles) ;
writel(reg, gpmi_regs + HW_GPMI_TIMING0); writel(reg, gpmi_regs + HW_GPMI_TIMING0);
/* /* [2] Set HW_GPMI_TIMING1 */
* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout),
*/ gpmi_regs + HW_GPMI_TIMING1);
/* [3] The following code is to set the HW_GPMI_CTRL1. */
/* Set the WRN_DLY_SEL */
writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR);
writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel),
gpmi_regs + HW_GPMI_CTRL1_SET);
/* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */
writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
/* Clear out the DLL control fields. */ /* Clear out the DLL control fields. */
writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR); reg = BM_GPMI_CTRL1_RDN_DELAY | BM_GPMI_CTRL1_HALF_PERIOD;
writel(BM_GPMI_CTRL1_HALF_PERIOD, gpmi_regs + HW_GPMI_CTRL1_CLR); writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR);
/* If no sample delay is called for, return immediately. */ /* If no sample delay is called for, return immediately. */
if (!hw.sample_delay_factor) if (!hw.sample_delay_factor)
return; return;
/* Configure the HALF_PERIOD flag. */ /* Set RDN_DELAY or HALF_PERIOD. */
if (hw.use_half_periods) reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0)
writel(BM_GPMI_CTRL1_HALF_PERIOD, | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor);
gpmi_regs + HW_GPMI_CTRL1_SET);
/* Set the delay factor. */ writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET);
writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor),
gpmi_regs + HW_GPMI_CTRL1_SET);
/* Enable the DLL. */ /* At last, we enable the DLL. */
writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET);
/* /*
* After we enable the GPMI DLL, we have to wait 64 clock cycles before * After we enable the GPMI DLL, we have to wait 64 clock cycles before
* we can use the GPMI. * we can use the GPMI. Calculate the amount of time we need to wait,
* * in microseconds.
* Calculate the amount of time we need to wait, in microseconds.
*/ */
clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]);
dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000;
if (!dll_wait_time_in_us) if (!dll_wait_time_in_us)
@ -784,8 +1035,7 @@ err_out:
void gpmi_end(struct gpmi_nand_data *this) void gpmi_end(struct gpmi_nand_data *this)
{ {
struct resources *r = &this->resources; gpmi_disable_clk(this);
clk_disable_unprepare(r->clock);
} }
/* Clears a BCH interrupt. */ /* Clears a BCH interrupt. */

Просмотреть файл

@ -18,6 +18,9 @@
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
@ -27,6 +30,7 @@
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_mtd.h>
#include "gpmi-nand.h" #include "gpmi-nand.h"
/* add our owner bbt descriptor */ /* add our owner bbt descriptor */
@ -113,7 +117,7 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
/* We use the same ECC strength for all chunks. */ /* We use the same ECC strength for all chunks. */
geo->ecc_strength = get_ecc_strength(this); geo->ecc_strength = get_ecc_strength(this);
if (!geo->ecc_strength) { if (!geo->ecc_strength) {
pr_err("We get a wrong ECC strength.\n"); pr_err("wrong ECC strength.\n");
return -EINVAL; return -EINVAL;
} }
@ -316,7 +320,7 @@ acquire_register_block(struct gpmi_nand_data *this, const char *res_name)
struct platform_device *pdev = this->pdev; struct platform_device *pdev = this->pdev;
struct resources *res = &this->resources; struct resources *res = &this->resources;
struct resource *r; struct resource *r;
void *p; void __iomem *p;
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
if (!r) { if (!r) {
@ -423,8 +427,8 @@ static int __devinit acquire_dma_channels(struct gpmi_nand_data *this)
struct platform_device *pdev = this->pdev; struct platform_device *pdev = this->pdev;
struct resource *r_dma; struct resource *r_dma;
struct device_node *dn; struct device_node *dn;
int dma_channel; u32 dma_channel;
unsigned int ret; int ret;
struct dma_chan *dma_chan; struct dma_chan *dma_chan;
dma_cap_mask_t mask; dma_cap_mask_t mask;
@ -464,9 +468,73 @@ acquire_err:
return -EINVAL; return -EINVAL;
} }
static void gpmi_put_clks(struct gpmi_nand_data *this)
{
struct resources *r = &this->resources;
struct clk *clk;
int i;
for (i = 0; i < GPMI_CLK_MAX; i++) {
clk = r->clock[i];
if (clk) {
clk_put(clk);
r->clock[i] = NULL;
}
}
}
static char *extra_clks_for_mx6q[GPMI_CLK_MAX] = {
"gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch",
};
static int __devinit gpmi_get_clks(struct gpmi_nand_data *this)
{
struct resources *r = &this->resources;
char **extra_clks = NULL;
struct clk *clk;
int i;
/* The main clock is stored in the first. */
r->clock[0] = clk_get(this->dev, "gpmi_io");
if (IS_ERR(r->clock[0]))
goto err_clock;
/* Get extra clocks */
if (GPMI_IS_MX6Q(this))
extra_clks = extra_clks_for_mx6q;
if (!extra_clks)
return 0;
for (i = 1; i < GPMI_CLK_MAX; i++) {
if (extra_clks[i - 1] == NULL)
break;
clk = clk_get(this->dev, extra_clks[i - 1]);
if (IS_ERR(clk))
goto err_clock;
r->clock[i] = clk;
}
if (GPMI_IS_MX6Q(this))
/*
* Set the default value for the gpmi clock in mx6q:
*
* If you want to use the ONFI nand which is in the
* Synchronous Mode, you should change the clock as you need.
*/
clk_set_rate(r->clock[0], 22000000);
return 0;
err_clock:
dev_dbg(this->dev, "failed in finding the clocks.\n");
gpmi_put_clks(this);
return -ENOMEM;
}
static int __devinit acquire_resources(struct gpmi_nand_data *this) static int __devinit acquire_resources(struct gpmi_nand_data *this)
{ {
struct resources *res = &this->resources;
struct pinctrl *pinctrl; struct pinctrl *pinctrl;
int ret; int ret;
@ -492,12 +560,9 @@ static int __devinit acquire_resources(struct gpmi_nand_data *this)
goto exit_pin; goto exit_pin;
} }
res->clock = clk_get(&this->pdev->dev, NULL); ret = gpmi_get_clks(this);
if (IS_ERR(res->clock)) { if (ret)
pr_err("can not get the clock\n");
ret = -ENOENT;
goto exit_clock; goto exit_clock;
}
return 0; return 0;
exit_clock: exit_clock:
@ -512,9 +577,7 @@ exit_regs:
static void release_resources(struct gpmi_nand_data *this) static void release_resources(struct gpmi_nand_data *this)
{ {
struct resources *r = &this->resources; gpmi_put_clks(this);
clk_put(r->clock);
release_register_block(this); release_register_block(this);
release_bch_irq(this); release_bch_irq(this);
release_dma_channels(this); release_dma_channels(this);
@ -667,12 +730,12 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
struct device *dev = this->dev; struct device *dev = this->dev;
/* [1] Allocate a command buffer. PAGE_SIZE is enough. */ /* [1] Allocate a command buffer. PAGE_SIZE is enough. */
this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA); this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (this->cmd_buffer == NULL) if (this->cmd_buffer == NULL)
goto error_alloc; goto error_alloc;
/* [2] Allocate a read/write data buffer. PAGE_SIZE is enough. */ /* [2] Allocate a read/write data buffer. PAGE_SIZE is enough. */
this->data_buffer_dma = kzalloc(PAGE_SIZE, GFP_DMA); this->data_buffer_dma = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (this->data_buffer_dma == NULL) if (this->data_buffer_dma == NULL)
goto error_alloc; goto error_alloc;
@ -930,7 +993,7 @@ exit_nfc:
return ret; return ret;
} }
static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
struct gpmi_nand_data *this = chip->priv; struct gpmi_nand_data *this = chip->priv;
@ -972,7 +1035,7 @@ static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
&payload_virt, &payload_phys); &payload_virt, &payload_phys);
if (ret) { if (ret) {
pr_err("Inadequate payload DMA buffer\n"); pr_err("Inadequate payload DMA buffer\n");
return; return 0;
} }
ret = send_page_prepare(this, ret = send_page_prepare(this,
@ -1002,6 +1065,8 @@ exit_auxiliary:
nfc_geo->payload_size, nfc_geo->payload_size,
payload_virt, payload_phys); payload_virt, payload_phys);
} }
return 0;
} }
/* /*
@ -1064,6 +1129,9 @@ exit_auxiliary:
* ECC-based or raw view of the page is implicit in which function it calls * ECC-based or raw view of the page is implicit in which function it calls
* (there is a similar pair of ECC-based/raw functions for writing). * (there is a similar pair of ECC-based/raw functions for writing).
* *
* FIXME: The following paragraph is incorrect, now that there exist
* ecc.read_oob_raw and ecc.write_oob_raw functions.
*
* Since MTD assumes the OOB is not covered by ECC, there is no pair of * Since MTD assumes the OOB is not covered by ECC, there is no pair of
* ECC-based/raw functions for reading or or writing the OOB. The fact that the * ECC-based/raw functions for reading or or writing the OOB. The fact that the
* caller wants an ECC-based or raw view of the page is not propagated down to * caller wants an ECC-based or raw view of the page is not propagated down to
@ -1190,7 +1258,6 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
unsigned int search_area_size_in_strides; unsigned int search_area_size_in_strides;
unsigned int stride; unsigned int stride;
unsigned int page; unsigned int page;
loff_t byte;
uint8_t *buffer = chip->buffers->databuf; uint8_t *buffer = chip->buffers->databuf;
int saved_chip_number; int saved_chip_number;
int found_an_ncb_fingerprint = false; int found_an_ncb_fingerprint = false;
@ -1207,9 +1274,8 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
dev_dbg(dev, "Scanning for an NCB fingerprint...\n"); dev_dbg(dev, "Scanning for an NCB fingerprint...\n");
for (stride = 0; stride < search_area_size_in_strides; stride++) { for (stride = 0; stride < search_area_size_in_strides; stride++) {
/* Compute the page and byte addresses. */ /* Compute the page addresses. */
page = stride * rom_geo->stride_size_in_pages; page = stride * rom_geo->stride_size_in_pages;
byte = page * mtd->writesize;
dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page); dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page);
@ -1251,7 +1317,6 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
unsigned int block; unsigned int block;
unsigned int stride; unsigned int stride;
unsigned int page; unsigned int page;
loff_t byte;
uint8_t *buffer = chip->buffers->databuf; uint8_t *buffer = chip->buffers->databuf;
int saved_chip_number; int saved_chip_number;
int status; int status;
@ -1300,9 +1365,8 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
/* Loop through the first search area, writing NCB fingerprints. */ /* Loop through the first search area, writing NCB fingerprints. */
dev_dbg(dev, "Writing NCB fingerprints...\n"); dev_dbg(dev, "Writing NCB fingerprints...\n");
for (stride = 0; stride < search_area_size_in_strides; stride++) { for (stride = 0; stride < search_area_size_in_strides; stride++) {
/* Compute the page and byte addresses. */ /* Compute the page addresses. */
page = stride * rom_geo->stride_size_in_pages; page = stride * rom_geo->stride_size_in_pages;
byte = page * mtd->writesize;
/* Write the first page of the current stride. */ /* Write the first page of the current stride. */
dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
@ -1436,6 +1500,7 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this)
/* Adjust the ECC strength according to the chip. */ /* Adjust the ECC strength according to the chip. */
this->nand.ecc.strength = this->bch_geometry.ecc_strength; this->nand.ecc.strength = this->bch_geometry.ecc_strength;
this->mtd.ecc_strength = this->bch_geometry.ecc_strength; this->mtd.ecc_strength = this->bch_geometry.ecc_strength;
this->mtd.bitflip_threshold = this->bch_geometry.ecc_strength;
/* NAND boot init, depends on the gpmi_set_geometry(). */ /* NAND boot init, depends on the gpmi_set_geometry(). */
return nand_boot_init(this); return nand_boot_init(this);
@ -1452,11 +1517,19 @@ static int gpmi_scan_bbt(struct mtd_info *mtd)
if (ret) if (ret)
return ret; return ret;
/*
* Can we enable the extra features? such as EDO or Sync mode.
*
* We do not check the return value now. That's means if we fail in
* enable the extra features, we still can run in the normal way.
*/
gpmi_extra_init(this);
/* use the default BBT implementation */ /* use the default BBT implementation */
return nand_default_bbt(mtd); return nand_default_bbt(mtd);
} }
void gpmi_nfc_exit(struct gpmi_nand_data *this) static void gpmi_nfc_exit(struct gpmi_nand_data *this)
{ {
nand_release(&this->mtd); nand_release(&this->mtd);
gpmi_free_dma_buffer(this); gpmi_free_dma_buffer(this);
@ -1497,6 +1570,8 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
chip->ecc.size = 1; chip->ecc.size = 1;
chip->ecc.strength = 8; chip->ecc.strength = 8;
chip->ecc.layout = &gpmi_hw_ecclayout; chip->ecc.layout = &gpmi_hw_ecclayout;
if (of_get_nand_on_flash_bbt(this->dev->of_node))
chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
/* Allocate a temporary DMA buffer for reading ID in the nand_scan() */ /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */
this->bch_geometry.payload_size = 1024; this->bch_geometry.payload_size = 1024;
@ -1579,6 +1654,8 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev)
if (ret) if (ret)
goto exit_nfc_init; goto exit_nfc_init;
dev_info(this->dev, "driver registered.\n");
return 0; return 0;
exit_nfc_init: exit_nfc_init:
@ -1586,10 +1663,12 @@ exit_nfc_init:
exit_acquire_resources: exit_acquire_resources:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(this); kfree(this);
dev_err(this->dev, "driver registration failed: %d\n", ret);
return ret; return ret;
} }
static int __exit gpmi_nand_remove(struct platform_device *pdev) static int __devexit gpmi_nand_remove(struct platform_device *pdev)
{ {
struct gpmi_nand_data *this = platform_get_drvdata(pdev); struct gpmi_nand_data *this = platform_get_drvdata(pdev);
@ -1606,29 +1685,10 @@ static struct platform_driver gpmi_nand_driver = {
.of_match_table = gpmi_nand_id_table, .of_match_table = gpmi_nand_id_table,
}, },
.probe = gpmi_nand_probe, .probe = gpmi_nand_probe,
.remove = __exit_p(gpmi_nand_remove), .remove = __devexit_p(gpmi_nand_remove),
.id_table = gpmi_ids, .id_table = gpmi_ids,
}; };
module_platform_driver(gpmi_nand_driver);
static int __init gpmi_nand_init(void)
{
int err;
err = platform_driver_register(&gpmi_nand_driver);
if (err == 0)
printk(KERN_INFO "GPMI NAND driver registered. (IMX)\n");
else
pr_err("i.MX GPMI NAND driver registration failed\n");
return err;
}
static void __exit gpmi_nand_exit(void)
{
platform_driver_unregister(&gpmi_nand_driver);
}
module_init(gpmi_nand_init);
module_exit(gpmi_nand_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");

Просмотреть файл

@ -22,14 +22,15 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/fsl/mxs-dma.h> #include <linux/fsl/mxs-dma.h>
#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */
struct resources { struct resources {
void *gpmi_regs; void __iomem *gpmi_regs;
void *bch_regs; void __iomem *bch_regs;
unsigned int bch_low_interrupt; unsigned int bch_low_interrupt;
unsigned int bch_high_interrupt; unsigned int bch_high_interrupt;
unsigned int dma_low_channel; unsigned int dma_low_channel;
unsigned int dma_high_channel; unsigned int dma_high_channel;
struct clk *clock; struct clk *clock[GPMI_CLK_MAX];
}; };
/** /**
@ -121,6 +122,11 @@ struct nand_timing {
}; };
struct gpmi_nand_data { struct gpmi_nand_data {
/* flags */
#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
#define GPMI_TIMING_INIT_OK (1 << 1)
int flags;
/* System Interface */ /* System Interface */
struct device *dev; struct device *dev;
struct platform_device *pdev; struct platform_device *pdev;
@ -131,6 +137,7 @@ struct gpmi_nand_data {
/* Flash Hardware */ /* Flash Hardware */
struct nand_timing timing; struct nand_timing timing;
int timing_mode;
/* BCH */ /* BCH */
struct bch_geometry bch_geometry; struct bch_geometry bch_geometry;
@ -188,16 +195,28 @@ struct gpmi_nand_data {
* @data_setup_in_cycles: The data setup time, in cycles. * @data_setup_in_cycles: The data setup time, in cycles.
* @data_hold_in_cycles: The data hold time, in cycles. * @data_hold_in_cycles: The data hold time, in cycles.
* @address_setup_in_cycles: The address setup time, in cycles. * @address_setup_in_cycles: The address setup time, in cycles.
* @device_busy_timeout: The timeout waiting for NAND Ready/Busy,
* this value is the number of cycles multiplied
* by 4096.
* @use_half_periods: Indicates the clock is running slowly, so the * @use_half_periods: Indicates the clock is running slowly, so the
* NFC DLL should use half-periods. * NFC DLL should use half-periods.
* @sample_delay_factor: The sample delay factor. * @sample_delay_factor: The sample delay factor.
* @wrn_dly_sel: The delay on the GPMI write strobe.
*/ */
struct gpmi_nfc_hardware_timing { struct gpmi_nfc_hardware_timing {
/* for HW_GPMI_TIMING0 */
uint8_t data_setup_in_cycles; uint8_t data_setup_in_cycles;
uint8_t data_hold_in_cycles; uint8_t data_hold_in_cycles;
uint8_t address_setup_in_cycles; uint8_t address_setup_in_cycles;
/* for HW_GPMI_TIMING1 */
uint16_t device_busy_timeout;
#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/
/* for HW_GPMI_CTRL1 */
bool use_half_periods; bool use_half_periods;
uint8_t sample_delay_factor; uint8_t sample_delay_factor;
uint8_t wrn_dly_sel;
}; };
/** /**
@ -246,6 +265,7 @@ extern int start_dma_with_bch_irq(struct gpmi_nand_data *,
/* GPMI-NAND helper function library */ /* GPMI-NAND helper function library */
extern int gpmi_init(struct gpmi_nand_data *); extern int gpmi_init(struct gpmi_nand_data *);
extern int gpmi_extra_init(struct gpmi_nand_data *);
extern void gpmi_clear_bch(struct gpmi_nand_data *); extern void gpmi_clear_bch(struct gpmi_nand_data *);
extern void gpmi_dump_info(struct gpmi_nand_data *); extern void gpmi_dump_info(struct gpmi_nand_data *);
extern int bch_set_geometry(struct gpmi_nand_data *); extern int bch_set_geometry(struct gpmi_nand_data *);

Просмотреть файл

@ -108,6 +108,15 @@
#define HW_GPMI_CTRL1_CLR 0x00000068 #define HW_GPMI_CTRL1_CLR 0x00000068
#define HW_GPMI_CTRL1_TOG 0x0000006c #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 BM_GPMI_CTRL1_BCH_MODE (1 << 18)
#define BP_GPMI_CTRL1_DLL_ENABLE 17 #define BP_GPMI_CTRL1_DLL_ENABLE 17
@ -154,6 +163,9 @@
#define HW_GPMI_TIMING1 0x00000080 #define HW_GPMI_TIMING1 0x00000080
#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16 #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_TIMING2 0x00000090
#define HW_GPMI_DATA 0x000000a0 #define HW_GPMI_DATA 0x000000a0

Просмотреть файл

@ -0,0 +1,924 @@
/*
* Driver for NAND MLC Controller in LPC32xx
*
* Author: Roland Stigge <stigge@antcom.de>
*
* Copyright © 2011 WORK Microwave GmbH
* Copyright © 2011, 2012 Roland Stigge
*
* 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 Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* NAND Flash Controller Operation:
* - Read: Auto Decode
* - Write: Auto Encode
* - Tested Page Sizes: 2048, 4096
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/of_gpio.h>
#include <linux/mtd/lpc32xx_mlc.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/mtd/nand_ecc.h>
#define DRV_NAME "lpc32xx_mlc"
/**********************************************************************
* MLC NAND controller register offsets
**********************************************************************/
#define MLC_BUFF(x) (x + 0x00000)
#define MLC_DATA(x) (x + 0x08000)
#define MLC_CMD(x) (x + 0x10000)
#define MLC_ADDR(x) (x + 0x10004)
#define MLC_ECC_ENC_REG(x) (x + 0x10008)
#define MLC_ECC_DEC_REG(x) (x + 0x1000C)
#define MLC_ECC_AUTO_ENC_REG(x) (x + 0x10010)
#define MLC_ECC_AUTO_DEC_REG(x) (x + 0x10014)
#define MLC_RPR(x) (x + 0x10018)
#define MLC_WPR(x) (x + 0x1001C)
#define MLC_RUBP(x) (x + 0x10020)
#define MLC_ROBP(x) (x + 0x10024)
#define MLC_SW_WP_ADD_LOW(x) (x + 0x10028)
#define MLC_SW_WP_ADD_HIG(x) (x + 0x1002C)
#define MLC_ICR(x) (x + 0x10030)
#define MLC_TIME_REG(x) (x + 0x10034)
#define MLC_IRQ_MR(x) (x + 0x10038)
#define MLC_IRQ_SR(x) (x + 0x1003C)
#define MLC_LOCK_PR(x) (x + 0x10044)
#define MLC_ISR(x) (x + 0x10048)
#define MLC_CEH(x) (x + 0x1004C)
/**********************************************************************
* MLC_CMD bit definitions
**********************************************************************/
#define MLCCMD_RESET 0xFF
/**********************************************************************
* MLC_ICR bit definitions
**********************************************************************/
#define MLCICR_WPROT (1 << 3)
#define MLCICR_LARGEBLOCK (1 << 2)
#define MLCICR_LONGADDR (1 << 1)
#define MLCICR_16BIT (1 << 0) /* unsupported by LPC32x0! */
/**********************************************************************
* MLC_TIME_REG bit definitions
**********************************************************************/
#define MLCTIMEREG_TCEA_DELAY(n) (((n) & 0x03) << 24)
#define MLCTIMEREG_BUSY_DELAY(n) (((n) & 0x1F) << 19)
#define MLCTIMEREG_NAND_TA(n) (((n) & 0x07) << 16)
#define MLCTIMEREG_RD_HIGH(n) (((n) & 0x0F) << 12)
#define MLCTIMEREG_RD_LOW(n) (((n) & 0x0F) << 8)
#define MLCTIMEREG_WR_HIGH(n) (((n) & 0x0F) << 4)
#define MLCTIMEREG_WR_LOW(n) (((n) & 0x0F) << 0)
/**********************************************************************
* MLC_IRQ_MR and MLC_IRQ_SR bit definitions
**********************************************************************/
#define MLCIRQ_NAND_READY (1 << 5)
#define MLCIRQ_CONTROLLER_READY (1 << 4)
#define MLCIRQ_DECODE_FAILURE (1 << 3)
#define MLCIRQ_DECODE_ERROR (1 << 2)
#define MLCIRQ_ECC_READY (1 << 1)
#define MLCIRQ_WRPROT_FAULT (1 << 0)
/**********************************************************************
* MLC_LOCK_PR bit definitions
**********************************************************************/
#define MLCLOCKPR_MAGIC 0xA25E
/**********************************************************************
* MLC_ISR bit definitions
**********************************************************************/
#define MLCISR_DECODER_FAILURE (1 << 6)
#define MLCISR_ERRORS ((1 << 4) | (1 << 5))
#define MLCISR_ERRORS_DETECTED (1 << 3)
#define MLCISR_ECC_READY (1 << 2)
#define MLCISR_CONTROLLER_READY (1 << 1)
#define MLCISR_NAND_READY (1 << 0)
/**********************************************************************
* MLC_CEH bit definitions
**********************************************************************/
#define MLCCEH_NORMAL (1 << 0)
struct lpc32xx_nand_cfg_mlc {
uint32_t tcea_delay;
uint32_t busy_delay;
uint32_t nand_ta;
uint32_t rd_high;
uint32_t rd_low;
uint32_t wr_high;
uint32_t wr_low;
int wp_gpio;
struct mtd_partition *parts;
unsigned num_parts;
};
static struct nand_ecclayout lpc32xx_nand_oob = {
.eccbytes = 40,
.eccpos = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
.oobfree = {
{ .offset = 0,
.length = 6, },
{ .offset = 16,
.length = 6, },
{ .offset = 32,
.length = 6, },
{ .offset = 48,
.length = 6, },
},
};
static struct nand_bbt_descr lpc32xx_nand_bbt = {
.options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB |
NAND_BBT_WRITE,
.pages = { 524224, 0, 0, 0, 0, 0, 0, 0 },
};
static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = {
.options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB |
NAND_BBT_WRITE,
.pages = { 524160, 0, 0, 0, 0, 0, 0, 0 },
};
struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_mlc_platform_data *pdata;
struct clk *clk;
struct mtd_info mtd;
void __iomem *io_base;
int irq;
struct lpc32xx_nand_cfg_mlc *ncfg;
struct completion comp_nand;
struct completion comp_controller;
uint32_t llptr;
/*
* Physical addresses of ECC buffer, DMA data buffers, OOB data buffer
*/
dma_addr_t oob_buf_phy;
/*
* Virtual addresses of ECC buffer, DMA data buffers, OOB data buffer
*/
uint8_t *oob_buf;
/* Physical address of DMA base address */
dma_addr_t io_base_phy;
struct completion comp_dma;
struct dma_chan *dma_chan;
struct dma_slave_config dma_slave_config;
struct scatterlist sgl;
uint8_t *dma_buf;
uint8_t *dummy_buf;
int mlcsubpages; /* number of 512bytes-subpages */
};
/*
* Activate/Deactivate DMA Operation:
*
* Using the PL080 DMA Controller for transferring the 512 byte subpages
* instead of doing readl() / writel() in a loop slows it down significantly.
* Measurements via getnstimeofday() upon 512 byte subpage reads reveal:
*
* - readl() of 128 x 32 bits in a loop: ~20us
* - DMA read of 512 bytes (32 bit, 4...128 words bursts): ~60us
* - DMA read of 512 bytes (32 bit, no bursts): ~100us
*
* This applies to the transfer itself. In the DMA case: only the
* wait_for_completion() (DMA setup _not_ included).
*
* Note that the 512 bytes subpage transfer is done directly from/to a
* FIFO/buffer inside the NAND controller. Most of the time (~400-800us for a
* 2048 bytes page) is spent waiting for the NAND IRQ, anyway. (The NAND
* controller transferring data between its internal buffer to/from the NAND
* chip.)
*
* Therefore, using the PL080 DMA is disabled by default, for now.
*
*/
static int use_dma;
static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host)
{
uint32_t clkrate, tmp;
/* Reset MLC controller */
writel(MLCCMD_RESET, MLC_CMD(host->io_base));
udelay(1000);
/* Get base clock for MLC block */
clkrate = clk_get_rate(host->clk);
if (clkrate == 0)
clkrate = 104000000;
/* Unlock MLC_ICR
* (among others, will be locked again automatically) */
writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base));
/* Configure MLC Controller: Large Block, 5 Byte Address */
tmp = MLCICR_LARGEBLOCK | MLCICR_LONGADDR;
writel(tmp, MLC_ICR(host->io_base));
/* Unlock MLC_TIME_REG
* (among others, will be locked again automatically) */
writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base));
/* Compute clock setup values, see LPC and NAND manual */
tmp = 0;
tmp |= MLCTIMEREG_TCEA_DELAY(clkrate / host->ncfg->tcea_delay + 1);
tmp |= MLCTIMEREG_BUSY_DELAY(clkrate / host->ncfg->busy_delay + 1);
tmp |= MLCTIMEREG_NAND_TA(clkrate / host->ncfg->nand_ta + 1);
tmp |= MLCTIMEREG_RD_HIGH(clkrate / host->ncfg->rd_high + 1);
tmp |= MLCTIMEREG_RD_LOW(clkrate / host->ncfg->rd_low);
tmp |= MLCTIMEREG_WR_HIGH(clkrate / host->ncfg->wr_high + 1);
tmp |= MLCTIMEREG_WR_LOW(clkrate / host->ncfg->wr_low);
writel(tmp, MLC_TIME_REG(host->io_base));
/* Enable IRQ for CONTROLLER_READY and NAND_READY */
writeb(MLCIRQ_CONTROLLER_READY | MLCIRQ_NAND_READY,
MLC_IRQ_MR(host->io_base));
/* Normal nCE operation: nCE controlled by controller */
writel(MLCCEH_NORMAL, MLC_CEH(host->io_base));
}
/*
* Hardware specific access to control lines
*/
static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct nand_chip *nand_chip = mtd->priv;
struct lpc32xx_nand_host *host = nand_chip->priv;
if (cmd != NAND_CMD_NONE) {
if (ctrl & NAND_CLE)
writel(cmd, MLC_CMD(host->io_base));
else
writel(cmd, MLC_ADDR(host->io_base));
}
}
/*
* Read Device Ready (NAND device _and_ controller ready)
*/
static int lpc32xx_nand_device_ready(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
struct lpc32xx_nand_host *host = nand_chip->priv;
if ((readb(MLC_ISR(host->io_base)) &
(MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) ==
(MLCISR_CONTROLLER_READY | MLCISR_NAND_READY))
return 1;
return 0;
}
static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host)
{
uint8_t sr;
/* Clear interrupt flag by reading status */
sr = readb(MLC_IRQ_SR(host->io_base));
if (sr & MLCIRQ_NAND_READY)
complete(&host->comp_nand);
if (sr & MLCIRQ_CONTROLLER_READY)
complete(&host->comp_controller);
return IRQ_HANDLED;
}
static int lpc32xx_waitfunc_nand(struct mtd_info *mtd, struct nand_chip *chip)
{
struct lpc32xx_nand_host *host = chip->priv;
if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)
goto exit;
wait_for_completion(&host->comp_nand);
while (!(readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)) {
/* Seems to be delayed sometimes by controller */
dev_dbg(&mtd->dev, "Warning: NAND not ready.\n");
cpu_relax();
}
exit:
return NAND_STATUS_READY;
}
static int lpc32xx_waitfunc_controller(struct mtd_info *mtd,
struct nand_chip *chip)
{
struct lpc32xx_nand_host *host = chip->priv;
if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY)
goto exit;
wait_for_completion(&host->comp_controller);
while (!(readb(MLC_ISR(host->io_base)) &
MLCISR_CONTROLLER_READY)) {
dev_dbg(&mtd->dev, "Warning: Controller not ready.\n");
cpu_relax();
}
exit:
return NAND_STATUS_READY;
}
static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
{
lpc32xx_waitfunc_nand(mtd, chip);
lpc32xx_waitfunc_controller(mtd, chip);
return NAND_STATUS_READY;
}
/*
* Enable NAND write protect
*/
static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
{
if (gpio_is_valid(host->ncfg->wp_gpio))
gpio_set_value(host->ncfg->wp_gpio, 0);
}
/*
* Disable NAND write protect
*/
static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
{
if (gpio_is_valid(host->ncfg->wp_gpio))
gpio_set_value(host->ncfg->wp_gpio, 1);
}
static void lpc32xx_dma_complete_func(void *completion)
{
complete(completion);
}
static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
enum dma_transfer_direction dir)
{
struct nand_chip *chip = mtd->priv;
struct lpc32xx_nand_host *host = chip->priv;
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int res;
sg_init_one(&host->sgl, mem, len);
res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
if (res != 1) {
dev_err(mtd->dev.parent, "Failed to map sg list\n");
return -ENXIO;
}
desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir,
flags);
if (!desc) {
dev_err(mtd->dev.parent, "Failed to prepare slave sg\n");
goto out1;
}
init_completion(&host->comp_dma);
desc->callback = lpc32xx_dma_complete_func;
desc->callback_param = &host->comp_dma;
dmaengine_submit(desc);
dma_async_issue_pending(host->dma_chan);
wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000));
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
return 0;
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
return -ENXIO;
}
static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
struct lpc32xx_nand_host *host = chip->priv;
int i, j;
uint8_t *oobbuf = chip->oob_poi;
uint32_t mlc_isr;
int res;
uint8_t *dma_buf;
bool dma_mapped;
if ((void *)buf <= high_memory) {
dma_buf = buf;
dma_mapped = true;
} else {
dma_buf = host->dma_buf;
dma_mapped = false;
}
/* Writing Command and Address */
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
/* For all sub-pages */
for (i = 0; i < host->mlcsubpages; i++) {
/* Start Auto Decode Command */
writeb(0x00, MLC_ECC_AUTO_DEC_REG(host->io_base));
/* Wait for Controller Ready */
lpc32xx_waitfunc_controller(mtd, chip);
/* Check ECC Error status */
mlc_isr = readl(MLC_ISR(host->io_base));
if (mlc_isr & MLCISR_DECODER_FAILURE) {
mtd->ecc_stats.failed++;
dev_warn(&mtd->dev, "%s: DECODER_FAILURE\n", __func__);
} else if (mlc_isr & MLCISR_ERRORS_DETECTED) {
mtd->ecc_stats.corrected += ((mlc_isr >> 4) & 0x3) + 1;
}
/* Read 512 + 16 Bytes */
if (use_dma) {
res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512,
DMA_DEV_TO_MEM);
if (res)
return res;
} else {
for (j = 0; j < (512 >> 2); j++) {
*((uint32_t *)(buf)) =
readl(MLC_BUFF(host->io_base));
buf += 4;
}
}
for (j = 0; j < (16 >> 2); j++) {
*((uint32_t *)(oobbuf)) =
readl(MLC_BUFF(host->io_base));
oobbuf += 4;
}
}
if (use_dma && !dma_mapped)
memcpy(buf, dma_buf, mtd->writesize);
return 0;
}
static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
struct lpc32xx_nand_host *host = chip->priv;
const uint8_t *oobbuf = chip->oob_poi;
uint8_t *dma_buf = (uint8_t *)buf;
int res;
int i, j;
if (use_dma && (void *)buf >= high_memory) {
dma_buf = host->dma_buf;
memcpy(dma_buf, buf, mtd->writesize);
}
for (i = 0; i < host->mlcsubpages; i++) {
/* Start Encode */
writeb(0x00, MLC_ECC_ENC_REG(host->io_base));
/* Write 512 + 6 Bytes to Buffer */
if (use_dma) {
res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512,
DMA_MEM_TO_DEV);
if (res)
return res;
} else {
for (j = 0; j < (512 >> 2); j++) {
writel(*((uint32_t *)(buf)),
MLC_BUFF(host->io_base));
buf += 4;
}
}
writel(*((uint32_t *)(oobbuf)), MLC_BUFF(host->io_base));
oobbuf += 4;
writew(*((uint16_t *)(oobbuf)), MLC_BUFF(host->io_base));
oobbuf += 12;
/* Auto Encode w/ Bit 8 = 0 (see LPC MLC Controller manual) */
writeb(0x00, MLC_ECC_AUTO_ENC_REG(host->io_base));
/* Wait for Controller Ready */
lpc32xx_waitfunc_controller(mtd, chip);
}
return 0;
}
static int lpc32xx_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page,
int cached, int raw)
{
int res;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
res = lpc32xx_write_page_lowlevel(mtd, chip, buf, oob_required);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
lpc32xx_waitfunc(mtd, chip);
return res;
}
static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
struct lpc32xx_nand_host *host = chip->priv;
/* Read whole page - necessary with MLC controller! */
lpc32xx_read_page(mtd, chip, host->dummy_buf, 1, page);
return 0;
}
static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
/* None, write_oob conflicts with the automatic LPC MLC ECC decoder! */
return 0;
}
/* Prepares MLC for transfers with H/W ECC enabled: always enabled anyway */
static void lpc32xx_ecc_enable(struct mtd_info *mtd, int mode)
{
/* Always enabled! */
}
static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host)
{
struct mtd_info *mtd = &host->mtd;
dma_cap_mask_t mask;
if (!host->pdata || !host->pdata->dma_filter) {
dev_err(mtd->dev.parent, "no DMA platform data\n");
return -ENOENT;
}
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter,
"nand-mlc");
if (!host->dma_chan) {
dev_err(mtd->dev.parent, "Failed to request DMA channel\n");
return -EBUSY;
}
/*
* Set direction to a sensible value even if the dmaengine driver
* should ignore it. With the default (DMA_MEM_TO_MEM), the amba-pl08x
* driver criticizes it as "alien transfer direction".
*/
host->dma_slave_config.direction = DMA_DEV_TO_MEM;
host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
host->dma_slave_config.src_maxburst = 128;
host->dma_slave_config.dst_maxburst = 128;
/* DMA controller does flow control: */
host->dma_slave_config.device_fc = false;
host->dma_slave_config.src_addr = MLC_BUFF(host->io_base_phy);
host->dma_slave_config.dst_addr = MLC_BUFF(host->io_base_phy);
if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) {
dev_err(mtd->dev.parent, "Failed to setup DMA slave\n");
goto out1;
}
return 0;
out1:
dma_release_channel(host->dma_chan);
return -ENXIO;
}
static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev)
{
struct lpc32xx_nand_cfg_mlc *ncfg;
struct device_node *np = dev->of_node;
ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL);
if (!ncfg) {
dev_err(dev, "could not allocate memory for platform data\n");
return NULL;
}
of_property_read_u32(np, "nxp,tcea-delay", &ncfg->tcea_delay);
of_property_read_u32(np, "nxp,busy-delay", &ncfg->busy_delay);
of_property_read_u32(np, "nxp,nand-ta", &ncfg->nand_ta);
of_property_read_u32(np, "nxp,rd-high", &ncfg->rd_high);
of_property_read_u32(np, "nxp,rd-low", &ncfg->rd_low);
of_property_read_u32(np, "nxp,wr-high", &ncfg->wr_high);
of_property_read_u32(np, "nxp,wr-low", &ncfg->wr_low);
if (!ncfg->tcea_delay || !ncfg->busy_delay || !ncfg->nand_ta ||
!ncfg->rd_high || !ncfg->rd_low || !ncfg->wr_high ||
!ncfg->wr_low) {
dev_err(dev, "chip parameters not specified correctly\n");
return NULL;
}
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
return ncfg;
}
/*
* Probe for NAND controller
*/
static int __devinit lpc32xx_nand_probe(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct resource *rc;
int res;
struct mtd_part_parser_data ppdata = {};
/* Allocate memory for the device structure (and zero it) */
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) {
dev_err(&pdev->dev, "failed to allocate device structure.\n");
return -ENOMEM;
}
rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (rc == NULL) {
dev_err(&pdev->dev, "No memory resource found for device!\r\n");
return -ENXIO;
}
host->io_base = devm_request_and_ioremap(&pdev->dev, rc);
if (host->io_base == NULL) {
dev_err(&pdev->dev, "ioremap failed\n");
return -EIO;
}
host->io_base_phy = rc->start;
mtd = &host->mtd;
nand_chip = &host->nand_chip;
if (pdev->dev.of_node)
host->ncfg = lpc32xx_parse_dt(&pdev->dev);
if (!host->ncfg) {
dev_err(&pdev->dev,
"Missing or bad NAND config from device tree\n");
return -ENOENT;
}
if (host->ncfg->wp_gpio == -EPROBE_DEFER)
return -EPROBE_DEFER;
if (gpio_is_valid(host->ncfg->wp_gpio) &&
gpio_request(host->ncfg->wp_gpio, "NAND WP")) {
dev_err(&pdev->dev, "GPIO not available\n");
return -EBUSY;
}
lpc32xx_wp_disable(host);
host->pdata = pdev->dev.platform_data;
nand_chip->priv = host; /* link the private data structures */
mtd->priv = nand_chip;
mtd->owner = THIS_MODULE;
mtd->dev.parent = &pdev->dev;
/* Get NAND clock */
host->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "Clock initialization failure\n");
res = -ENOENT;
goto err_exit1;
}
clk_enable(host->clk);
nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl;
nand_chip->dev_ready = lpc32xx_nand_device_ready;
nand_chip->chip_delay = 25; /* us */
nand_chip->IO_ADDR_R = MLC_DATA(host->io_base);
nand_chip->IO_ADDR_W = MLC_DATA(host->io_base);
/* Init NAND controller */
lpc32xx_nand_setup(host);
platform_set_drvdata(pdev, host);
/* Initialize function pointers */
nand_chip->ecc.hwctl = lpc32xx_ecc_enable;
nand_chip->ecc.read_page_raw = lpc32xx_read_page;
nand_chip->ecc.read_page = lpc32xx_read_page;
nand_chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel;
nand_chip->ecc.write_page = lpc32xx_write_page_lowlevel;
nand_chip->ecc.write_oob = lpc32xx_write_oob;
nand_chip->ecc.read_oob = lpc32xx_read_oob;
nand_chip->ecc.strength = 4;
nand_chip->write_page = lpc32xx_write_page;
nand_chip->waitfunc = lpc32xx_waitfunc;
nand_chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
nand_chip->bbt_td = &lpc32xx_nand_bbt;
nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror;
/* bitflip_threshold's default is defined as ecc_strength anyway.
* Unfortunately, it is set only later at add_mtd_device(). Meanwhile
* being 0, it causes bad block table scanning errors in
* nand_scan_tail(), so preparing it here. */
mtd->bitflip_threshold = nand_chip->ecc.strength;
if (use_dma) {
res = lpc32xx_dma_setup(host);
if (res) {
res = -EIO;
goto err_exit2;
}
}
/*
* Scan to find existance of the device and
* Get the type of NAND device SMALL block or LARGE block
*/
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
goto err_exit3;
}
host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL);
if (!host->dma_buf) {
dev_err(&pdev->dev, "Error allocating dma_buf memory\n");
res = -ENOMEM;
goto err_exit3;
}
host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL);
if (!host->dummy_buf) {
dev_err(&pdev->dev, "Error allocating dummy_buf memory\n");
res = -ENOMEM;
goto err_exit3;
}
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = mtd->writesize;
nand_chip->ecc.layout = &lpc32xx_nand_oob;
host->mlcsubpages = mtd->writesize / 512;
/* initially clear interrupt status */
readb(MLC_IRQ_SR(host->io_base));
init_completion(&host->comp_nand);
init_completion(&host->comp_controller);
host->irq = platform_get_irq(pdev, 0);
if ((host->irq < 0) || (host->irq >= NR_IRQS)) {
dev_err(&pdev->dev, "failed to get platform irq\n");
res = -EINVAL;
goto err_exit3;
}
if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq,
IRQF_TRIGGER_HIGH, DRV_NAME, host)) {
dev_err(&pdev->dev, "Error requesting NAND IRQ\n");
res = -ENXIO;
goto err_exit3;
}
/*
* Fills out all the uninitialized function pointers with the defaults
* And scans for a bad block table if appropriate.
*/
if (nand_scan_tail(mtd)) {
res = -ENXIO;
goto err_exit4;
}
mtd->name = DRV_NAME;
ppdata.of_node = pdev->dev.of_node;
res = mtd_device_parse_register(mtd, NULL, &ppdata, host->ncfg->parts,
host->ncfg->num_parts);
if (!res)
return res;
nand_release(mtd);
err_exit4:
free_irq(host->irq, host);
err_exit3:
if (use_dma)
dma_release_channel(host->dma_chan);
err_exit2:
clk_disable(host->clk);
clk_put(host->clk);
platform_set_drvdata(pdev, NULL);
err_exit1:
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);
return res;
}
/*
* Remove NAND device
*/
static int __devexit lpc32xx_nand_remove(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
struct mtd_info *mtd = &host->mtd;
nand_release(mtd);
free_irq(host->irq, host);
if (use_dma)
dma_release_channel(host->dma_chan);
clk_disable(host->clk);
clk_put(host->clk);
platform_set_drvdata(pdev, NULL);
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);
return 0;
}
#ifdef CONFIG_PM
static int lpc32xx_nand_resume(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
/* Re-enable NAND clock */
clk_enable(host->clk);
/* Fresh init of NAND controller */
lpc32xx_nand_setup(host);
/* Disable write protect */
lpc32xx_wp_disable(host);
return 0;
}
static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
/* Enable write protect for safety */
lpc32xx_wp_enable(host);
/* Disable clock */
clk_disable(host->clk);
return 0;
}
#else
#define lpc32xx_nand_resume NULL
#define lpc32xx_nand_suspend NULL
#endif
static const struct of_device_id lpc32xx_nand_match[] = {
{ .compatible = "nxp,lpc3220-mlc" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
static struct platform_driver lpc32xx_nand_driver = {
.probe = lpc32xx_nand_probe,
.remove = __devexit_p(lpc32xx_nand_remove),
.resume = lpc32xx_nand_resume,
.suspend = lpc32xx_nand_suspend,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(lpc32xx_nand_match),
},
};
module_platform_driver(lpc32xx_nand_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX MLC controller");

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -506,27 +506,6 @@ static void mpc5121_nfc_write_buf(struct mtd_info *mtd,
mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1);
} }
/* Compare buffer with NAND flash */
static int mpc5121_nfc_verify_buf(struct mtd_info *mtd,
const u_char *buf, int len)
{
u_char tmp[256];
uint bsize;
while (len) {
bsize = min(len, 256);
mpc5121_nfc_read_buf(mtd, tmp, bsize);
if (memcmp(buf, tmp, bsize))
return 1;
buf += bsize;
len -= bsize;
}
return 0;
}
/* Read byte from NFC buffers */ /* Read byte from NFC buffers */
static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd)
{ {
@ -732,7 +711,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
chip->read_word = mpc5121_nfc_read_word; chip->read_word = mpc5121_nfc_read_word;
chip->read_buf = mpc5121_nfc_read_buf; chip->read_buf = mpc5121_nfc_read_buf;
chip->write_buf = mpc5121_nfc_write_buf; chip->write_buf = mpc5121_nfc_write_buf;
chip->verify_buf = mpc5121_nfc_verify_buf;
chip->select_chip = mpc5121_nfc_select_chip; chip->select_chip = mpc5121_nfc_select_chip;
chip->bbt_options = NAND_BBT_USE_FLASH; chip->bbt_options = NAND_BBT_USE_FLASH;
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;

Просмотреть файл

@ -43,8 +43,8 @@
#define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) #define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35())
#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21()) #define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21())
#define nfc_is_v3_2() (cpu_is_mx51() || cpu_is_mx53()) #define nfc_is_v3_2a() cpu_is_mx51()
#define nfc_is_v3() nfc_is_v3_2() #define nfc_is_v3_2b() cpu_is_mx53()
/* Addresses for NFC registers */ /* Addresses for NFC registers */
#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) #define NFC_V1_V2_BUF_SIZE (host->regs + 0x00)
@ -122,7 +122,7 @@
#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) #define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4)
#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) #define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5)
#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) #define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6)
#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) #define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift)
#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) #define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12)
#define NFC_V3_CONFIG2_INT_MSK (1 << 15) #define NFC_V3_CONFIG2_INT_MSK (1 << 15)
#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) #define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24)
@ -174,6 +174,7 @@ struct mxc_nand_devtype_data {
int spare_len; int spare_len;
int eccbytes; int eccbytes;
int eccsize; int eccsize;
int ppb_shift;
}; };
struct mxc_nand_host { struct mxc_nand_host {
@ -745,14 +746,6 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
host->buf_start += n; host->buf_start += n;
} }
/* Used by the upper layer to verify the data in NAND Flash
* with the data in the buf. */
static int mxc_nand_verify_buf(struct mtd_info *mtd,
const u_char *buf, int len)
{
return -EFAULT;
}
/* This function is used by upper layer for select and /* This function is used by upper layer for select and
* deselect of the NAND chip */ * deselect of the NAND chip */
static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip)
@ -784,7 +777,7 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
if (chip == -1) { if (chip == -1) {
/* Disable the NFC clock */ /* Disable the NFC clock */
if (host->clk_act) { if (host->clk_act) {
clk_disable(host->clk); clk_disable_unprepare(host->clk);
host->clk_act = 0; host->clk_act = 0;
} }
return; return;
@ -792,7 +785,7 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
if (!host->clk_act) { if (!host->clk_act) {
/* Enable the NFC clock */ /* Enable the NFC clock */
clk_enable(host->clk); clk_prepare_enable(host->clk);
host->clk_act = 1; host->clk_act = 1;
} }
@ -1021,7 +1014,9 @@ static void preset_v3(struct mtd_info *mtd)
} }
if (mtd->writesize) { if (mtd->writesize) {
config2 |= NFC_V3_CONFIG2_PPB(ffs(mtd->erasesize / mtd->writesize) - 6); config2 |= NFC_V3_CONFIG2_PPB(
ffs(mtd->erasesize / mtd->writesize) - 6,
host->devtype_data->ppb_shift);
host->eccsize = get_eccsize(mtd); host->eccsize = get_eccsize(mtd);
if (host->eccsize == 8) if (host->eccsize == 8)
config2 |= NFC_V3_CONFIG2_ECC_MODE_8; config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
@ -1234,7 +1229,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
.eccsize = 0, .eccsize = 0,
}; };
/* v3: i.MX51, i.MX53 */ /* v3.2a: i.MX51 */
static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
.preset = preset_v3, .preset = preset_v3,
.send_cmd = send_cmd_v3, .send_cmd = send_cmd_v3,
@ -1258,6 +1253,34 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
.spare_len = 64, .spare_len = 64,
.eccbytes = 0, .eccbytes = 0,
.eccsize = 0, .eccsize = 0,
.ppb_shift = 7,
};
/* v3.2b: i.MX53 */
static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
.preset = preset_v3,
.send_cmd = send_cmd_v3,
.send_addr = send_addr_v3,
.send_page = send_page_v3,
.send_read_id = send_read_id_v3,
.get_dev_status = get_dev_status_v3,
.check_int = check_int_v3,
.irq_control = irq_control_v3,
.get_ecc_status = get_ecc_status_v3,
.ecclayout_512 = &nandv2_hw_eccoob_smallpage,
.ecclayout_2k = &nandv2_hw_eccoob_largepage,
.ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */
.select_chip = mxc_nand_select_chip_v1_v3,
.correct_data = mxc_nand_correct_data_v2_v3,
.irqpending_quirk = 0,
.needs_ip = 1,
.regs_offset = 0,
.spare0_offset = 0x1000,
.axi_offset = 0x1e00,
.spare_len = 64,
.eccbytes = 0,
.eccsize = 0,
.ppb_shift = 8,
}; };
#ifdef CONFIG_OF_MTD #ifdef CONFIG_OF_MTD
@ -1274,6 +1297,9 @@ static const struct of_device_id mxcnd_dt_ids[] = {
}, { }, {
.compatible = "fsl,imx51-nand", .compatible = "fsl,imx51-nand",
.data = &imx51_nand_devtype_data, .data = &imx51_nand_devtype_data,
}, {
.compatible = "fsl,imx53-nand",
.data = &imx53_nand_devtype_data,
}, },
{ /* sentinel */ } { /* sentinel */ }
}; };
@ -1327,15 +1353,17 @@ static int __init mxcnd_probe_pdata(struct mxc_nand_host *host)
host->devtype_data = &imx27_nand_devtype_data; host->devtype_data = &imx27_nand_devtype_data;
} else if (nfc_is_v21()) { } else if (nfc_is_v21()) {
host->devtype_data = &imx25_nand_devtype_data; host->devtype_data = &imx25_nand_devtype_data;
} else if (nfc_is_v3_2()) { } else if (nfc_is_v3_2a()) {
host->devtype_data = &imx51_nand_devtype_data; host->devtype_data = &imx51_nand_devtype_data;
} else if (nfc_is_v3_2b()) {
host->devtype_data = &imx53_nand_devtype_data;
} else } else
BUG(); BUG();
return 0; return 0;
} }
static int __init mxcnd_probe(struct platform_device *pdev) static int __devinit mxcnd_probe(struct platform_device *pdev)
{ {
struct nand_chip *this; struct nand_chip *this;
struct mtd_info *mtd; struct mtd_info *mtd;
@ -1344,8 +1372,8 @@ static int __init mxcnd_probe(struct platform_device *pdev)
int err = 0; int err = 0;
/* Allocate memory for MTD device structure and private data */ /* Allocate memory for MTD device structure and private data */
host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host) +
NAND_MAX_OOBSIZE, GFP_KERNEL); NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE, GFP_KERNEL);
if (!host) if (!host)
return -ENOMEM; return -ENOMEM;
@ -1370,36 +1398,38 @@ static int __init mxcnd_probe(struct platform_device *pdev)
this->read_word = mxc_nand_read_word; this->read_word = mxc_nand_read_word;
this->write_buf = mxc_nand_write_buf; this->write_buf = mxc_nand_write_buf;
this->read_buf = mxc_nand_read_buf; this->read_buf = mxc_nand_read_buf;
this->verify_buf = mxc_nand_verify_buf;
host->clk = clk_get(&pdev->dev, "nfc"); host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) { if (IS_ERR(host->clk))
err = PTR_ERR(host->clk); return PTR_ERR(host->clk);
goto eclk;
}
clk_prepare_enable(host->clk);
host->clk_act = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
err = -ENODEV;
goto eres;
}
host->base = ioremap(res->start, resource_size(res));
if (!host->base) {
err = -ENOMEM;
goto eres;
}
host->main_area0 = host->base;
err = mxcnd_probe_dt(host); err = mxcnd_probe_dt(host);
if (err > 0) if (err > 0)
err = mxcnd_probe_pdata(host); err = mxcnd_probe_pdata(host);
if (err < 0) if (err < 0)
goto eirq; return err;
if (host->devtype_data->needs_ip) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
host->regs_ip = devm_request_and_ioremap(&pdev->dev, res);
if (!host->regs_ip)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
} else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
}
if (!res)
return -ENODEV;
host->base = devm_request_and_ioremap(&pdev->dev, res);
if (!host->base)
return -ENOMEM;
host->main_area0 = host->base;
if (host->devtype_data->regs_offset) if (host->devtype_data->regs_offset)
host->regs = host->base + host->devtype_data->regs_offset; host->regs = host->base + host->devtype_data->regs_offset;
@ -1414,19 +1444,6 @@ static int __init mxcnd_probe(struct platform_device *pdev)
this->ecc.size = 512; this->ecc.size = 512;
this->ecc.layout = host->devtype_data->ecclayout_512; this->ecc.layout = host->devtype_data->ecclayout_512;
if (host->devtype_data->needs_ip) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
err = -ENODEV;
goto eirq;
}
host->regs_ip = ioremap(res->start, resource_size(res));
if (!host->regs_ip) {
err = -ENOMEM;
goto eirq;
}
}
if (host->pdata.hw_ecc) { if (host->pdata.hw_ecc) {
this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.calculate = mxc_nand_calculate_ecc;
this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.hwctl = mxc_nand_enable_hwecc;
@ -1458,9 +1475,13 @@ static int __init mxcnd_probe(struct platform_device *pdev)
*/ */
host->devtype_data->irq_control(host, 0); host->devtype_data->irq_control(host, 0);
err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq,
IRQF_DISABLED, DRIVER_NAME, host);
if (err) if (err)
goto eirq; return err;
clk_prepare_enable(host->clk);
host->clk_act = 1;
/* /*
* Now that we "own" the interrupt make sure the interrupt mask bit is * Now that we "own" the interrupt make sure the interrupt mask bit is
@ -1512,15 +1533,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
return 0; return 0;
escan: escan:
free_irq(host->irq, host); clk_disable_unprepare(host->clk);
eirq:
if (host->regs_ip)
iounmap(host->regs_ip);
iounmap(host->base);
eres:
clk_put(host->clk);
eclk:
kfree(host);
return err; return err;
} }
@ -1529,16 +1542,9 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
{ {
struct mxc_nand_host *host = platform_get_drvdata(pdev); struct mxc_nand_host *host = platform_get_drvdata(pdev);
clk_put(host->clk);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
nand_release(&host->mtd); nand_release(&host->mtd);
free_irq(host->irq, host);
if (host->regs_ip)
iounmap(host->regs_ip);
iounmap(host->base);
kfree(host);
return 0; return 0;
} }
@ -1549,22 +1555,10 @@ static struct platform_driver mxcnd_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(mxcnd_dt_ids), .of_match_table = of_match_ptr(mxcnd_dt_ids),
}, },
.probe = mxcnd_probe,
.remove = __devexit_p(mxcnd_remove), .remove = __devexit_p(mxcnd_remove),
}; };
module_platform_driver(mxcnd_driver);
static int __init mxc_nd_init(void)
{
return platform_driver_probe(&mxcnd_driver, mxcnd_probe);
}
static void __exit mxc_nd_cleanup(void)
{
/* Unregister the device structure */
platform_driver_unregister(&mxcnd_driver);
}
module_init(mxc_nd_init);
module_exit(mxc_nd_cleanup);
MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("MXC NAND MTD driver"); MODULE_DESCRIPTION("MXC NAND MTD driver");

Просмотреть файл

@ -242,25 +242,6 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
buf[i] = readb(chip->IO_ADDR_R); buf[i] = readb(chip->IO_ADDR_R);
} }
/**
* nand_verify_buf - [DEFAULT] Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
* Default verify function for 8bit buswidth.
*/
static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;
for (i = 0; i < len; i++)
if (buf[i] != readb(chip->IO_ADDR_R))
return -EFAULT;
return 0;
}
/** /**
* nand_write_buf16 - [DEFAULT] write buffer to chip * nand_write_buf16 - [DEFAULT] write buffer to chip
* @mtd: MTD device structure * @mtd: MTD device structure
@ -300,28 +281,6 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
p[i] = readw(chip->IO_ADDR_R); p[i] = readw(chip->IO_ADDR_R);
} }
/**
* nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
* Default verify function for 16bit buswidth.
*/
static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i = 0; i < len; i++)
if (p[i] != readw(chip->IO_ADDR_R))
return -EFAULT;
return 0;
}
/** /**
* nand_block_bad - [DEFAULT] Read bad block marker from the chip * nand_block_bad - [DEFAULT] Read bad block marker from the chip
* @mtd: MTD device structure * @mtd: MTD device structure
@ -1525,7 +1484,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
oob_required, oob_required,
page); page);
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
!oob)
ret = chip->ecc.read_subpage(mtd, chip, ret = chip->ecc.read_subpage(mtd, chip,
col, bytes, bufpoi); col, bytes, bufpoi);
else else
@ -1542,7 +1502,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Transfer not aligned data */ /* Transfer not aligned data */
if (!aligned) { if (!aligned) {
if (!NAND_SUBPAGE_READ(chip) && !oob && if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
!(mtd->ecc_stats.failed - stats.failed) && !(mtd->ecc_stats.failed - stats.failed) &&
(ops->mode != MTD_OPS_RAW)) { (ops->mode != MTD_OPS_RAW)) {
chip->pagebuf = realpage; chip->pagebuf = realpage;
@ -1565,14 +1525,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
oobreadlen -= toread; oobreadlen -= toread;
} }
} }
if (!(chip->options & NAND_NO_READRDY)) {
/* Apply delay or wait for ready/busy pin */
if (!chip->dev_ready)
udelay(chip->chip_delay);
else
nand_wait_ready(mtd);
}
} else { } else {
memcpy(buf, chip->buffers->databuf + col, bytes); memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes; buf += bytes;
@ -1633,7 +1585,7 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
ops.len = len; ops.len = len;
ops.datbuf = buf; ops.datbuf = buf;
ops.oobbuf = NULL; ops.oobbuf = NULL;
ops.mode = 0; ops.mode = MTD_OPS_PLACE_OOB;
ret = nand_do_read_ops(mtd, from, &ops); ret = nand_do_read_ops(mtd, from, &ops);
*retlen = ops.retlen; *retlen = ops.retlen;
nand_release_device(mtd); nand_release_device(mtd);
@ -1837,14 +1789,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
len = min(len, readlen); len = min(len, readlen);
buf = nand_transfer_oob(chip, buf, ops, len); buf = nand_transfer_oob(chip, buf, ops, len);
if (!(chip->options & NAND_NO_READRDY)) {
/* Apply delay or wait for ready/busy pin */
if (!chip->dev_ready)
udelay(chip->chip_delay);
else
nand_wait_ready(mtd);
}
readlen -= len; readlen -= len;
if (!readlen) if (!readlen)
break; break;
@ -1927,12 +1871,14 @@ out:
* *
* Not for syndrome calculating ECC controllers, which use a special oob layout. * Not for syndrome calculating ECC controllers, which use a special oob layout.
*/ */
static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, buf, mtd->writesize);
if (oob_required) if (oob_required)
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
} }
/** /**
@ -1944,7 +1890,7 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
* *
* We need a special oob layout and handling even when ECC isn't checked. * We need a special oob layout and handling even when ECC isn't checked.
*/ */
static void nand_write_page_raw_syndrome(struct mtd_info *mtd, static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
@ -1974,6 +1920,8 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
size = mtd->oobsize - (oob - chip->oob_poi); size = mtd->oobsize - (oob - chip->oob_poi);
if (size) if (size)
chip->write_buf(mtd, oob, size); chip->write_buf(mtd, oob, size);
return 0;
} }
/** /**
* nand_write_page_swecc - [REPLACEABLE] software ECC based page write function * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
@ -1982,7 +1930,7 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
* @buf: data buffer * @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB * @oob_required: must write chip->oob_poi to OOB
*/ */
static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
int i, eccsize = chip->ecc.size; int i, eccsize = chip->ecc.size;
@ -1999,7 +1947,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; i < chip->ecc.total; i++) for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i]; chip->oob_poi[eccpos[i]] = ecc_calc[i];
chip->ecc.write_page_raw(mtd, chip, buf, 1); return chip->ecc.write_page_raw(mtd, chip, buf, 1);
} }
/** /**
@ -2009,7 +1957,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @buf: data buffer * @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB * @oob_required: must write chip->oob_poi to OOB
*/ */
static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
int i, eccsize = chip->ecc.size; int i, eccsize = chip->ecc.size;
@ -2029,6 +1977,8 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
chip->oob_poi[eccpos[i]] = ecc_calc[i]; chip->oob_poi[eccpos[i]] = ecc_calc[i];
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
} }
/** /**
@ -2041,7 +1991,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
* The hw generator calculates the error syndrome automatically. Therefore we * The hw generator calculates the error syndrome automatically. Therefore we
* need a special oob layout and handling. * need a special oob layout and handling.
*/ */
static void nand_write_page_syndrome(struct mtd_info *mtd, static int nand_write_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
@ -2075,6 +2025,8 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
i = mtd->oobsize - (oob - chip->oob_poi); i = mtd->oobsize - (oob - chip->oob_poi);
if (i) if (i)
chip->write_buf(mtd, oob, i); chip->write_buf(mtd, oob, i);
return 0;
} }
/** /**
@ -2096,9 +2048,12 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw)) 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 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 it's worth the * Cached progamming disabled for now. Not sure if it's worth the
@ -2125,16 +2080,6 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
status = chip->waitfunc(mtd, 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;
/* Make sure the next page prog is preceded by a status read */
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
#endif
return 0; return 0;
} }
@ -2336,7 +2281,7 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
ops.len = len; ops.len = len;
ops.datbuf = (uint8_t *)buf; ops.datbuf = (uint8_t *)buf;
ops.oobbuf = NULL; ops.oobbuf = NULL;
ops.mode = 0; ops.mode = MTD_OPS_PLACE_OOB;
ret = nand_do_write_ops(mtd, to, &ops); ret = nand_do_write_ops(mtd, to, &ops);
@ -2365,7 +2310,7 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
ops.len = len; ops.len = len;
ops.datbuf = (uint8_t *)buf; ops.datbuf = (uint8_t *)buf;
ops.oobbuf = NULL; ops.oobbuf = NULL;
ops.mode = 0; ops.mode = MTD_OPS_PLACE_OOB;
ret = nand_do_write_ops(mtd, to, &ops); ret = nand_do_write_ops(mtd, to, &ops);
*retlen = ops.retlen; *retlen = ops.retlen;
nand_release_device(mtd); nand_release_device(mtd);
@ -2754,6 +2699,50 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
return chip->block_markbad(mtd, ofs); return chip->block_markbad(mtd, ofs);
} }
/**
* nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
* @mtd: MTD device structure
* @chip: nand chip info structure
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
int status;
if (!chip->onfi_version)
return -EINVAL;
chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
return 0;
}
/**
* nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
* @mtd: MTD device structure
* @chip: nand chip info structure
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
if (!chip->onfi_version)
return -EINVAL;
/* clear the sub feature parameters */
memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
return 0;
}
/** /**
* nand_suspend - [MTD Interface] Suspend the NAND flash * nand_suspend - [MTD Interface] Suspend the NAND flash
* @mtd: MTD device structure * @mtd: MTD device structure
@ -2809,8 +2798,6 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!chip->read_buf) if (!chip->read_buf)
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!chip->verify_buf)
chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!chip->scan_bbt) if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt; chip->scan_bbt = nand_default_bbt;
@ -2914,13 +2901,249 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
if (le16_to_cpu(p->features) & 1) if (le16_to_cpu(p->features) & 1)
*busw = NAND_BUSWIDTH_16; *busw = NAND_BUSWIDTH_16;
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK;
pr_info("ONFI flash detected\n"); pr_info("ONFI flash detected\n");
return 1; return 1;
} }
/*
* nand_id_has_period - Check if an ID string has a given wraparound period
* @id_data: the ID string
* @arrlen: the length of the @id_data array
* @period: the period of repitition
*
* Check if an ID string is repeated within a given sequence of bytes at
* specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
* period of 2). This is a helper function for nand_id_len(). Returns non-zero
* if the repetition has a period of @period; otherwise, returns zero.
*/
static int nand_id_has_period(u8 *id_data, int arrlen, int period)
{
int i, j;
for (i = 0; i < period; i++)
for (j = i + period; j < arrlen; j += period)
if (id_data[i] != id_data[j])
return 0;
return 1;
}
/*
* nand_id_len - Get the length of an ID string returned by CMD_READID
* @id_data: the ID string
* @arrlen: the length of the @id_data array
* Returns the length of the ID string, according to known wraparound/trailing
* zero patterns. If no pattern exists, returns the length of the array.
*/
static int nand_id_len(u8 *id_data, int arrlen)
{
int last_nonzero, period;
/* Find last non-zero byte */
for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
if (id_data[last_nonzero])
break;
/* All zeros */
if (last_nonzero < 0)
return 0;
/* Calculate wraparound period */
for (period = 1; period < arrlen; period++)
if (nand_id_has_period(id_data, arrlen, period))
break;
/* There's a repeated pattern */
if (period < arrlen)
return period;
/* There are trailing zeros */
if (last_nonzero < arrlen - 1)
return last_nonzero + 1;
/* No pattern detected */
return arrlen;
}
/*
* Many new NAND share similar device ID codes, which represent the size of the
* chip. The rest of the parameters must be decoded according to generic or
* manufacturer-specific "extended ID" decoding patterns.
*/
static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
u8 id_data[8], int *busw)
{
int extid, id_len;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = id_data[2];
/* The 4th id byte is the important one */
extid = id_data[3];
id_len = nand_id_len(id_data, 8);
/*
* Field definitions are in the following datasheets:
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
* New style (6 byte ID): Samsung K9GAG08U0F (p.44)
* Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22)
*
* Check for ID length, cell type, and Hynix/Samsung ID to decide what
* to do.
*/
if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG) {
/* Calc pagesize */
mtd->writesize = 2048 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
case 1:
mtd->oobsize = 128;
break;
case 2:
mtd->oobsize = 218;
break;
case 3:
mtd->oobsize = 400;
break;
case 4:
mtd->oobsize = 436;
break;
case 5:
mtd->oobsize = 512;
break;
case 6:
default: /* Other cases are "reserved" (unknown) */
mtd->oobsize = 640;
break;
}
extid >>= 2;
/* Calc blocksize */
mtd->erasesize = (128 * 1024) <<
(((extid >> 1) & 0x04) | (extid & 0x03));
*busw = 0;
} else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
unsigned int tmp;
/* Calc pagesize */
mtd->writesize = 2048 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
case 0:
mtd->oobsize = 128;
break;
case 1:
mtd->oobsize = 224;
break;
case 2:
mtd->oobsize = 448;
break;
case 3:
mtd->oobsize = 64;
break;
case 4:
mtd->oobsize = 32;
break;
case 5:
mtd->oobsize = 16;
break;
default:
mtd->oobsize = 640;
break;
}
extid >>= 2;
/* Calc blocksize */
tmp = ((extid >> 1) & 0x04) | (extid & 0x03);
if (tmp < 0x03)
mtd->erasesize = (128 * 1024) << tmp;
else if (tmp == 0x03)
mtd->erasesize = 768 * 1024;
else
mtd->erasesize = (64 * 1024) << tmp;
*busw = 0;
} else {
/* Calc pagesize */
mtd->writesize = 1024 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) *
(mtd->writesize >> 9);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
/* Get buswidth information */
*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
}
}
/*
* Old devices have chip data hardcoded in the device ID table. nand_decode_id
* decodes a matching ID table entry and assigns the MTD size parameters for
* the chip.
*/
static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
struct nand_flash_dev *type, u8 id_data[8],
int *busw)
{
int maf_id = id_data[0];
mtd->erasesize = type->erasesize;
mtd->writesize = type->pagesize;
mtd->oobsize = mtd->writesize / 32;
*busw = type->options & NAND_BUSWIDTH_16;
/*
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
* some Spansion chips have erasesize that conflicts with size
* listed in nand_ids table.
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
*/
if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00
&& id_data[6] == 0x00 && id_data[7] == 0x00
&& mtd->writesize == 512) {
mtd->erasesize = 128 * 1024;
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
}
}
/*
* Set the bad block marker/indicator (BBM/BBI) patterns according to some
* heuristic patterns using various detected parameters (e.g., manufacturer,
* page size, cell-type information).
*/
static void nand_decode_bbm_options(struct mtd_info *mtd,
struct nand_chip *chip, u8 id_data[8])
{
int maf_id = id_data[0];
/* Set the bad block position */
if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
else
chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
/*
* Bad block marker is stored in the last page of each block on Samsung
* and Hynix MLC devices; stored in first two pages of each block on
* Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,
* AMD/Spansion, and Macronix. All others scan only the first page.
*/
if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(maf_id == NAND_MFR_SAMSUNG ||
maf_id == NAND_MFR_HYNIX))
chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(maf_id == NAND_MFR_SAMSUNG ||
maf_id == NAND_MFR_HYNIX ||
maf_id == NAND_MFR_TOSHIBA ||
maf_id == NAND_MFR_AMD ||
maf_id == NAND_MFR_MACRONIX)) ||
(mtd->writesize == 2048 &&
maf_id == NAND_MFR_MICRON))
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
}
/* /*
* Get the flash and manufacturer id and lookup if the type is supported. * Get the flash and manufacturer id and lookup if the type is supported.
*/ */
@ -2932,7 +3155,6 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
{ {
int i, maf_idx; int i, maf_idx;
u8 id_data[8]; u8 id_data[8];
int ret;
/* Select the device */ /* Select the device */
chip->select_chip(mtd, 0); chip->select_chip(mtd, 0);
@ -2959,7 +3181,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
for (i = 0; i < 2; i++) /* Read entire ID string */
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd); id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) { if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
@ -2979,18 +3202,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->onfi_version = 0; chip->onfi_version = 0;
if (!type->name || !type->pagesize) { if (!type->name || !type->pagesize) {
/* Check is chip is ONFI compliant */ /* Check is chip is ONFI compliant */
ret = nand_flash_detect_onfi(mtd, chip, &busw); if (nand_flash_detect_onfi(mtd, chip, &busw))
if (ret)
goto ident_done; goto ident_done;
} }
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd);
if (!type->name) if (!type->name)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
@ -3003,86 +3218,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/* Set the pagesize, oobsize, erasesize by the driver */ /* Set the pagesize, oobsize, erasesize by the driver */
busw = chip->init_size(mtd, chip, id_data); busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) { } else if (!type->pagesize) {
int extid; /* Decode parameters from extended ID */
/* The 3rd id byte holds MLC / multichip data */ nand_decode_ext_id(mtd, chip, id_data, &busw);
chip->cellinfo = id_data[2];
/* The 4th id byte is the important one */
extid = id_data[3];
/*
* Field definitions are in the following datasheets:
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
* New style (6 byte ID): Samsung K9GBG08U0M (p.40)
*
* Check for wraparound + Samsung ID + nonzero 6th byte
* to decide what to do.
*/
if (id_data[0] == id_data[6] && id_data[1] == id_data[7] &&
id_data[0] == NAND_MFR_SAMSUNG &&
(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
id_data[5] != 0x00) {
/* Calc pagesize */
mtd->writesize = 2048 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
switch (extid & 0x03) {
case 1:
mtd->oobsize = 128;
break;
case 2:
mtd->oobsize = 218;
break;
case 3:
mtd->oobsize = 400;
break;
default:
mtd->oobsize = 436;
break;
}
extid >>= 2;
/* Calc blocksize */
mtd->erasesize = (128 * 1024) <<
(((extid >> 1) & 0x04) | (extid & 0x03));
busw = 0;
} else {
/* Calc pagesize */
mtd->writesize = 1024 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) *
(mtd->writesize >> 9);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
}
} else { } else {
/* nand_decode_id(mtd, chip, type, id_data, &busw);
* Old devices have chip data hardcoded in the device id table.
*/
mtd->erasesize = type->erasesize;
mtd->writesize = type->pagesize;
mtd->oobsize = mtd->writesize / 32;
busw = type->options & NAND_BUSWIDTH_16;
/*
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
* some Spansion chips have erasesize that conflicts with size
* listed in nand_ids table.
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
*/
if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
id_data[5] == 0x00 && id_data[6] == 0x00 &&
id_data[7] == 0x00 && mtd->writesize == 512) {
mtd->erasesize = 128 * 1024;
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
}
} }
/* Get chip options, preserve non chip based options */ /* Get chip options */
chip->options &= ~NAND_CHIPOPTIONS_MSK; chip->options |= type->options;
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
/* /*
* Check if chip is not a Samsung device. Do not clear the * Check if chip is not a Samsung device. Do not clear the
@ -3112,6 +3254,8 @@ ident_done:
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
nand_decode_bbm_options(mtd, chip, id_data);
/* Calculate the address shift from the page size */ /* Calculate the address shift from the page size */
chip->page_shift = ffs(mtd->writesize) - 1; chip->page_shift = ffs(mtd->writesize) - 1;
/* Convert chipsize to number of pages per chip -1 */ /* Convert chipsize to number of pages per chip -1 */
@ -3128,33 +3272,6 @@ ident_done:
chip->badblockbits = 8; chip->badblockbits = 8;
/* Set the bad block position */
if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16))
chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
else
chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
/*
* Bad block marker is stored in the last page of each block
* on Samsung and Hynix MLC devices; stored in first two pages
* of each block on Micron devices with 2KiB pages and on
* SLC Samsung, Hynix, Toshiba, AMD/Spansion, and Macronix.
* All others scan only the first page.
*/
if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(*maf_id == NAND_MFR_SAMSUNG ||
*maf_id == NAND_MFR_HYNIX))
chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(*maf_id == NAND_MFR_SAMSUNG ||
*maf_id == NAND_MFR_HYNIX ||
*maf_id == NAND_MFR_TOSHIBA ||
*maf_id == NAND_MFR_AMD ||
*maf_id == NAND_MFR_MACRONIX)) ||
(mtd->writesize == 2048 &&
*maf_id == NAND_MFR_MICRON))
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
/* Check for AND chips with 4 page planes */ /* Check for AND chips with 4 page planes */
if (chip->options & NAND_4PAGE_ARRAY) if (chip->options & NAND_4PAGE_ARRAY)
chip->erase_cmd = multi_erase_cmd; chip->erase_cmd = multi_erase_cmd;
@ -3284,6 +3401,12 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!chip->write_page) if (!chip->write_page)
chip->write_page = nand_write_page; chip->write_page = nand_write_page;
/* set for ONFI nand */
if (!chip->onfi_set_features)
chip->onfi_set_features = nand_onfi_set_features;
if (!chip->onfi_get_features)
chip->onfi_get_features = nand_onfi_get_features;
/* /*
* Check ECC mode, default to software if 3byte/512byte hardware ECC is * Check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC * selected and we have 256 byte pagesize fallback to software ECC
@ -3477,6 +3600,10 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Invalidate the pagebuffer reference */ /* Invalidate the pagebuffer reference */
chip->pagebuf = -1; chip->pagebuf = -1;
/* Large page NAND with SOFT_ECC should support subpage reads */
if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
chip->options |= NAND_SUBPAGE_READ;
/* Fill in remaining MTD driver data */ /* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH; mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :

Просмотреть файл

@ -4,7 +4,7 @@
* Overview: * Overview:
* Bad block table support for the NAND driver * Bad block table support for the NAND driver
* *
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
@ -22,7 +22,7 @@
* BBT on flash. If a BBT is found then the contents are read and the memory * BBT on flash. If a BBT is found then the contents are read and the memory
* based BBT is created. If a mirrored BBT is selected then the mirror is * based BBT is created. If a mirrored BBT is selected then the mirror is
* searched too and the versions are compared. If the mirror has a greater * searched too and the versions are compared. If the mirror has a greater
* version number than the mirror BBT is used to build the memory based BBT. * version number, then the mirror BBT is used to build the memory based BBT.
* If the tables are not versioned, then we "or" the bad block information. * If the tables are not versioned, then we "or" the bad block information.
* If one of the BBTs is out of date or does not exist it is (re)created. * If one of the BBTs is out of date or does not exist it is (re)created.
* If no BBT exists at all then the device is scanned for factory marked * If no BBT exists at all then the device is scanned for factory marked
@ -62,21 +62,20 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/bbm.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h> #include <linux/mtd/nand_ecc.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/string.h>
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
{ {
int ret; if (memcmp(buf, td->pattern, td->len))
return -1;
ret = memcmp(buf, td->pattern, td->len); return 0;
if (!ret)
return ret;
return -1;
} }
/** /**
@ -92,19 +91,16 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
*/ */
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{ {
int i, end = 0; int end = 0;
uint8_t *p = buf; uint8_t *p = buf;
if (td->options & NAND_BBT_NO_OOB) if (td->options & NAND_BBT_NO_OOB)
return check_pattern_no_oob(buf, td); return check_pattern_no_oob(buf, td);
end = paglen + td->offs; end = paglen + td->offs;
if (td->options & NAND_BBT_SCANEMPTY) { if (td->options & NAND_BBT_SCANEMPTY)
for (i = 0; i < end; i++) { if (memchr_inv(p, 0xff, end))
if (p[i] != 0xff) return -1;
return -1;
}
}
p += end; p += end;
/* Compare the pattern */ /* Compare the pattern */
@ -114,10 +110,8 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
if (td->options & NAND_BBT_SCANEMPTY) { if (td->options & NAND_BBT_SCANEMPTY) {
p += td->len; p += td->len;
end += td->len; end += td->len;
for (i = end; i < len; i++) { if (memchr_inv(p, 0xff, len - end))
if (*p++ != 0xff) return -1;
return -1;
}
} }
return 0; return 0;
} }
@ -133,14 +127,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
*/ */
static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
{ {
int i;
uint8_t *p = buf;
/* Compare the pattern */ /* Compare the pattern */
for (i = 0; i < td->len; i++) { if (memcmp(buf + td->offs, td->pattern, td->len))
if (p[td->offs + i] != td->pattern[i]) return -1;
return -1;
}
return 0; return 0;
} }
@ -288,7 +277,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
} }
/* BBT marker is in the first page, no OOB */ /* BBT marker is in the first page, no OOB */
static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
struct nand_bbt_descr *td) struct nand_bbt_descr *td)
{ {
size_t retlen; size_t retlen;
@ -301,14 +290,24 @@ static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
return mtd_read(mtd, offs, len, &retlen, buf); return mtd_read(mtd, offs, len, &retlen, buf);
} }
/* Scan read raw data from flash */ /**
static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
* @mtd: MTD device structure
* @buf: temporary buffer
* @offs: offset at which to scan
* @len: length of data region to read
*
* Scan read data from data+OOB. May traverse multiple pages, interleaving
* page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
* ECC condition (error or bitflip). May quit on the first (non-ECC) error.
*/
static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len) size_t len)
{ {
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
int res; int res, ret = 0;
ops.mode = MTD_OPS_RAW; ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = 0; ops.ooboffs = 0;
ops.ooblen = mtd->oobsize; ops.ooblen = mtd->oobsize;
@ -318,24 +317,27 @@ static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
ops.oobbuf = buf + ops.len; ops.oobbuf = buf + ops.len;
res = mtd_read_oob(mtd, offs, &ops); res = mtd_read_oob(mtd, offs, &ops);
if (res) {
if (res) if (!mtd_is_bitflip_or_eccerr(res))
return res; return res;
else if (mtd_is_eccerr(res) || !ret)
ret = res;
}
buf += mtd->oobsize + mtd->writesize; buf += mtd->oobsize + mtd->writesize;
len -= mtd->writesize; len -= mtd->writesize;
offs += mtd->writesize; offs += mtd->writesize;
} }
return 0; return ret;
} }
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len, struct nand_bbt_descr *td) size_t len, struct nand_bbt_descr *td)
{ {
if (td->options & NAND_BBT_NO_OOB) if (td->options & NAND_BBT_NO_OOB)
return scan_read_raw_data(mtd, buf, offs, td); return scan_read_data(mtd, buf, offs, td);
else else
return scan_read_raw_oob(mtd, buf, offs, len); return scan_read_oob(mtd, buf, offs, len);
} }
/* Scan write data with oob to flash */ /* Scan write data with oob to flash */
@ -373,14 +375,14 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
* Read the bad block table(s) for all chips starting at a given page. We * Read the bad block table(s) for all chips starting at a given page. We
* assume that the bbt bits are in consecutive order. * assume that the bbt bits are in consecutive order.
*/ */
static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md) struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
/* Read the primary version, if available */ /* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) { if (td->options & NAND_BBT_VERSION) {
scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
mtd->writesize, td); mtd->writesize, td);
td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
pr_info("Bad block table at page %d, version 0x%02X\n", pr_info("Bad block table at page %d, version 0x%02X\n",
@ -389,28 +391,27 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the mirror version, if available */ /* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) { if (md && (md->options & NAND_BBT_VERSION)) {
scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift, scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
mtd->writesize, td); mtd->writesize, md);
md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
pr_info("Bad block table at page %d, version 0x%02X\n", pr_info("Bad block table at page %d, version 0x%02X\n",
md->pages[0], md->version[0]); md->pages[0], md->version[0]);
} }
return 1;
} }
/* Scan a given block full */ /* Scan a given block full */
static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, size_t readlen, loff_t offs, uint8_t *buf, size_t readlen,
int scanlen, int len) int scanlen, int numpages)
{ {
int ret, j; int ret, j;
ret = scan_read_raw_oob(mtd, buf, offs, readlen); ret = scan_read_oob(mtd, buf, offs, readlen);
/* Ignore ECC errors when checking for BBM */ /* Ignore ECC errors when checking for BBM */
if (ret && !mtd_is_bitflip_or_eccerr(ret)) if (ret && !mtd_is_bitflip_or_eccerr(ret))
return ret; return ret;
for (j = 0; j < len; j++, buf += scanlen) { for (j = 0; j < numpages; j++, buf += scanlen) {
if (check_pattern(buf, scanlen, mtd->writesize, bd)) if (check_pattern(buf, scanlen, mtd->writesize, bd))
return 1; return 1;
} }
@ -419,7 +420,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
/* Scan a given block partially */ /* Scan a given block partially */
static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, int len) loff_t offs, uint8_t *buf, int numpages)
{ {
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
int j, ret; int j, ret;
@ -430,7 +431,7 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
ops.datbuf = NULL; ops.datbuf = NULL;
ops.mode = MTD_OPS_PLACE_OOB; ops.mode = MTD_OPS_PLACE_OOB;
for (j = 0; j < len; j++) { for (j = 0; j < numpages; j++) {
/* /*
* Read the full oob until read_oob is fixed to handle single * Read the full oob until read_oob is fixed to handle single
* byte reads for 16 bit buswidth. * byte reads for 16 bit buswidth.
@ -463,7 +464,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *bd, int chip) struct nand_bbt_descr *bd, int chip)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
int i, numblocks, len, scanlen; int i, numblocks, numpages, scanlen;
int startblock; int startblock;
loff_t from; loff_t from;
size_t readlen; size_t readlen;
@ -471,11 +472,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
pr_info("Scanning device for bad blocks\n"); pr_info("Scanning device for bad blocks\n");
if (bd->options & NAND_BBT_SCANALLPAGES) if (bd->options & NAND_BBT_SCANALLPAGES)
len = 1 << (this->bbt_erase_shift - this->page_shift); numpages = 1 << (this->bbt_erase_shift - this->page_shift);
else if (bd->options & NAND_BBT_SCAN2NDPAGE) else if (bd->options & NAND_BBT_SCAN2NDPAGE)
len = 2; numpages = 2;
else else
len = 1; numpages = 1;
if (!(bd->options & NAND_BBT_SCANEMPTY)) { if (!(bd->options & NAND_BBT_SCANEMPTY)) {
/* We need only read few bytes from the OOB area */ /* We need only read few bytes from the OOB area */
@ -484,7 +485,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
} else { } else {
/* Full page content should be read */ /* Full page content should be read */
scanlen = mtd->writesize + mtd->oobsize; scanlen = mtd->writesize + mtd->oobsize;
readlen = len * mtd->writesize; readlen = numpages * mtd->writesize;
} }
if (chip == -1) { if (chip == -1) {
@ -508,7 +509,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
} }
if (this->bbt_options & NAND_BBT_SCANLASTPAGE) if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
from += mtd->erasesize - (mtd->writesize * len); from += mtd->erasesize - (mtd->writesize * numpages);
for (i = startblock; i < numblocks;) { for (i = startblock; i < numblocks;) {
int ret; int ret;
@ -517,9 +518,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
if (bd->options & NAND_BBT_SCANALLPAGES) if (bd->options & NAND_BBT_SCANALLPAGES)
ret = scan_block_full(mtd, bd, from, buf, readlen, ret = scan_block_full(mtd, bd, from, buf, readlen,
scanlen, len); scanlen, numpages);
else else
ret = scan_block_fast(mtd, bd, from, buf, len); ret = scan_block_fast(mtd, bd, from, buf, numpages);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -594,7 +595,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
loff_t offs = (loff_t)actblock << this->bbt_erase_shift; loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
/* Read first page */ /* Read first page */
scan_read_raw(mtd, buf, offs, mtd->writesize, td); scan_read(mtd, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) { if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << blocktopage; td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) { if (td->options & NAND_BBT_VERSION) {
@ -626,7 +627,9 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
* *
* Search and read the bad block table(s). * Search and read the bad block table(s).
*/ */
static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td,
struct nand_bbt_descr *md)
{ {
/* Search the primary table */ /* Search the primary table */
search_bbt(mtd, buf, td); search_bbt(mtd, buf, td);
@ -634,9 +637,6 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt
/* Search the mirror table */ /* Search the mirror table */
if (md) if (md)
search_bbt(mtd, buf, md); search_bbt(mtd, buf, md);
/* Force result check */
return 1;
} }
/** /**
@ -1162,14 +1162,13 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
/* Is the bbt at a given page? */ /* Is the bbt at a given page? */
if (td->options & NAND_BBT_ABSPAGE) { if (td->options & NAND_BBT_ABSPAGE) {
res = read_abs_bbts(mtd, buf, td, md); read_abs_bbts(mtd, buf, td, md);
} else { } else {
/* Search the bad block table using a pattern in oob */ /* Search the bad block table using a pattern in oob */
res = search_read_bbts(mtd, buf, td, md); search_read_bbts(mtd, buf, td, md);
} }
if (res) res = check_create(mtd, buf, bd);
res = check_create(mtd, buf, bd);
/* Prevent the bbt regions from erasing / writing */ /* Prevent the bbt regions from erasing / writing */
mark_bbt_region(mtd, td); mark_bbt_region(mtd, td);
@ -1260,7 +1259,7 @@ static struct nand_bbt_descr bbt_main_descr = {
.offs = 8, .offs = 8,
.len = 4, .len = 4,
.veroffs = 12, .veroffs = 12,
.maxblocks = 4, .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = bbt_pattern .pattern = bbt_pattern
}; };
@ -1270,27 +1269,27 @@ static struct nand_bbt_descr bbt_mirror_descr = {
.offs = 8, .offs = 8,
.len = 4, .len = 4,
.veroffs = 12, .veroffs = 12,
.maxblocks = 4, .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = mirror_pattern .pattern = mirror_pattern
}; };
static struct nand_bbt_descr bbt_main_no_bbt_descr = { static struct nand_bbt_descr bbt_main_no_oob_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB, | NAND_BBT_NO_OOB,
.len = 4, .len = 4,
.veroffs = 4, .veroffs = 4,
.maxblocks = 4, .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = bbt_pattern .pattern = bbt_pattern
}; };
static struct nand_bbt_descr bbt_mirror_no_bbt_descr = { static struct nand_bbt_descr bbt_mirror_no_oob_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB, | NAND_BBT_NO_OOB,
.len = 4, .len = 4,
.veroffs = 4, .veroffs = 4,
.maxblocks = 4, .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = mirror_pattern .pattern = mirror_pattern
}; };
@ -1355,8 +1354,8 @@ int nand_default_bbt(struct mtd_info *mtd)
/* Use the default pattern descriptors */ /* Use the default pattern descriptors */
if (!this->bbt_td) { if (!this->bbt_td) {
if (this->bbt_options & NAND_BBT_NO_OOB) { if (this->bbt_options & NAND_BBT_NO_OOB) {
this->bbt_td = &bbt_main_no_bbt_descr; this->bbt_td = &bbt_main_no_oob_descr;
this->bbt_md = &bbt_mirror_no_bbt_descr; this->bbt_md = &bbt_mirror_no_oob_descr;
} else { } else {
this->bbt_td = &bbt_main_descr; this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr; this->bbt_md = &bbt_mirror_descr;
@ -1406,3 +1405,4 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
EXPORT_SYMBOL(nand_scan_bbt); EXPORT_SYMBOL(nand_scan_bbt);
EXPORT_SYMBOL(nand_default_bbt); EXPORT_SYMBOL(nand_default_bbt);
EXPORT_SYMBOL_GPL(nand_update_bbt);

Просмотреть файл

@ -1,149 +0,0 @@
/*****************************************************************************
* 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 <mach/reg_umi.h>
#include "nand_bcm_umi.h"
#ifdef BOOT0_BUILD
#include <uart.h>
#endif
/* ---- External Variable Declarations ----------------------------------- */
/* ---- External Function Prototypes ------------------------------------- */
/* ---- Public Variables ------------------------------------------------- */
/* ---- Private Constants and Types -------------------------------------- */
/* ---- Private Function Prototypes -------------------------------------- */
/* ---- Private Variables ------------------------------------------------ */
/* ---- Private Functions ------------------------------------------------ */
#if NAND_ECC_BCH
/****************************************************************************
* nand_bch_ecc_flip_bit - Routine to flip an errored bit
*
* PURPOSE:
* This is a helper routine that flips the bit (0 -> 1 or 1 -> 0) of the
* errored bit specified
*
* PARAMETERS:
* datap - Container that holds the 512 byte data
* errorLocation - Location of the bit that needs to be flipped
*
* RETURNS:
* None
****************************************************************************/
static void nand_bcm_umi_bch_ecc_flip_bit(uint8_t *datap, int errorLocation)
{
int locWithinAByte = (errorLocation & REG_UMI_BCH_ERR_LOC_BYTE) >> 0;
int locWithinAWord = (errorLocation & REG_UMI_BCH_ERR_LOC_WORD) >> 3;
int locWithinAPage = (errorLocation & REG_UMI_BCH_ERR_LOC_PAGE) >> 5;
uint8_t errorByte = 0;
uint8_t byteMask = 1 << locWithinAByte;
/* BCH uses big endian, need to change the location
* bits to little endian */
locWithinAWord = 3 - locWithinAWord;
errorByte = datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord];
#ifdef BOOT0_BUILD
puthexs("\nECC Correct Offset: ",
locWithinAPage * sizeof(uint32_t) + locWithinAWord);
puthexs(" errorByte:", errorByte);
puthex8(" Bit: ", locWithinAByte);
#endif
if (errorByte & byteMask) {
/* bit needs to be cleared */
errorByte &= ~byteMask;
} else {
/* bit needs to be set */
errorByte |= byteMask;
}
/* write back the value with the fixed bit */
datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord] = errorByte;
}
/****************************************************************************
* nand_correct_page_bch - Routine to correct bit errors when reading NAND
*
* PURPOSE:
* This routine reads the BCH registers to determine if there are any bit
* errors during the read of the last 512 bytes of data + ECC bytes. If
* errors exists, the routine fixes it.
*
* PARAMETERS:
* datap - Container that holds the 512 byte data
*
* RETURNS:
* 0 or greater = Number of errors corrected
* (No errors are found or errors have been fixed)
* -1 = Error(s) cannot be fixed
****************************************************************************/
int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
int numEccBytes)
{
int numErrors;
int errorLocation;
int idx;
uint32_t regValue;
/* wait for read ECC to be valid */
regValue = nand_bcm_umi_bch_poll_read_ecc_calc();
/*
* read the control status register to determine if there
* are error'ed bits
* see if errors are correctible
*/
if ((regValue & REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR) > 0) {
int i;
for (i = 0; i < numEccBytes; i++) {
if (readEccData[i] != 0xff) {
/* errors cannot be fixed, return -1 */
return -1;
}
}
/* If ECC is unprogrammed then we can't correct,
* assume everything OK */
return 0;
}
if ((regValue & REG_UMI_BCH_CTRL_STATUS_CORR_ERR) == 0) {
/* no errors */
return 0;
}
/*
* Fix errored bits by doing the following:
* 1. Read the number of errors in the control and status register
* 2. Read the error location registers that corresponds to the number
* of errors reported
* 3. Invert the bit in the data
*/
numErrors = (regValue & REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR) >> 20;
for (idx = 0; idx < numErrors; idx++) {
errorLocation =
REG_UMI_BCH_ERR_LOC_ADDR(idx) & REG_UMI_BCH_ERR_LOC_MASK;
/* Flip bit */
nand_bcm_umi_bch_ecc_flip_bit(datap, errorLocation);
}
/* Errors corrected */
return numErrors;
}
#endif

Просмотреть файл

@ -1,336 +0,0 @@
/*****************************************************************************
* Copyright 2003 - 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.
*****************************************************************************/
#ifndef NAND_BCM_UMI_H
#define NAND_BCM_UMI_H
/* ---- Include Files ---------------------------------------------------- */
#include <mach/reg_umi.h>
#include <mach/reg_nand.h>
#include <mach/cfg_global.h>
/* ---- Constants and Types ---------------------------------------------- */
#if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING)
#define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0)
#else
#define NAND_ECC_BCH 0
#endif
#define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13
#if NAND_ECC_BCH
#ifdef BOOT0_BUILD
#define NAND_ECC_NUM_BYTES 13
#else
#define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES
#endif
#else
#define NAND_ECC_NUM_BYTES 3
#endif
#define NAND_DATA_ACCESS_SIZE 512
/* ---- Variable Externs ------------------------------------------ */
/* ---- Function Prototypes --------------------------------------- */
int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
int numEccBytes);
/* Check in device is ready */
static inline int nand_bcm_umi_dev_ready(void)
{
return readl(&REG_UMI_NAND_RCSR) & REG_UMI_NAND_RCSR_RDY;
}
/* Wait until device is ready */
static inline void nand_bcm_umi_wait_till_ready(void)
{
while (nand_bcm_umi_dev_ready() == 0)
;
}
/* Enable Hamming ECC */
static inline void nand_bcm_umi_hamming_enable_hwecc(void)
{
/* disable and reset ECC, 512 byte page */
writel(readl(&REG_UMI_NAND_ECC_CSR) & ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE |
REG_UMI_NAND_ECC_CSR_256BYTE), &REG_UMI_NAND_ECC_CSR);
/* enable ECC */
writel(readl(&REG_UMI_NAND_ECC_CSR) | REG_UMI_NAND_ECC_CSR_ECC_ENABLE,
&REG_UMI_NAND_ECC_CSR);
}
#if NAND_ECC_BCH
/* BCH ECC specifics */
#define ECC_BITS_PER_CORRECTABLE_BIT 13
/* Enable BCH Read ECC */
static inline void nand_bcm_umi_bch_enable_read_hwecc(void)
{
/* disable and reset ECC */
writel(REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID, &REG_UMI_BCH_CTRL_STATUS);
/* Turn on ECC */
writel(REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN, &REG_UMI_BCH_CTRL_STATUS);
}
/* Enable BCH Write ECC */
static inline void nand_bcm_umi_bch_enable_write_hwecc(void)
{
/* disable and reset ECC */
writel(REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID, &REG_UMI_BCH_CTRL_STATUS);
/* Turn on ECC */
writel(REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN, &REG_UMI_BCH_CTRL_STATUS);
}
/* Config number of BCH ECC bytes */
static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes)
{
uint32_t nValue;
uint32_t tValue;
uint32_t kValue;
uint32_t numBits = numEccBytes * 8;
/* disable and reset ECC */
writel(REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID |
REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID,
&REG_UMI_BCH_CTRL_STATUS);
/* Every correctible bit requires 13 ECC bits */
tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT);
/* Total data in number of bits for generating and computing BCH ECC */
nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8;
/* K parameter is used internally. K = N - (T * 13) */
kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT);
/* Write the settings */
writel(nValue, &REG_UMI_BCH_N);
writel(tValue, &REG_UMI_BCH_T);
writel(kValue, &REG_UMI_BCH_K);
}
/* Pause during ECC read calculation to skip bytes in OOB */
static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void)
{
writel(REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN | REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC, &REG_UMI_BCH_CTRL_STATUS);
}
/* Resume during ECC read calculation after skipping bytes in OOB */
static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void)
{
writel(REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN, &REG_UMI_BCH_CTRL_STATUS);
}
/* Poll read ECC calc to check when hardware completes */
static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void)
{
uint32_t regVal;
do {
/* wait for ECC to be valid */
regVal = readl(&REG_UMI_BCH_CTRL_STATUS);
} while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0);
return regVal;
}
/* Poll write ECC calc to check when hardware completes */
static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void)
{
/* wait for ECC to be valid */
while ((readl(&REG_UMI_BCH_CTRL_STATUS) & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID)
== 0)
;
}
/* Read the OOB and ECC, for kernel write OOB to a buffer */
#if defined(__KERNEL__) && !defined(STANDALONE)
static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
uint8_t *eccCalc, int numEccBytes, uint8_t *oobp)
#else
static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
uint8_t *eccCalc, int numEccBytes)
#endif
{
int eccPos = 0;
int numToRead = 16; /* There are 16 bytes per sector in the OOB */
/* ECC is already paused when this function is called */
if (pageSize != NAND_DATA_ACCESS_SIZE) {
/* skip BI */
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp++ = readb(&REG_NAND_DATA8);
#else
readb(&REG_NAND_DATA8);
#endif
numToRead--;
}
while (numToRead > numEccBytes) {
/* skip free oob region */
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp++ = readb(&REG_NAND_DATA8);
#else
readb(&REG_NAND_DATA8);
#endif
numToRead--;
}
if (pageSize == NAND_DATA_ACCESS_SIZE) {
/* read ECC bytes before BI */
nand_bcm_umi_bch_resume_read_ecc_calc();
while (numToRead > 11) {
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp = readb(&REG_NAND_DATA8);
eccCalc[eccPos++] = *oobp;
oobp++;
#else
eccCalc[eccPos++] = readb(&REG_NAND_DATA8);
#endif
numToRead--;
}
nand_bcm_umi_bch_pause_read_ecc_calc();
if (numToRead == 11) {
/* read BI */
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp++ = readb(&REG_NAND_DATA8);
#else
readb(&REG_NAND_DATA8);
#endif
numToRead--;
}
}
/* read ECC bytes */
nand_bcm_umi_bch_resume_read_ecc_calc();
while (numToRead) {
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp = readb(&REG_NAND_DATA8);
eccCalc[eccPos++] = *oobp;
oobp++;
#else
eccCalc[eccPos++] = readb(&REG_NAND_DATA8);
#endif
numToRead--;
}
}
/* Helper function to write ECC */
static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos,
uint8_t *oobp, uint8_t eccVal)
{
if (eccBytePos <= numEccBytes)
*oobp = eccVal;
}
/* Write OOB with ECC */
static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize,
uint8_t *oobp, int numEccBytes)
{
uint32_t eccVal = 0xffffffff;
/* wait for write ECC to be valid */
nand_bcm_umi_bch_poll_write_ecc_calc();
/*
** Get the hardware ecc from the 32-bit result registers.
** Read after 512 byte accesses. Format B3B2B1B0
** where B3 = ecc3, etc.
*/
if (pageSize == NAND_DATA_ACCESS_SIZE) {
/* Now fill in the ECC bytes */
if (numEccBytes >= 13)
eccVal = readl(&REG_UMI_BCH_WR_ECC_3);
/* Usually we skip CM in oob[0,1] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0],
(eccVal >> 16) & 0xff);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1],
(eccVal >> 8) & 0xff);
/* Write ECC in oob[2,3,4] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2],
eccVal & 0xff); /* ECC 12 */
if (numEccBytes >= 9)
eccVal = readl(&REG_UMI_BCH_WR_ECC_2);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3],
(eccVal >> 24) & 0xff); /* ECC11 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4],
(eccVal >> 16) & 0xff); /* ECC10 */
/* Always Skip BI in oob[5] */
} else {
/* Always Skip BI in oob[0] */
/* Now fill in the ECC bytes */
if (numEccBytes >= 13)
eccVal = readl(&REG_UMI_BCH_WR_ECC_3);
/* Usually skip CM in oob[1,2] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1],
(eccVal >> 16) & 0xff);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2],
(eccVal >> 8) & 0xff);
/* Write ECC in oob[3-15] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3],
eccVal & 0xff); /* ECC12 */
if (numEccBytes >= 9)
eccVal = readl(&REG_UMI_BCH_WR_ECC_2);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4],
(eccVal >> 24) & 0xff); /* ECC11 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5],
(eccVal >> 16) & 0xff); /* ECC10 */
}
/* Fill in the remainder of ECC locations */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6],
(eccVal >> 8) & 0xff); /* ECC9 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7],
eccVal & 0xff); /* ECC8 */
if (numEccBytes >= 5)
eccVal = readl(&REG_UMI_BCH_WR_ECC_1);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8],
(eccVal >> 24) & 0xff); /* ECC7 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9],
(eccVal >> 16) & 0xff); /* ECC6 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10],
(eccVal >> 8) & 0xff); /* ECC5 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11],
eccVal & 0xff); /* ECC4 */
if (numEccBytes >= 1)
eccVal = readl(&REG_UMI_BCH_WR_ECC_0);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12],
(eccVal >> 24) & 0xff); /* ECC3 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13],
(eccVal >> 16) & 0xff); /* ECC2 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14],
(eccVal >> 8) & 0xff); /* ECC1 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15],
eccVal & 0xff); /* ECC0 */
}
#endif
#endif /* NAND_BCM_UMI_H */

Просмотреть файл

@ -70,7 +70,7 @@ struct nand_flash_dev nand_flash_ids[] = {
* These are the new chips with large page size. The pagesize and the * These are the new chips with large page size. The pagesize and the
* erasesize is determined from the extended id bytes * erasesize is determined from the extended id bytes
*/ */
#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY) #define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
/* 512 Megabit */ /* 512 Megabit */
@ -157,7 +157,7 @@ struct nand_flash_dev nand_flash_ids[] = {
* writes possible, but not implemented now * writes possible, but not implemented now
*/ */
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
NAND_IS_AND | NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, NAND_IS_AND | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
{NULL,} {NULL,}
}; };
@ -174,8 +174,9 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"}, {NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"}, {NAND_MFR_MICRON, "Micron"},
{NAND_MFR_AMD, "AMD"}, {NAND_MFR_AMD, "AMD/Spansion"},
{NAND_MFR_MACRONIX, "Macronix"}, {NAND_MFR_MACRONIX, "Macronix"},
{NAND_MFR_EON, "Eon"},
{0x0, "Unknown"} {0x0, "Unknown"}
}; };

Просмотреть файл

@ -447,8 +447,6 @@ static unsigned int rptwear_cnt = 0;
/* MTD structure for NAND controller */ /* MTD structure for NAND controller */
static struct mtd_info *nsmtd; static struct mtd_info *nsmtd;
static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
/* /*
* Allocate array of page pointers, create slab allocation for an array * Allocate array of page pointers, create slab allocation for an array
* and initialize the array by NULL pointers. * and initialize the array by NULL pointers.
@ -2189,19 +2187,6 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
return; return;
} }
static int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
if (!memcmp(buf, &ns_verify_buf[0], len)) {
NS_DBG("verify_buf: the buffer is OK\n");
return 0;
} else {
NS_DBG("verify_buf: the buffer is wrong\n");
return -EFAULT;
}
}
/* /*
* Module initialization function * Module initialization function
*/ */
@ -2236,7 +2221,6 @@ static int __init ns_init_module(void)
chip->dev_ready = ns_device_ready; chip->dev_ready = ns_device_ready;
chip->write_buf = ns_nand_write_buf; chip->write_buf = ns_nand_write_buf;
chip->read_buf = ns_nand_read_buf; chip->read_buf = ns_nand_read_buf;
chip->verify_buf = ns_nand_verify_buf;
chip->read_word = ns_nand_read_word; chip->read_word = ns_nand_read_word;
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
@ -2333,6 +2317,7 @@ static int __init ns_init_module(void)
uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
if (new_size >> overridesize != nsmtd->erasesize) { if (new_size >> overridesize != nsmtd->erasesize) {
NS_ERR("overridesize is too big\n"); NS_ERR("overridesize is too big\n");
retval = -EINVAL;
goto err_exit; goto err_exit;
} }
/* N.B. This relies on nand_scan not doing anything with the size before we change it */ /* N.B. This relies on nand_scan not doing anything with the size before we change it */

Просмотреть файл

@ -140,18 +140,6 @@ static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); out_be32(ndfc->ndfcbase + NDFC_DATA, *p++);
} }
static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
struct nand_chip *chip = mtd->priv;
struct ndfc_controller *ndfc = chip->priv;
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA))
return -EFAULT;
return 0;
}
/* /*
* Initialize chip structure * Initialize chip structure
*/ */
@ -172,7 +160,6 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
chip->controller = &ndfc->ndfc_control; chip->controller = &ndfc->ndfc_control;
chip->read_buf = ndfc_read_buf; chip->read_buf = ndfc_read_buf;
chip->write_buf = ndfc_write_buf; chip->write_buf = ndfc_write_buf;
chip->verify_buf = ndfc_verify_buf;
chip->ecc.correct = nand_correct_data; chip->ecc.correct = nand_correct_data;
chip->ecc.hwctl = ndfc_enable_hwecc; chip->ecc.hwctl = ndfc_enable_hwecc;
chip->ecc.calculate = ndfc_calculate_ecc; chip->ecc.calculate = ndfc_calculate_ecc;

Просмотреть файл

@ -112,22 +112,6 @@ static void nuc900_nand_write_buf(struct mtd_info *mtd,
write_data_reg(nand, buf[i]); write_data_reg(nand, buf[i]);
} }
static int nuc900_verify_buf(struct mtd_info *mtd,
const unsigned char *buf, int len)
{
int i;
struct nuc900_nand *nand;
nand = container_of(mtd, struct nuc900_nand, mtd);
for (i = 0; i < len; i++) {
if (buf[i] != (unsigned char)read_data_reg(nand))
return -EFAULT;
}
return 0;
}
static int nuc900_check_rb(struct nuc900_nand *nand) static int nuc900_check_rb(struct nuc900_nand *nand)
{ {
unsigned int val; unsigned int val;
@ -292,7 +276,6 @@ static int __devinit nuc900_nand_probe(struct platform_device *pdev)
chip->read_byte = nuc900_nand_read_byte; chip->read_byte = nuc900_nand_read_byte;
chip->write_buf = nuc900_nand_write_buf; chip->write_buf = nuc900_nand_write_buf;
chip->read_buf = nuc900_nand_read_buf; chip->read_buf = nuc900_nand_read_buf;
chip->verify_buf = nuc900_verify_buf;
chip->chip_delay = 50; chip->chip_delay = 50;
chip->options = 0; chip->options = 0;
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;

Просмотреть файл

@ -425,7 +425,7 @@ static void omap_nand_dma_callback(void *data)
} }
/* /*
* omap_nand_dma_transfer: configer and start dma transfer * omap_nand_dma_transfer: configure and start dma transfer
* @mtd: MTD device structure * @mtd: MTD device structure
* @addr: virtual address in RAM of source/destination * @addr: virtual address in RAM of source/destination
* @len: number of data bytes to be transferred * @len: number of data bytes to be transferred
@ -546,7 +546,7 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd,
} }
/* /*
* omap_nand_irq - GMPC irq handler * omap_nand_irq - GPMC irq handler
* @this_irq: gpmc irq number * @this_irq: gpmc irq number
* @dev: omap_nand_info structure pointer is passed here * @dev: omap_nand_info structure pointer is passed here
*/ */
@ -697,27 +697,6 @@ out_copy:
omap_write_buf8(mtd, buf, len); omap_write_buf8(mtd, buf, len);
} }
/**
* omap_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*/
static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
u16 *p = (u16 *) buf;
len >>= 1;
while (len--) {
if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
return -EFAULT;
}
return 0;
}
/** /**
* gen_true_ecc - This function will generate true ECC value * gen_true_ecc - This function will generate true ECC value
* @ecc_buf: buffer to store ecc code * @ecc_buf: buffer to store ecc code
@ -1326,8 +1305,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
/* /*
* If RDY/BSY line is connected to OMAP then use the omap ready * If RDY/BSY line is connected to OMAP then use the omap ready
* funcrtion and the generic nand_wait function which reads the status * function and the generic nand_wait function which reads the status
* register after monitoring the RDY/BSY line.Otherwise use a standard * register after monitoring the RDY/BSY line. Otherwise use a standard
* chip delay which is slightly more than tR (AC Timing) of the NAND * chip delay which is slightly more than tR (AC Timing) of the NAND
* device and read status register until you get a failure or success * device and read status register until you get a failure or success
*/ */
@ -1428,9 +1407,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
goto out_release_mem_region; goto out_release_mem_region;
} }
info->nand.verify_buf = omap_verify_buf; /* select the ecc type */
/* selsect the ecc type */
if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT) if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT)
info->nand.ecc.mode = NAND_ECC_SOFT; info->nand.ecc.mode = NAND_ECC_SOFT;
else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) || else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) ||
@ -1536,7 +1513,8 @@ static int omap_nand_remove(struct platform_device *pdev)
/* Release NAND device, its internal structures and partitions */ /* Release NAND device, its internal structures and partitions */
nand_release(&info->mtd); nand_release(&info->mtd);
iounmap(info->nand.IO_ADDR_R); iounmap(info->nand.IO_ADDR_R);
kfree(&info->mtd); release_mem_region(info->phys_base, NAND_IO_SIZE);
kfree(info);
return 0; return 0;
} }

Просмотреть файл

@ -21,7 +21,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/sizes.h> #include <asm/sizes.h>
#include <mach/hardware.h>
#include <linux/platform_data/mtd-orion_nand.h> #include <linux/platform_data/mtd-orion_nand.h>
static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)

Просмотреть файл

@ -37,6 +37,11 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
const char **part_types; const char **part_types;
int err = 0; int err = 0;
if (!pdata) {
dev_err(&pdev->dev, "platform_nand_data is missing\n");
return -EINVAL;
}
if (pdata->chip.nr_chips < 1) { if (pdata->chip.nr_chips < 1) {
dev_err(&pdev->dev, "invalid number of chips specified\n"); dev_err(&pdev->dev, "invalid number of chips specified\n");
return -EINVAL; return -EINVAL;

Просмотреть файл

@ -683,11 +683,13 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
info->state = STATE_IDLE; info->state = STATE_IDLE;
} }
static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required) struct nand_chip *chip, const uint8_t *buf, int oob_required)
{ {
chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, buf, mtd->writesize);
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
} }
static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
@ -771,12 +773,6 @@ static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
info->buf_start += real_len; info->buf_start += real_len;
} }
static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
return 0;
}
static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
{ {
return; return;
@ -1007,7 +1003,6 @@ KEEP_CONFIG:
chip->ecc.size = host->page_size; chip->ecc.size = host->page_size;
chip->ecc.strength = 1; chip->ecc.strength = 1;
chip->options |= NAND_NO_READRDY;
if (host->reg_ndcr & NDCR_DWIDTH_M) if (host->reg_ndcr & NDCR_DWIDTH_M)
chip->options |= NAND_BUSWIDTH_16; chip->options |= NAND_BUSWIDTH_16;
@ -1070,7 +1065,6 @@ static int alloc_nand_resource(struct platform_device *pdev)
chip->read_byte = pxa3xx_nand_read_byte; chip->read_byte = pxa3xx_nand_read_byte;
chip->read_buf = pxa3xx_nand_read_buf; chip->read_buf = pxa3xx_nand_read_buf;
chip->write_buf = pxa3xx_nand_write_buf; chip->write_buf = pxa3xx_nand_write_buf;
chip->verify_buf = pxa3xx_nand_verify_buf;
} }
spin_lock_init(&chip->controller->lock); spin_lock_init(&chip->controller->lock);

Просмотреть файл

@ -309,27 +309,6 @@ static uint8_t r852_read_byte(struct mtd_info *mtd)
return r852_read_reg(dev, R852_DATALINE); return r852_read_reg(dev, R852_DATALINE);
} }
/*
* Readback the buffer to verify it
*/
int r852_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
struct r852_device *dev = r852_get_dev(mtd);
/* We can't be sure about anything here... */
if (dev->card_unstable)
return -1;
/* This will never happen, unless you wired up a nand chip
with > 512 bytes page size to the reader */
if (len > SM_SECTOR_SIZE)
return 0;
r852_read_buf(mtd, dev->tmp_buffer, len);
return memcmp(buf, dev->tmp_buffer, len);
}
/* /*
* Control several chip lines & send commands * Control several chip lines & send commands
*/ */
@ -882,7 +861,6 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
chip->read_byte = r852_read_byte; chip->read_byte = r852_read_byte;
chip->read_buf = r852_read_buf; chip->read_buf = r852_read_buf;
chip->write_buf = r852_write_buf; chip->write_buf = r852_write_buf;
chip->verify_buf = r852_verify_buf;
/* ecc */ /* ecc */
chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->ecc.mode = NAND_ECC_HW_SYNDROME;

Просмотреть файл

@ -21,6 +21,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#define pr_fmt(fmt) "nand-s3c2410: " fmt
#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
#define DEBUG #define DEBUG
#endif #endif
@ -30,6 +32,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -43,24 +46,9 @@
#include <linux/mtd/nand_ecc.h> #include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <plat/regs-nand.h> #include <plat/regs-nand.h>
#include <linux/platform_data/mtd-nand-s3c2410.h> #include <linux/platform_data/mtd-nand-s3c2410.h>
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
#else
static int hardware_ecc = 0;
#endif
#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
static const int clock_stop = 1;
#else
static const int clock_stop = 0;
#endif
/* new oob placement block for use with hardware ecc generation /* new oob placement block for use with hardware ecc generation
*/ */
@ -109,9 +97,8 @@ enum s3c_nand_clk_state {
* @mtds: An array of MTD instances on this controoler. * @mtds: An array of MTD instances on this controoler.
* @platform: The platform data for this board. * @platform: The platform data for this board.
* @device: The platform device we bound to. * @device: The platform device we bound to.
* @area: The IO area resource that came from request_mem_region().
* @clk: The clock resource for this controller. * @clk: The clock resource for this controller.
* @regs: The area mapped for the hardware registers described by @area. * @regs: The area mapped for the hardware registers.
* @sel_reg: Pointer to the register controlling the NAND selection. * @sel_reg: Pointer to the register controlling the NAND selection.
* @sel_bit: The bit in @sel_reg to select the NAND chip. * @sel_bit: The bit in @sel_reg to select the NAND chip.
* @mtd_count: The number of MTDs created from this controller. * @mtd_count: The number of MTDs created from this controller.
@ -128,7 +115,6 @@ struct s3c2410_nand_info {
/* device info */ /* device info */
struct device *device; struct device *device;
struct resource *area;
struct clk *clk; struct clk *clk;
void __iomem *regs; void __iomem *regs;
void __iomem *sel_reg; void __iomem *sel_reg;
@ -169,7 +155,11 @@ static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
static inline int allow_clk_suspend(struct s3c2410_nand_info *info) static inline int allow_clk_suspend(struct s3c2410_nand_info *info)
{ {
return clock_stop; #ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
return 1;
#else
return 0;
#endif
} }
/** /**
@ -215,7 +205,8 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
pr_debug("result %d from %ld, %d\n", result, clk, wanted); pr_debug("result %d from %ld, %d\n", result, clk, wanted);
if (result > max) { if (result > max) {
printk("%d ns is too big for current clock rate %ld\n", wanted, clk); pr_err("%d ns is too big for current clock rate %ld\n",
wanted, clk);
return -1; return -1;
} }
@ -225,7 +216,7 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
return result; return result;
} }
#define to_ns(ticks,clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) #define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk))
/* controller setup */ /* controller setup */
@ -268,7 +259,8 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
} }
dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate),
twrph1, to_ns(twrph1, clkrate));
switch (info->cpu_type) { switch (info->cpu_type) {
case TYPE_S3C2410: case TYPE_S3C2410:
@ -325,13 +317,13 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
if (ret < 0) if (ret < 0)
return ret; return ret;
switch (info->cpu_type) { switch (info->cpu_type) {
case TYPE_S3C2410: case TYPE_S3C2410:
default: default:
break; break;
case TYPE_S3C2440: case TYPE_S3C2440:
case TYPE_S3C2412: case TYPE_S3C2412:
/* enable the controller and de-assert nFCE */ /* enable the controller and de-assert nFCE */
writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
@ -450,6 +442,7 @@ static int s3c2412_nand_devready(struct mtd_info *mtd)
/* ECC handling functions */ /* ECC handling functions */
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc) u_char *read_ecc, u_char *calc_ecc)
{ {
@ -463,10 +456,8 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
diff1 = read_ecc[1] ^ calc_ecc[1]; diff1 = read_ecc[1] ^ calc_ecc[1];
diff2 = read_ecc[2] ^ calc_ecc[2]; diff2 = read_ecc[2] ^ calc_ecc[2];
pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n",
__func__, __func__, 3, read_ecc, 3, calc_ecc,
read_ecc[0], read_ecc[1], read_ecc[2],
calc_ecc[0], calc_ecc[1], calc_ecc[2],
diff0, diff1, diff2); diff0, diff1, diff2);
if (diff0 == 0 && diff1 == 0 && diff2 == 0) if (diff0 == 0 && diff1 == 0 && diff2 == 0)
@ -546,7 +537,8 @@ static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
unsigned long ctrl; unsigned long ctrl;
ctrl = readl(info->regs + S3C2440_NFCONT); ctrl = readl(info->regs + S3C2440_NFCONT);
writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC,
info->regs + S3C2440_NFCONT);
} }
static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
@ -558,7 +550,8 @@ static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
} }
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{ {
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@ -566,13 +559,13 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code);
ecc_code[0], ecc_code[1], ecc_code[2]);
return 0; return 0;
} }
static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{ {
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); unsigned long ecc = readl(info->regs + S3C2412_NFMECC0);
@ -581,12 +574,13 @@ static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
ecc_code[1] = ecc >> 8; ecc_code[1] = ecc >> 8;
ecc_code[2] = ecc >> 16; ecc_code[2] = ecc >> 16;
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code);
return 0; return 0;
} }
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{ {
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
@ -599,6 +593,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
return 0; return 0;
} }
#endif
/* over-ride the standard functions for a little more speed. We can /* over-ride the standard functions for a little more speed. We can
* use read/write block to move the data buffers to/from the controller * use read/write block to move the data buffers to/from the controller
@ -625,13 +620,15 @@ static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
} }
} }
static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
writesb(this->IO_ADDR_W, buf, len); writesb(this->IO_ADDR_W, buf, len);
} }
static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{ {
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@ -675,7 +672,8 @@ static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
CPUFREQ_TRANSITION_NOTIFIER); CPUFREQ_TRANSITION_NOTIFIER);
} }
static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) static inline void
s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
{ {
cpufreq_unregister_notifier(&info->freq_transition, cpufreq_unregister_notifier(&info->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER); CPUFREQ_TRANSITION_NOTIFIER);
@ -687,7 +685,8 @@ static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
return 0; return 0;
} }
static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) static inline void
s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
{ {
} }
#endif #endif
@ -717,29 +716,12 @@ static int s3c24xx_nand_remove(struct platform_device *pdev)
pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
nand_release(&ptr->mtd); nand_release(&ptr->mtd);
} }
kfree(info->mtds);
} }
/* free the common resources */ /* free the common resources */
if (!IS_ERR(info->clk)) { if (!IS_ERR(info->clk))
s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
clk_put(info->clk);
}
if (info->regs != NULL) {
iounmap(info->regs);
info->regs = NULL;
}
if (info->area != NULL) {
release_resource(info->area);
kfree(info->area);
info->area = NULL;
}
kfree(info);
return 0; return 0;
} }
@ -810,7 +792,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
dev_info(info->device, "System booted from NAND\n"); dev_info(info->device, "System booted from NAND\n");
break; break;
} }
chip->IO_ADDR_R = chip->IO_ADDR_W; chip->IO_ADDR_R = chip->IO_ADDR_W;
@ -819,32 +801,31 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
nmtd->mtd.owner = THIS_MODULE; nmtd->mtd.owner = THIS_MODULE;
nmtd->set = set; nmtd->set = set;
if (hardware_ecc) { #ifdef CONFIG_MTD_NAND_S3C2410_HWECC
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
chip->ecc.correct = s3c2410_nand_correct_data;
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.strength = 1;
switch (info->cpu_type) {
case TYPE_S3C2410:
chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.calculate = s3c2410_nand_calculate_ecc;
chip->ecc.correct = s3c2410_nand_correct_data; break;
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.strength = 1;
switch (info->cpu_type) { case TYPE_S3C2412:
case TYPE_S3C2410: chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
chip->ecc.hwctl = s3c2410_nand_enable_hwecc; chip->ecc.calculate = s3c2412_nand_calculate_ecc;
chip->ecc.calculate = s3c2410_nand_calculate_ecc; break;
break;
case TYPE_S3C2412: case TYPE_S3C2440:
chip->ecc.hwctl = s3c2412_nand_enable_hwecc; chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
chip->ecc.calculate = s3c2412_nand_calculate_ecc; chip->ecc.calculate = s3c2440_nand_calculate_ecc;
break; break;
case TYPE_S3C2440:
chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
chip->ecc.calculate = s3c2440_nand_calculate_ecc;
break;
}
} else {
chip->ecc.mode = NAND_ECC_SOFT;
} }
#else
chip->ecc.mode = NAND_ECC_SOFT;
#endif
if (set->ecc_layout != NULL) if (set->ecc_layout != NULL)
chip->ecc.layout = set->ecc_layout; chip->ecc.layout = set->ecc_layout;
@ -921,7 +902,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
static int s3c24xx_nand_probe(struct platform_device *pdev) static int s3c24xx_nand_probe(struct platform_device *pdev)
{ {
struct s3c2410_platform_nand *plat = to_nand_plat(pdev); struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
enum s3c_cpu_type cpu_type; enum s3c_cpu_type cpu_type;
struct s3c2410_nand_info *info; struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_mtd *nmtd;
struct s3c2410_nand_set *sets; struct s3c2410_nand_set *sets;
@ -935,7 +916,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
pr_debug("s3c2410_nand_probe(%p)\n", pdev); pr_debug("s3c2410_nand_probe(%p)\n", pdev);
info = kzalloc(sizeof(*info), GFP_KERNEL); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) { if (info == NULL) {
dev_err(&pdev->dev, "no memory for flash info\n"); dev_err(&pdev->dev, "no memory for flash info\n");
err = -ENOMEM; err = -ENOMEM;
@ -949,7 +930,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
/* get the clock source and enable it */ /* get the clock source and enable it */
info->clk = clk_get(&pdev->dev, "nand"); info->clk = devm_clk_get(&pdev->dev, "nand");
if (IS_ERR(info->clk)) { if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get clock\n"); dev_err(&pdev->dev, "failed to get clock\n");
err = -ENOENT; err = -ENOENT;
@ -961,22 +942,14 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
/* allocate and map the resource */ /* allocate and map the resource */
/* currently we assume we have the one resource */ /* currently we assume we have the one resource */
res = pdev->resource; res = pdev->resource;
size = resource_size(res); size = resource_size(res);
info->area = request_mem_region(res->start, size, pdev->name); info->device = &pdev->dev;
info->platform = plat;
if (info->area == NULL) { info->cpu_type = cpu_type;
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -ENOENT;
goto exit_error;
}
info->device = &pdev->dev;
info->platform = plat;
info->regs = ioremap(res->start, size);
info->cpu_type = cpu_type;
info->regs = devm_request_and_ioremap(&pdev->dev, res);
if (info->regs == NULL) { if (info->regs == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n"); dev_err(&pdev->dev, "cannot reserve register region\n");
err = -EIO; err = -EIO;
@ -999,7 +972,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
/* allocate our information */ /* allocate our information */
size = nr_sets * sizeof(*info->mtds); size = nr_sets * sizeof(*info->mtds);
info->mtds = kzalloc(size, GFP_KERNEL); info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (info->mtds == NULL) { if (info->mtds == NULL) {
dev_err(&pdev->dev, "failed to allocate mtd storage\n"); dev_err(&pdev->dev, "failed to allocate mtd storage\n");
err = -ENOMEM; err = -ENOMEM;
@ -1011,7 +984,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
nmtd = info->mtds; nmtd = info->mtds;
for (setno = 0; setno < nr_sets; setno++, nmtd++) { for (setno = 0; setno < nr_sets; setno++, nmtd++) {
pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info); pr_debug("initialising set %d (%p, info %p)\n",
setno, nmtd, info);
s3c2410_nand_init_chip(info, nmtd, sets); s3c2410_nand_init_chip(info, nmtd, sets);
@ -1134,20 +1108,7 @@ static struct platform_driver s3c24xx_nand_driver = {
}, },
}; };
static int __init s3c2410_nand_init(void) module_platform_driver(s3c24xx_nand_driver);
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
return platform_driver_register(&s3c24xx_nand_driver);
}
static void __exit s3c2410_nand_exit(void)
{
platform_driver_unregister(&s3c24xx_nand_driver);
}
module_init(s3c2410_nand_init);
module_exit(s3c2410_nand_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");

Просмотреть файл

@ -24,10 +24,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
@ -43,11 +45,17 @@ static struct nand_ecclayout flctl_4secc_oob_16 = {
}; };
static struct nand_ecclayout flctl_4secc_oob_64 = { static struct nand_ecclayout flctl_4secc_oob_64 = {
.eccbytes = 10, .eccbytes = 4 * 10,
.eccpos = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, .eccpos = {
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
.oobfree = { .oobfree = {
{.offset = 60, {.offset = 2, .length = 4},
. length = 4} }, {.offset = 16, .length = 6},
{.offset = 32, .length = 6},
{.offset = 48, .length = 6} },
}; };
static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
@ -61,15 +69,15 @@ static struct nand_bbt_descr flctl_4secc_smallpage = {
static struct nand_bbt_descr flctl_4secc_largepage = { static struct nand_bbt_descr flctl_4secc_largepage = {
.options = NAND_BBT_SCAN2NDPAGE, .options = NAND_BBT_SCAN2NDPAGE,
.offs = 58, .offs = 0,
.len = 2, .len = 2,
.pattern = scan_ff_pattern, .pattern = scan_ff_pattern,
}; };
static void empty_fifo(struct sh_flctl *flctl) static void empty_fifo(struct sh_flctl *flctl)
{ {
writel(0x000c0000, FLINTDMACR(flctl)); /* FIFO Clear */ writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl));
writel(0x00000000, FLINTDMACR(flctl)); /* Clear Error flags */ writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
} }
static void start_translation(struct sh_flctl *flctl) static void start_translation(struct sh_flctl *flctl)
@ -158,27 +166,56 @@ static void wait_wfifo_ready(struct sh_flctl *flctl)
timeout_error(flctl, __func__); timeout_error(flctl, __func__);
} }
static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) static enum flctl_ecc_res_t wait_recfifo_ready
(struct sh_flctl *flctl, int sector_number)
{ {
uint32_t timeout = LOOP_TIMEOUT_MAX; uint32_t timeout = LOOP_TIMEOUT_MAX;
int checked[4];
void __iomem *ecc_reg[4]; void __iomem *ecc_reg[4];
int i; int i;
int state = FL_SUCCESS;
uint32_t data, size; uint32_t data, size;
memset(checked, 0, sizeof(checked)); /*
* First this loops checks in FLDTCNTR if we are ready to read out the
* oob data. This is the case if either all went fine without errors or
* if the bottom part of the loop corrected the errors or marked them as
* uncorrectable and the controller is given time to push the data into
* the FIFO.
*/
while (timeout--) { while (timeout--) {
/* check if all is ok and we can read out the OOB */
size = readl(FLDTCNTR(flctl)) >> 24; size = readl(FLDTCNTR(flctl)) >> 24;
if (size & 0xFF) if ((size & 0xFF) == 4)
return 0; /* success */ return state;
if (readl(FL4ECCCR(flctl)) & _4ECCFA) /* check if a correction code has been calculated */
return 1; /* can't correct */ if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) {
/*
udelay(1); * either we wait for the fifo to be filled or a
if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) * correction pattern is being generated
*/
udelay(1);
continue; continue;
}
/* check for an uncorrectable error */
if (readl(FL4ECCCR(flctl)) & _4ECCFA) {
/* check if we face a non-empty page */
for (i = 0; i < 512; i++) {
if (flctl->done_buff[i] != 0xff) {
state = FL_ERROR; /* can't correct */
break;
}
}
if (state == FL_SUCCESS)
dev_dbg(&flctl->pdev->dev,
"reading empty sector %d, ecc error ignored\n",
sector_number);
writel(0, FL4ECCCR(flctl));
continue;
}
/* start error correction */ /* start error correction */
ecc_reg[0] = FL4ECCRESULT0(flctl); ecc_reg[0] = FL4ECCRESULT0(flctl);
@ -187,28 +224,26 @@ static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number)
ecc_reg[3] = FL4ECCRESULT3(flctl); ecc_reg[3] = FL4ECCRESULT3(flctl);
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
uint8_t org;
int index;
data = readl(ecc_reg[i]); data = readl(ecc_reg[i]);
if (data != INIT_FL4ECCRESULT_VAL && !checked[i]) {
uint8_t org;
int index;
if (flctl->page_size) if (flctl->page_size)
index = (512 * sector_number) + index = (512 * sector_number) +
(data >> 16); (data >> 16);
else else
index = data >> 16; index = data >> 16;
org = flctl->done_buff[index]; org = flctl->done_buff[index];
flctl->done_buff[index] = org ^ (data & 0xFF); flctl->done_buff[index] = org ^ (data & 0xFF);
checked[i] = 1;
}
} }
state = FL_REPAIRABLE;
writel(0, FL4ECCCR(flctl)); writel(0, FL4ECCCR(flctl));
} }
timeout_error(flctl, __func__); timeout_error(flctl, __func__);
return 1; /* timeout */ return FL_TIMEOUT; /* timeout */
} }
static void wait_wecfifo_ready(struct sh_flctl *flctl) static void wait_wecfifo_ready(struct sh_flctl *flctl)
@ -241,31 +276,33 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
{ {
int i, len_4align; int i, len_4align;
unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
void *fifo_addr = (void *)FLDTFIFO(flctl);
len_4align = (rlen + 3) / 4; len_4align = (rlen + 3) / 4;
for (i = 0; i < len_4align; i++) { for (i = 0; i < len_4align; i++) {
wait_rfifo_ready(flctl); wait_rfifo_ready(flctl);
buf[i] = readl(fifo_addr); buf[i] = readl(FLDTFIFO(flctl));
buf[i] = be32_to_cpu(buf[i]); buf[i] = be32_to_cpu(buf[i]);
} }
} }
static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector) static enum flctl_ecc_res_t read_ecfiforeg
(struct sh_flctl *flctl, uint8_t *buff, int sector)
{ {
int i; int i;
enum flctl_ecc_res_t res;
unsigned long *ecc_buf = (unsigned long *)buff; unsigned long *ecc_buf = (unsigned long *)buff;
void *fifo_addr = (void *)FLECFIFO(flctl);
for (i = 0; i < 4; i++) { res = wait_recfifo_ready(flctl , sector);
if (wait_recfifo_ready(flctl , sector))
return 1; if (res != FL_ERROR) {
ecc_buf[i] = readl(fifo_addr); for (i = 0; i < 4; i++) {
ecc_buf[i] = be32_to_cpu(ecc_buf[i]); ecc_buf[i] = readl(FLECFIFO(flctl));
ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
}
} }
return 0; return res;
} }
static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
@ -281,6 +318,18 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
} }
} }
static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
{
int i, len_4align;
unsigned long *data = (unsigned long *)&flctl->done_buff[offset];
len_4align = (rlen + 3) / 4;
for (i = 0; i < len_4align; i++) {
wait_wecfifo_ready(flctl);
writel(cpu_to_be32(data[i]), FLECFIFO(flctl));
}
}
static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
{ {
struct sh_flctl *flctl = mtd_to_flctl(mtd); struct sh_flctl *flctl = mtd_to_flctl(mtd);
@ -346,73 +395,65 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page) uint8_t *buf, int oob_required, int page)
{ {
int i, eccsize = chip->ecc.size; chip->read_buf(mtd, buf, mtd->writesize);
int eccbytes = chip->ecc.bytes; if (oob_required)
int eccsteps = chip->ecc.steps; chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
uint8_t *p = buf;
struct sh_flctl *flctl = mtd_to_flctl(mtd);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->read_buf(mtd, p, eccsize);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
if (flctl->hwecc_cant_correct[i])
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += 0; /* FIXME */
}
return 0; return 0;
} }
static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required) const uint8_t *buf, int oob_required)
{ {
int i, eccsize = chip->ecc.size; chip->write_buf(mtd, buf, mtd->writesize);
int eccbytes = chip->ecc.bytes; chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
int eccsteps = chip->ecc.steps; return 0;
const uint8_t *p = buf;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->write_buf(mtd, p, eccsize);
} }
static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
{ {
struct sh_flctl *flctl = mtd_to_flctl(mtd); struct sh_flctl *flctl = mtd_to_flctl(mtd);
int sector, page_sectors; int sector, page_sectors;
enum flctl_ecc_res_t ecc_result;
if (flctl->page_size) page_sectors = flctl->page_size ? 4 : 1;
page_sectors = 4;
else
page_sectors = 1;
writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT,
FLCMNCR(flctl));
set_cmd_regs(mtd, NAND_CMD_READ0, set_cmd_regs(mtd, NAND_CMD_READ0,
(NAND_CMD_READSTART << 8) | NAND_CMD_READ0); (NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT,
FLCMNCR(flctl));
writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
writel(page_addr << 2, FLADR(flctl));
empty_fifo(flctl);
start_translation(flctl);
for (sector = 0; sector < page_sectors; sector++) { for (sector = 0; sector < page_sectors; sector++) {
int ret;
empty_fifo(flctl);
writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl));
writel(page_addr << 2 | sector, FLADR(flctl));
start_translation(flctl);
read_fiforeg(flctl, 512, 512 * sector); read_fiforeg(flctl, 512, 512 * sector);
ret = read_ecfiforeg(flctl, ecc_result = read_ecfiforeg(flctl,
&flctl->done_buff[mtd->writesize + 16 * sector], &flctl->done_buff[mtd->writesize + 16 * sector],
sector); sector);
if (ret) switch (ecc_result) {
flctl->hwecc_cant_correct[sector] = 1; case FL_REPAIRABLE:
dev_info(&flctl->pdev->dev,
writel(0x0, FL4ECCCR(flctl)); "applied ecc on page 0x%x", page_addr);
wait_completion(flctl); flctl->mtd.ecc_stats.corrected++;
break;
case FL_ERROR:
dev_warn(&flctl->pdev->dev,
"page 0x%x contains corrupted data\n",
page_addr);
flctl->mtd.ecc_stats.failed++;
break;
default:
;
}
} }
wait_completion(flctl);
writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT),
FLCMNCR(flctl)); FLCMNCR(flctl));
} }
@ -420,30 +461,20 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
static void execmd_read_oob(struct mtd_info *mtd, int page_addr) static void execmd_read_oob(struct mtd_info *mtd, int page_addr)
{ {
struct sh_flctl *flctl = mtd_to_flctl(mtd); struct sh_flctl *flctl = mtd_to_flctl(mtd);
int page_sectors = flctl->page_size ? 4 : 1;
int i;
set_cmd_regs(mtd, NAND_CMD_READ0, set_cmd_regs(mtd, NAND_CMD_READ0,
(NAND_CMD_READSTART << 8) | NAND_CMD_READ0); (NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
empty_fifo(flctl); empty_fifo(flctl);
if (flctl->page_size) {
int i;
/* In case that the page size is 2k */
for (i = 0; i < 16 * 3; i++)
flctl->done_buff[i] = 0xFF;
set_addr(mtd, 3 * 528 + 512, page_addr); for (i = 0; i < page_sectors; i++) {
set_addr(mtd, (512 + 16) * i + 512 , page_addr);
writel(16, FLDTCNTR(flctl)); writel(16, FLDTCNTR(flctl));
start_translation(flctl); start_translation(flctl);
read_fiforeg(flctl, 16, 16 * 3); read_fiforeg(flctl, 16, 16 * i);
wait_completion(flctl);
} else {
/* In case that the page size is 512b */
set_addr(mtd, 512, page_addr);
writel(16, FLDTCNTR(flctl));
start_translation(flctl);
read_fiforeg(flctl, 16, 0);
wait_completion(flctl); wait_completion(flctl);
} }
} }
@ -451,34 +482,26 @@ static void execmd_read_oob(struct mtd_info *mtd, int page_addr)
static void execmd_write_page_sector(struct mtd_info *mtd) static void execmd_write_page_sector(struct mtd_info *mtd)
{ {
struct sh_flctl *flctl = mtd_to_flctl(mtd); struct sh_flctl *flctl = mtd_to_flctl(mtd);
int i, page_addr = flctl->seqin_page_addr; int page_addr = flctl->seqin_page_addr;
int sector, page_sectors; int sector, page_sectors;
if (flctl->page_size) page_sectors = flctl->page_size ? 4 : 1;
page_sectors = 4;
else
page_sectors = 1;
writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl));
set_cmd_regs(mtd, NAND_CMD_PAGEPROG, set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
(NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
empty_fifo(flctl);
writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl));
writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
writel(page_addr << 2, FLADR(flctl));
start_translation(flctl);
for (sector = 0; sector < page_sectors; sector++) { for (sector = 0; sector < page_sectors; sector++) {
empty_fifo(flctl);
writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl));
writel(page_addr << 2 | sector, FLADR(flctl));
start_translation(flctl);
write_fiforeg(flctl, 512, 512 * sector); write_fiforeg(flctl, 512, 512 * sector);
write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector);
for (i = 0; i < 4; i++) {
wait_wecfifo_ready(flctl); /* wait for write ready */
writel(0xFFFFFFFF, FLECFIFO(flctl));
}
wait_completion(flctl);
} }
wait_completion(flctl);
writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl));
} }
@ -488,18 +511,12 @@ static void execmd_write_oob(struct mtd_info *mtd)
int page_addr = flctl->seqin_page_addr; int page_addr = flctl->seqin_page_addr;
int sector, page_sectors; int sector, page_sectors;
if (flctl->page_size) { page_sectors = flctl->page_size ? 4 : 1;
sector = 3;
page_sectors = 4;
} else {
sector = 0;
page_sectors = 1;
}
set_cmd_regs(mtd, NAND_CMD_PAGEPROG, set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
(NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
for (; sector < page_sectors; sector++) { for (sector = 0; sector < page_sectors; sector++) {
empty_fifo(flctl); empty_fifo(flctl);
set_addr(mtd, sector * 528 + 512, page_addr); set_addr(mtd, sector * 528 + 512, page_addr);
writel(16, FLDTCNTR(flctl)); /* set read size */ writel(16, FLDTCNTR(flctl)); /* set read size */
@ -731,10 +748,9 @@ static void flctl_select_chip(struct mtd_info *mtd, int chipnr)
static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{ {
struct sh_flctl *flctl = mtd_to_flctl(mtd); struct sh_flctl *flctl = mtd_to_flctl(mtd);
int i, index = flctl->index; int index = flctl->index;
for (i = 0; i < len; i++) memcpy(&flctl->done_buff[index], buf, len);
flctl->done_buff[index + i] = buf[i];
flctl->index += len; flctl->index += len;
} }
@ -763,20 +779,11 @@ static uint16_t flctl_read_word(struct mtd_info *mtd)
static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{ {
int i; struct sh_flctl *flctl = mtd_to_flctl(mtd);
int index = flctl->index;
for (i = 0; i < len; i++) memcpy(buf, &flctl->done_buff[index], len);
buf[i] = flctl_read_byte(mtd); flctl->index += len;
}
static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
for (i = 0; i < len; i++)
if (buf[i] != flctl_read_byte(mtd))
return -EFAULT;
return 0;
} }
static int flctl_chip_init_tail(struct mtd_info *mtd) static int flctl_chip_init_tail(struct mtd_info *mtd)
@ -831,7 +838,7 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
/* 4 symbols ECC enabled */ /* 4 symbols ECC enabled */
flctl->flcmncr_base |= _4ECCEN | ECCPOS2 | ECCPOS_02; flctl->flcmncr_base |= _4ECCEN;
} else { } else {
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
} }
@ -839,6 +846,16 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
return 0; return 0;
} }
static irqreturn_t flctl_handle_flste(int irq, void *dev_id)
{
struct sh_flctl *flctl = dev_id;
dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl)));
writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
return IRQ_HANDLED;
}
static int __devinit flctl_probe(struct platform_device *pdev) static int __devinit flctl_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
@ -847,6 +864,7 @@ static int __devinit flctl_probe(struct platform_device *pdev)
struct nand_chip *nand; struct nand_chip *nand;
struct sh_flctl_platform_data *pdata; struct sh_flctl_platform_data *pdata;
int ret = -ENXIO; int ret = -ENXIO;
int irq;
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
if (pdata == NULL) { if (pdata == NULL) {
@ -872,14 +890,27 @@ static int __devinit flctl_probe(struct platform_device *pdev)
goto err_iomap; goto err_iomap;
} }
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get flste irq data\n");
goto err_flste;
}
ret = request_irq(irq, flctl_handle_flste, IRQF_SHARED, "flste", flctl);
if (ret) {
dev_err(&pdev->dev, "request interrupt failed.\n");
goto err_flste;
}
platform_set_drvdata(pdev, flctl); platform_set_drvdata(pdev, flctl);
flctl_mtd = &flctl->mtd; flctl_mtd = &flctl->mtd;
nand = &flctl->chip; nand = &flctl->chip;
flctl_mtd->priv = nand; flctl_mtd->priv = nand;
flctl->pdev = pdev; flctl->pdev = pdev;
flctl->flcmncr_base = pdata->flcmncr_val;
flctl->hwecc = pdata->has_hwecc; flctl->hwecc = pdata->has_hwecc;
flctl->holden = pdata->use_holden; flctl->holden = pdata->use_holden;
flctl->flcmncr_base = pdata->flcmncr_val;
flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE;
/* Set address of hardware control function */ /* Set address of hardware control function */
/* 20 us command delay time */ /* 20 us command delay time */
@ -888,7 +919,6 @@ static int __devinit flctl_probe(struct platform_device *pdev)
nand->read_byte = flctl_read_byte; nand->read_byte = flctl_read_byte;
nand->write_buf = flctl_write_buf; nand->write_buf = flctl_write_buf;
nand->read_buf = flctl_read_buf; nand->read_buf = flctl_read_buf;
nand->verify_buf = flctl_verify_buf;
nand->select_chip = flctl_select_chip; nand->select_chip = flctl_select_chip;
nand->cmdfunc = flctl_cmdfunc; nand->cmdfunc = flctl_cmdfunc;
@ -918,6 +948,9 @@ static int __devinit flctl_probe(struct platform_device *pdev)
err_chip: err_chip:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
free_irq(irq, flctl);
err_flste:
iounmap(flctl->reg);
err_iomap: err_iomap:
kfree(flctl); kfree(flctl);
return ret; return ret;
@ -929,6 +962,8 @@ static int __devexit flctl_remove(struct platform_device *pdev)
nand_release(&flctl->mtd); nand_release(&flctl->mtd);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
free_irq(platform_get_irq(pdev, 0), flctl);
iounmap(flctl->reg);
kfree(flctl); kfree(flctl);
return 0; return 0;

Просмотреть файл

@ -98,24 +98,6 @@ static uint16_t socrates_nand_read_word(struct mtd_info *mtd)
return word; return word;
} }
/**
* socrates_nand_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*/
static int socrates_nand_verify_buf(struct mtd_info *mtd, const u8 *buf,
int len)
{
int i;
for (i = 0; i < len; i++) {
if (buf[i] != socrates_nand_read_byte(mtd))
return -EFAULT;
}
return 0;
}
/* /*
* Hardware specific access to control-lines * Hardware specific access to control-lines
*/ */
@ -201,7 +183,6 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev)
nand_chip->read_word = socrates_nand_read_word; nand_chip->read_word = socrates_nand_read_word;
nand_chip->write_buf = socrates_nand_write_buf; nand_chip->write_buf = socrates_nand_write_buf;
nand_chip->read_buf = socrates_nand_read_buf; nand_chip->read_buf = socrates_nand_read_buf;
nand_chip->verify_buf = socrates_nand_verify_buf;
nand_chip->dev_ready = socrates_nand_device_ready; nand_chip->dev_ready = socrates_nand_device_ready;
nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */

Просмотреть файл

@ -256,18 +256,6 @@ static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1);
} }
static int
tmio_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
struct tmio_nand *tmio = mtd_to_tmio(mtd);
u16 *p = (u16 *) buf;
for (len >>= 1; len; len--)
if (*(p++) != tmio_ioread16(tmio->fcr + FCR_DATA))
return -EFAULT;
return 0;
}
static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode) static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{ {
struct tmio_nand *tmio = mtd_to_tmio(mtd); struct tmio_nand *tmio = mtd_to_tmio(mtd);
@ -424,7 +412,6 @@ static int tmio_probe(struct platform_device *dev)
nand_chip->read_byte = tmio_nand_read_byte; nand_chip->read_byte = tmio_nand_read_byte;
nand_chip->write_buf = tmio_nand_write_buf; nand_chip->write_buf = tmio_nand_write_buf;
nand_chip->read_buf = tmio_nand_read_buf; nand_chip->read_buf = tmio_nand_read_buf;
nand_chip->verify_buf = tmio_nand_verify_buf;
/* set eccmode using hardware ECC */ /* set eccmode using hardware ECC */
nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.mode = NAND_ECC_HW;

Просмотреть файл

@ -131,18 +131,6 @@ static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
*buf++ = __raw_readl(ndfdtr); *buf++ = __raw_readl(ndfdtr);
} }
static int txx9ndfmc_verify_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
struct platform_device *dev = mtd_to_platdev(mtd);
void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
while (len--)
if (*buf++ != (uint8_t)__raw_readl(ndfdtr))
return -EFAULT;
return 0;
}
static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl) unsigned int ctrl)
{ {
@ -346,7 +334,6 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
chip->read_byte = txx9ndfmc_read_byte; chip->read_byte = txx9ndfmc_read_byte;
chip->read_buf = txx9ndfmc_read_buf; chip->read_buf = txx9ndfmc_read_buf;
chip->write_buf = txx9ndfmc_write_buf; chip->write_buf = txx9ndfmc_write_buf;
chip->verify_buf = txx9ndfmc_verify_buf;
chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; chip->cmd_ctrl = txx9ndfmc_cmd_ctrl;
chip->dev_ready = txx9ndfmc_dev_ready; chip->dev_ready = txx9ndfmc_dev_ready;
chip->ecc.calculate = txx9ndfmc_calculate_ecc; chip->ecc.calculate = txx9ndfmc_calculate_ecc;

Просмотреть файл

@ -0,0 +1,201 @@
/*
* 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.
*
* Copyright © 2012 John Crispin <blogic@openwrt.org>
*/
#include <linux/mtd/nand.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <lantiq_soc.h>
/* nand registers */
#define EBU_ADDSEL1 0x24
#define EBU_NAND_CON 0xB0
#define EBU_NAND_WAIT 0xB4
#define EBU_NAND_ECC0 0xB8
#define EBU_NAND_ECC_AC 0xBC
/* nand commands */
#define NAND_CMD_ALE (1 << 2)
#define NAND_CMD_CLE (1 << 3)
#define NAND_CMD_CS (1 << 4)
#define NAND_WRITE_CMD_RESET 0xff
#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE)
#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE)
#define NAND_WRITE_DATA (NAND_CMD_CS)
#define NAND_READ_DATA (NAND_CMD_CS)
#define NAND_WAIT_WR_C (1 << 3)
#define NAND_WAIT_RD (0x1)
/* we need to tel the ebu which addr we mapped the nand to */
#define ADDSEL1_MASK(x) (x << 4)
#define ADDSEL1_REGEN 1
/* we need to tell the EBU that we have nand attached and set it up properly */
#define BUSCON1_SETUP (1 << 22)
#define BUSCON1_BCGEN_RES (0x3 << 12)
#define BUSCON1_WAITWRC2 (2 << 8)
#define BUSCON1_WAITRDC2 (2 << 6)
#define BUSCON1_HOLDC1 (1 << 4)
#define BUSCON1_RECOVC1 (1 << 2)
#define BUSCON1_CMULT4 1
#define NAND_CON_CE (1 << 20)
#define NAND_CON_OUT_CS1 (1 << 10)
#define NAND_CON_IN_CS1 (1 << 8)
#define NAND_CON_PRE_P (1 << 7)
#define NAND_CON_WP_P (1 << 6)
#define NAND_CON_SE_P (1 << 5)
#define NAND_CON_CS_P (1 << 4)
#define NAND_CON_CSMUX (1 << 1)
#define NAND_CON_NANDM 1
static void xway_reset_chip(struct nand_chip *chip)
{
unsigned long nandaddr = (unsigned long) chip->IO_ADDR_W;
unsigned long flags;
nandaddr &= ~NAND_WRITE_ADDR;
nandaddr |= NAND_WRITE_CMD;
/* finish with a reset */
spin_lock_irqsave(&ebu_lock, flags);
writeb(NAND_WRITE_CMD_RESET, (void __iomem *) nandaddr);
while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
spin_unlock_irqrestore(&ebu_lock, flags);
}
static void xway_select_chip(struct mtd_info *mtd, int chip)
{
switch (chip) {
case -1:
ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON);
ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON);
break;
case 0:
ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON);
ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON);
break;
default:
BUG();
}
}
static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *this = mtd->priv;
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
unsigned long flags;
if (ctrl & NAND_CTRL_CHANGE) {
nandaddr &= ~(NAND_WRITE_CMD | NAND_WRITE_ADDR);
if (ctrl & NAND_CLE)
nandaddr |= NAND_WRITE_CMD;
else
nandaddr |= NAND_WRITE_ADDR;
this->IO_ADDR_W = (void __iomem *) nandaddr;
}
if (cmd != NAND_CMD_NONE) {
spin_lock_irqsave(&ebu_lock, flags);
writeb(cmd, this->IO_ADDR_W);
while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
spin_unlock_irqrestore(&ebu_lock, flags);
}
}
static int xway_dev_ready(struct mtd_info *mtd)
{
return ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD;
}
static unsigned char xway_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
unsigned long nandaddr = (unsigned long) this->IO_ADDR_R;
unsigned long flags;
int ret;
spin_lock_irqsave(&ebu_lock, flags);
ret = ltq_r8((void __iomem *)(nandaddr + NAND_READ_DATA));
spin_unlock_irqrestore(&ebu_lock, flags);
return ret;
}
static int xway_nand_probe(struct platform_device *pdev)
{
struct nand_chip *this = platform_get_drvdata(pdev);
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
const __be32 *cs = of_get_property(pdev->dev.of_node,
"lantiq,cs", NULL);
u32 cs_flag = 0;
/* load our CS from the DT. Either we find a valid 1 or default to 0 */
if (cs && (*cs == 1))
cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1;
/* setup the EBU to run in NAND mode on our base addr */
ltq_ebu_w32(CPHYSADDR(nandaddr)
| ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1);
ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2
| BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1
| BUSCON1_CMULT4, LTQ_EBU_BUSCON1);
ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P
| NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P
| cs_flag, EBU_NAND_CON);
/* finish with a reset */
xway_reset_chip(this);
return 0;
}
/* allow users to override the partition in DT using the cmdline */
static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL };
static struct platform_nand_data xway_nand_data = {
.chip = {
.nr_chips = 1,
.chip_delay = 30,
.part_probe_types = part_probes,
},
.ctrl = {
.probe = xway_nand_probe,
.cmd_ctrl = xway_cmd_ctrl,
.dev_ready = xway_dev_ready,
.select_chip = xway_select_chip,
.read_byte = xway_read_byte,
}
};
/*
* Try to find the node inside the DT. If it is available attach out
* platform_nand_data
*/
static int __init xway_register_nand(void)
{
struct device_node *node;
struct platform_device *pdev;
node = of_find_compatible_node(NULL, NULL, "lantiq,nand-xway");
if (!node)
return -ENOENT;
pdev = of_find_device_by_node(node);
if (!pdev)
return -EINVAL;
pdev->dev.platform_data = &xway_nand_data;
of_node_put(node);
return 0;
}
subsys_initcall(xway_register_nand);

Просмотреть файл

@ -346,7 +346,6 @@ static int sm_write_sector(struct sm_ftl *ftl,
ret = mtd_write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); ret = mtd_write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops);
/* Now we assume that hardware will catch write bitflip errors */ /* Now we assume that hardware will catch write bitflip errors */
/* If you are paranoid, use CONFIG_MTD_NAND_VERIFY_WRITE */
if (ret) { if (ret) {
dbg("write to block %d at zone %d, failed with error %d", dbg("write to block %d at zone %d, failed with error %d",

Просмотреть файл

@ -6,3 +6,4 @@ obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o
obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o
obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o

Просмотреть файл

@ -0,0 +1,460 @@
/*
* Copyright © 2012 NetCommWireless
* Iwo Mergler <Iwo.Mergler@netcommwireless.com.au>
*
* Test for multi-bit error recovery on a NAND page This mostly tests the
* ECC controller / driver.
*
* There are two test modes:
*
* 0 - artificially inserting bit errors until the ECC fails
* This is the default method and fairly quick. It should
* be independent of the quality of the FLASH.
*
* 1 - re-writing the same pattern repeatedly until the ECC fails.
* This method relies on the physics of NAND FLASH to eventually
* generate '0' bits if '1' has been written sufficient times.
* Depending on the NAND, the first bit errors will appear after
* 1000 or more writes and then will usually snowball, reaching the
* limits of the ECC quickly.
*
* The test stops after 10000 cycles, should your FLASH be
* exceptionally good and not generate bit errors before that. Try
* a different page in that case.
*
* Please note that neither of these tests will significantly 'use up' any
* FLASH endurance. Only a maximum of two erase operations will be performed.
*
*
* 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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mtd/mtd.h>
#include <linux/err.h>
#include <linux/mtd/nand.h>
#include <linux/slab.h>
#define msg(FMT, VA...) pr_info("mtd_nandbiterrs: "FMT, ##VA)
static int dev;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
static unsigned page_offset;
module_param(page_offset, uint, S_IRUGO);
MODULE_PARM_DESC(page_offset, "Page number relative to dev start");
static unsigned seed;
module_param(seed, uint, S_IRUGO);
MODULE_PARM_DESC(seed, "Random seed");
static int mode;
module_param(mode, int, S_IRUGO);
MODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test");
static unsigned max_overwrite = 10000;
static loff_t offset; /* Offset of the page we're using. */
static unsigned eraseblock; /* Eraseblock number for our page. */
/* We assume that the ECC can correct up to a certain number
* of biterrors per subpage. */
static unsigned subsize; /* Size of subpages */
static unsigned subcount; /* Number of subpages per page */
static struct mtd_info *mtd; /* MTD device */
static uint8_t *wbuffer; /* One page write / compare buffer */
static uint8_t *rbuffer; /* One page read buffer */
/* 'random' bytes from known offsets */
static uint8_t hash(unsigned offset)
{
unsigned v = offset;
unsigned char c;
v ^= 0x7f7edfd3;
v = v ^ (v >> 3);
v = v ^ (v >> 5);
v = v ^ (v >> 13);
c = v & 0xFF;
/* Reverse bits of result. */
c = (c & 0x0F) << 4 | (c & 0xF0) >> 4;
c = (c & 0x33) << 2 | (c & 0xCC) >> 2;
c = (c & 0x55) << 1 | (c & 0xAA) >> 1;
return c;
}
static int erase_block(void)
{
int err;
struct erase_info ei;
loff_t addr = eraseblock * mtd->erasesize;
msg("erase_block\n");
memset(&ei, 0, sizeof(struct erase_info));
ei.mtd = mtd;
ei.addr = addr;
ei.len = mtd->erasesize;
err = mtd_erase(mtd, &ei);
if (err || ei.state == MTD_ERASE_FAILED) {
msg("error %d while erasing\n", err);
if (!err)
err = -EIO;
return err;
}
return 0;
}
/* Writes wbuffer to page */
static int write_page(int log)
{
int err = 0;
size_t written;
if (log)
msg("write_page\n");
err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer);
if (err || written != mtd->writesize) {
msg("error: write failed at %#llx\n", (long long)offset);
if (!err)
err = -EIO;
}
return err;
}
/* Re-writes the data area while leaving the OOB alone. */
static int rewrite_page(int log)
{
int err = 0;
struct mtd_oob_ops ops;
if (log)
msg("rewrite page\n");
ops.mode = MTD_OPS_RAW; /* No ECC */
ops.len = mtd->writesize;
ops.retlen = 0;
ops.ooblen = 0;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = wbuffer;
ops.oobbuf = NULL;
err = mtd_write_oob(mtd, offset, &ops);
if (err || ops.retlen != mtd->writesize) {
msg("error: write_oob failed (%d)\n", err);
if (!err)
err = -EIO;
}
return err;
}
/* Reads page into rbuffer. Returns number of corrected bit errors (>=0)
* or error (<0) */
static int read_page(int log)
{
int err = 0;
size_t read;
struct mtd_ecc_stats oldstats;
if (log)
msg("read_page\n");
/* Saving last mtd stats */
memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats));
err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer);
if (err == -EUCLEAN)
err = mtd->ecc_stats.corrected - oldstats.corrected;
if (err < 0 || read != mtd->writesize) {
msg("error: read failed at %#llx\n", (long long)offset);
if (err >= 0)
err = -EIO;
}
return err;
}
/* Verifies rbuffer against random sequence */
static int verify_page(int log)
{
unsigned i, errs = 0;
if (log)
msg("verify_page\n");
for (i = 0; i < mtd->writesize; i++) {
if (rbuffer[i] != hash(i+seed)) {
msg("Error: page offset %u, expected %02x, got %02x\n",
i, hash(i+seed), rbuffer[i]);
errs++;
}
}
if (errs)
return -EIO;
else
return 0;
}
#define CBIT(v, n) ((v) & (1 << (n)))
#define BCLR(v, n) ((v) = (v) & ~(1 << (n)))
/* Finds the first '1' bit in wbuffer starting at offset 'byte'
* and sets it to '0'. */
static int insert_biterror(unsigned byte)
{
int bit;
while (byte < mtd->writesize) {
for (bit = 7; bit >= 0; bit--) {
if (CBIT(wbuffer[byte], bit)) {
BCLR(wbuffer[byte], bit);
msg("Inserted biterror @ %u/%u\n", byte, bit);
return 0;
}
}
byte++;
}
msg("biterror: Failed to find a '1' bit\n");
return -EIO;
}
/* Writes 'random' data to page and then introduces deliberate bit
* errors into the page, while verifying each step. */
static int incremental_errors_test(void)
{
int err = 0;
unsigned i;
unsigned errs_per_subpage = 0;
msg("incremental biterrors test\n");
for (i = 0; i < mtd->writesize; i++)
wbuffer[i] = hash(i+seed);
err = write_page(1);
if (err)
goto exit;
while (1) {
err = rewrite_page(1);
if (err)
goto exit;
err = read_page(1);
if (err > 0)
msg("Read reported %d corrected bit errors\n", err);
if (err < 0) {
msg("After %d biterrors per subpage, read reported error %d\n",
errs_per_subpage, err);
err = 0;
goto exit;
}
err = verify_page(1);
if (err) {
msg("ECC failure, read data is incorrect despite read success\n");
goto exit;
}
msg("Successfully corrected %d bit errors per subpage\n",
errs_per_subpage);
for (i = 0; i < subcount; i++) {
err = insert_biterror(i * subsize);
if (err < 0)
goto exit;
}
errs_per_subpage++;
}
exit:
return err;
}
/* Writes 'random' data to page and then re-writes that same data repeatedly.
This eventually develops bit errors (bits written as '1' will slowly become
'0'), which are corrected as far as the ECC is capable of. */
static int overwrite_test(void)
{
int err = 0;
unsigned i;
unsigned max_corrected = 0;
unsigned opno = 0;
/* We don't expect more than this many correctable bit errors per
* page. */
#define MAXBITS 512
static unsigned bitstats[MAXBITS]; /* bit error histogram. */
memset(bitstats, 0, sizeof(bitstats));
msg("overwrite biterrors test\n");
for (i = 0; i < mtd->writesize; i++)
wbuffer[i] = hash(i+seed);
err = write_page(1);
if (err)
goto exit;
while (opno < max_overwrite) {
err = rewrite_page(0);
if (err)
break;
err = read_page(0);
if (err >= 0) {
if (err >= MAXBITS) {
msg("Implausible number of bit errors corrected\n");
err = -EIO;
break;
}
bitstats[err]++;
if (err > max_corrected) {
max_corrected = err;
msg("Read reported %d corrected bit errors\n",
err);
}
} else { /* err < 0 */
msg("Read reported error %d\n", err);
err = 0;
break;
}
err = verify_page(0);
if (err) {
bitstats[max_corrected] = opno;
msg("ECC failure, read data is incorrect despite read success\n");
break;
}
opno++;
}
/* At this point bitstats[0] contains the number of ops with no bit
* errors, bitstats[1] the number of ops with 1 bit error, etc. */
msg("Bit error histogram (%d operations total):\n", opno);
for (i = 0; i < max_corrected; i++)
msg("Page reads with %3d corrected bit errors: %d\n",
i, bitstats[i]);
exit:
return err;
}
static int __init mtd_nandbiterrs_init(void)
{
int err = 0;
msg("\n");
msg("==================================================\n");
msg("MTD device: %d\n", dev);
mtd = get_mtd_device(NULL, dev);
if (IS_ERR(mtd)) {
err = PTR_ERR(mtd);
msg("error: cannot get MTD device\n");
goto exit_mtddev;
}
if (mtd->type != MTD_NANDFLASH) {
msg("this test requires NAND flash\n");
err = -ENODEV;
goto exit_nand;
}
msg("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n",
(unsigned long long)mtd->size, mtd->erasesize,
mtd->writesize, mtd->oobsize);
subsize = mtd->writesize >> mtd->subpage_sft;
subcount = mtd->writesize / subsize;
msg("Device uses %d subpages of %d bytes\n", subcount, subsize);
offset = page_offset * mtd->writesize;
eraseblock = mtd_div_by_eb(offset, mtd);
msg("Using page=%u, offset=%llu, eraseblock=%u\n",
page_offset, offset, eraseblock);
wbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
if (!wbuffer) {
err = -ENOMEM;
goto exit_wbuffer;
}
rbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
if (!rbuffer) {
err = -ENOMEM;
goto exit_rbuffer;
}
err = erase_block();
if (err)
goto exit_error;
if (mode == 0)
err = incremental_errors_test();
else
err = overwrite_test();
if (err)
goto exit_error;
/* We leave the block un-erased in case of test failure. */
err = erase_block();
if (err)
goto exit_error;
err = -EIO;
msg("finished successfully.\n");
msg("==================================================\n");
exit_error:
kfree(rbuffer);
exit_rbuffer:
kfree(wbuffer);
exit_wbuffer:
/* Nothing */
exit_nand:
put_mtd_device(mtd);
exit_mtddev:
return err;
}
static void __exit mtd_nandbiterrs_exit(void)
{
return;
}
module_init(mtd_nandbiterrs_init);
module_exit(mtd_nandbiterrs_exit);
MODULE_DESCRIPTION("NAND bit error recovery test");
MODULE_AUTHOR("Iwo Mergler");
MODULE_LICENSE("GPL");

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше