- factor out common code from MTD tests
- nand-gpio cleanup and portability to non-ARM - m25p80 support for 4-byte addressing chips, other new chips - pxa3xx cleanup and support for new platforms - remove obsolete alauda, octagon-5066 drivers - erase/write support for bcm47xxsflash - improve detection of ECC requirements for NAND, controller setup - NFC acceleration support for atmel-nand, read/write via SRAM - etc. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iEYEABECAAYFAlIt3VsACgkQdwG7hYl686NKBQCgqFzV1S+TiXZSjbqanK0374Hu lMIAoI2IOQb1bgQFSqaOzgSaBmVhLtMc =xWBt -----END PGP SIGNATURE----- Merge tag 'for-linus-20130909' of git://git.infradead.org/linux-mtd Pull mtd updates from David Woodhouse: - factor out common code from MTD tests - nand-gpio cleanup and portability to non-ARM - m25p80 support for 4-byte addressing chips, other new chips - pxa3xx cleanup and support for new platforms - remove obsolete alauda, octagon-5066 drivers - erase/write support for bcm47xxsflash - improve detection of ECC requirements for NAND, controller setup - NFC acceleration support for atmel-nand, read/write via SRAM - etc * tag 'for-linus-20130909' of git://git.infradead.org/linux-mtd: (184 commits) mtd: chips: Add support for PMC SPI Flash chips in m25p80.c mtd: ofpart: use for_each_child_of_node() macro mtd: mtdswap: replace strict_strtoul() with kstrtoul() mtd cs553x_nand: use kzalloc() instead of memset mtd: atmel_nand: fix error return code in atmel_nand_probe() mtd: bcm47xxsflash: writing support mtd: bcm47xxsflash: implement erasing support mtd: bcm47xxsflash: convert to module_platform_driver instead of init/exit mtd: bcm47xxsflash: convert kzalloc to avoid invalid access mtd: remove alauda driver mtd: nand: mxc_nand: mark 'const' properly mtd: maps: cfi_flagadm: add missing __iomem annotation mtd: spear_smi: add missing __iomem annotation mtd: r852: Staticize local symbols mtd: nandsim: Staticize local symbols mtd: impa7: add missing __iomem annotation mtd: sm_ftl: Staticize local symbols mtd: m25p80: add support for mr25h10 mtd: m25p80: make CONFIG_M25PXX_USE_FAST_READ safe to enable mtd: m25p80: Pass flags through CAT25_INFO macro ...
This commit is contained in:
commit
ef9a61bef9
|
@ -128,9 +128,8 @@ KernelVersion: 3.4
|
||||||
Contact: linux-mtd@lists.infradead.org
|
Contact: linux-mtd@lists.infradead.org
|
||||||
Description:
|
Description:
|
||||||
Maximum number of bit errors that the device is capable of
|
Maximum number of bit errors that the device is capable of
|
||||||
correcting within each region covering an ecc step. This will
|
correcting within each region covering an ECC step (see
|
||||||
always be a non-negative integer. Note that some devices will
|
ecc_step_size). This will always be a non-negative integer.
|
||||||
have multiple ecc steps within each writesize region.
|
|
||||||
|
|
||||||
In the case of devices lacking any ECC capability, it is 0.
|
In the case of devices lacking any ECC capability, it is 0.
|
||||||
|
|
||||||
|
@ -173,3 +172,15 @@ Description:
|
||||||
This is generally applicable only to NAND flash devices with ECC
|
This is generally applicable only to NAND flash devices with ECC
|
||||||
capability. It is ignored on devices lacking ECC capability;
|
capability. It is ignored on devices lacking ECC capability;
|
||||||
i.e., devices for which ecc_strength is zero.
|
i.e., devices for which ecc_strength is zero.
|
||||||
|
|
||||||
|
What: /sys/class/mtd/mtdX/ecc_step_size
|
||||||
|
Date: May 2013
|
||||||
|
KernelVersion: 3.10
|
||||||
|
Contact: linux-mtd@lists.infradead.org
|
||||||
|
Description:
|
||||||
|
The size of a single region covered by ECC, known as the ECC
|
||||||
|
step. Devices may have several equally sized ECC steps within
|
||||||
|
each writesize region.
|
||||||
|
|
||||||
|
It will always be a non-negative integer. In the case of
|
||||||
|
devices lacking any ECC capability, it is 0.
|
||||||
|
|
|
@ -1224,8 +1224,6 @@ in this page</entry>
|
||||||
#define NAND_BBT_CREATE 0x00000200
|
#define NAND_BBT_CREATE 0x00000200
|
||||||
/* Search good / bad pattern through all pages of a block */
|
/* Search good / bad pattern through all pages of a block */
|
||||||
#define NAND_BBT_SCANALLPAGES 0x00000400
|
#define NAND_BBT_SCANALLPAGES 0x00000400
|
||||||
/* Scan block empty during good / bad block scan */
|
|
||||||
#define NAND_BBT_SCANEMPTY 0x00000800
|
|
||||||
/* Write bbt if neccecary */
|
/* Write bbt if neccecary */
|
||||||
#define NAND_BBT_WRITE 0x00001000
|
#define NAND_BBT_WRITE 0x00001000
|
||||||
/* Read and write back block contents when writing bbt */
|
/* Read and write back block contents when writing bbt */
|
||||||
|
|
|
@ -15,6 +15,7 @@ Required properties:
|
||||||
optional gpio and may be set to 0 if not present.
|
optional gpio and may be set to 0 if not present.
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
- atmel,nand-has-dma : boolean to support dma transfer for nand read/write.
|
||||||
- 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".
|
||||||
|
@ -29,6 +30,14 @@ Optional properties:
|
||||||
sector size 1024.
|
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
|
||||||
|
- Nand Flash Controller(NFC) is a slave driver under Atmel nand flash
|
||||||
|
- Required properties:
|
||||||
|
- compatible : "atmel,sama5d3-nfc".
|
||||||
|
- reg : should specify the address and size used for NFC command registers,
|
||||||
|
NFC registers and NFC Sram. NFC Sram address and size can be absent
|
||||||
|
if don't want to use it.
|
||||||
|
- Optional properties:
|
||||||
|
- atmel,write-by-sram: boolean to enable NFC write by sram.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
nand0: nand@40000000,0 {
|
nand0: nand@40000000,0 {
|
||||||
|
@ -77,3 +86,22 @@ nand0: nand@40000000 {
|
||||||
...
|
...
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* for NFC supported chips */
|
||||||
|
nand0: nand@40000000 {
|
||||||
|
compatible = "atmel,at91rm9200-nand";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
ranges;
|
||||||
|
...
|
||||||
|
nfc@70000000 {
|
||||||
|
compatible = "atmel,sama5d3-nfc";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <
|
||||||
|
0x70000000 0x10000000 /* NFC Command Registers */
|
||||||
|
0xffffc000 0x00000070 /* NFC HSMC regs */
|
||||||
|
0x00200000 0x00100000 /* NFC SRAM banks */
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
* FSMC NAND
|
ST Microelectronics Flexible Static Memory Controller (FSMC)
|
||||||
|
NAND Interface
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "st,spear600-fsmc-nand", "stericsson,fsmc-nand"
|
- compatible : "st,spear600-fsmc-nand", "stericsson,fsmc-nand"
|
||||||
|
@ -9,6 +10,26 @@ Optional properties:
|
||||||
- bank-width : Width (in bytes) of the device. If not present, the width
|
- bank-width : Width (in bytes) of the device. If not present, the width
|
||||||
defaults to 1 byte
|
defaults to 1 byte
|
||||||
- nand-skip-bbtscan: Indicates the the BBT scanning should be skipped
|
- nand-skip-bbtscan: Indicates the the BBT scanning should be skipped
|
||||||
|
- timings: array of 6 bytes for NAND timings. The meanings of these bytes
|
||||||
|
are:
|
||||||
|
byte 0 TCLR : CLE to RE delay in number of AHB clock cycles, only 4 bits
|
||||||
|
are valid. Zero means one clockcycle, 15 means 16 clock
|
||||||
|
cycles.
|
||||||
|
byte 1 TAR : ALE to RE delay, 4 bits are valid. Same format as TCLR.
|
||||||
|
byte 2 THIZ : number of HCLK clock cycles during which the data bus is
|
||||||
|
kept in Hi-Z (tristate) after the start of a write access.
|
||||||
|
Only valid for write transactions. Zero means zero cycles,
|
||||||
|
255 means 255 cycles.
|
||||||
|
byte 3 THOLD : number of HCLK clock cycles to hold the address (and data
|
||||||
|
when writing) after the command deassertation. Zero means
|
||||||
|
one cycle, 255 means 256 cycles.
|
||||||
|
byte 4 TWAIT : number of HCLK clock cycles to assert the command to the
|
||||||
|
NAND flash in response to SMWAITn. Zero means 1 cycle,
|
||||||
|
255 means 256 cycles.
|
||||||
|
byte 5 TSET : number of HCLK clock cycles to assert the address before the
|
||||||
|
command is asserted. Zero means one cycle, 255 means 256
|
||||||
|
cycles.
|
||||||
|
- bank: default NAND bank to use (0-3 are valid, 0 is the default).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -24,6 +45,8 @@ Example:
|
||||||
|
|
||||||
bank-width = <1>;
|
bank-width = <1>;
|
||||||
nand-skip-bbtscan;
|
nand-skip-bbtscan;
|
||||||
|
timings = /bits/ 8 <0 0 0 2 3 0>;
|
||||||
|
bank = <1>;
|
||||||
|
|
||||||
partition@0 {
|
partition@0 {
|
||||||
...
|
...
|
||||||
|
|
|
@ -4,6 +4,7 @@ Partitions can be represented by sub-nodes of an mtd device. This can be used
|
||||||
on platforms which have strong conventions about which portions of a flash are
|
on platforms which have strong conventions about which portions of a flash are
|
||||||
used for what purposes, but which don't use an on-flash partition table such
|
used for what purposes, but which don't use an on-flash partition table such
|
||||||
as RedBoot.
|
as RedBoot.
|
||||||
|
NOTE: if the sub-node has a compatible string, then it is not a partition.
|
||||||
|
|
||||||
#address-cells & #size-cells must both be present in the mtd device. There are
|
#address-cells & #size-cells must both be present in the mtd device. There are
|
||||||
two valid values for both:
|
two valid values for both:
|
||||||
|
|
|
@ -1983,6 +1983,9 @@ at32_add_device_nand(unsigned int id, struct atmel_nand_data *data)
|
||||||
ARRAY_SIZE(smc_cs3_resource)))
|
ARRAY_SIZE(smc_cs3_resource)))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
/* For at32ap7000, we use the reset workaround for nand driver */
|
||||||
|
data->need_reset_workaround = true;
|
||||||
|
|
||||||
if (platform_device_add_data(pdev, data,
|
if (platform_device_add_data(pdev, data,
|
||||||
sizeof(struct atmel_nand_data)))
|
sizeof(struct atmel_nand_data)))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
|
@ -35,6 +35,8 @@ struct bcm963xx_nvram {
|
||||||
u32 checksum_high;
|
u32 checksum_high;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define BCM63XX_DEFAULT_PSI_SIZE 64
|
||||||
|
|
||||||
static struct bcm963xx_nvram nvram;
|
static struct bcm963xx_nvram nvram;
|
||||||
static int mac_addr_used;
|
static int mac_addr_used;
|
||||||
|
|
||||||
|
@ -114,3 +116,12 @@ int bcm63xx_nvram_get_mac_address(u8 *mac)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(bcm63xx_nvram_get_mac_address);
|
EXPORT_SYMBOL(bcm63xx_nvram_get_mac_address);
|
||||||
|
|
||||||
|
int bcm63xx_nvram_get_psi_size(void)
|
||||||
|
{
|
||||||
|
if (nvram.psi_size > 0)
|
||||||
|
return nvram.psi_size;
|
||||||
|
|
||||||
|
return BCM63XX_DEFAULT_PSI_SIZE;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(bcm63xx_nvram_get_psi_size);
|
||||||
|
|
|
@ -30,4 +30,6 @@ u8 *bcm63xx_nvram_get_name(void);
|
||||||
*/
|
*/
|
||||||
int bcm63xx_nvram_get_mac_address(u8 *mac);
|
int bcm63xx_nvram_get_mac_address(u8 *mac);
|
||||||
|
|
||||||
|
int bcm63xx_nvram_get_psi_size(void);
|
||||||
|
|
||||||
#endif /* BCM63XX_NVRAM_H */
|
#endif /* BCM63XX_NVRAM_H */
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
|
* Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
|
||||||
* Mike Albon <malbon@openwrt.org>
|
* Mike Albon <malbon@openwrt.org>
|
||||||
* Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
|
* Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
|
||||||
* Copyright © 2011-2012 Jonas Gorski <jonas.gorski@gmail.com>
|
* Copyright © 2011-2013 Jonas Gorski <jonas.gorski@gmail.com>
|
||||||
*
|
*
|
||||||
* 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 as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -27,17 +27,19 @@
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
|
#include <asm/mach-bcm63xx/bcm63xx_nvram.h>
|
||||||
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
|
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
|
||||||
#include <asm/mach-bcm63xx/board_bcm963xx.h>
|
#include <asm/mach-bcm63xx/board_bcm963xx.h>
|
||||||
|
|
||||||
#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
|
#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
|
||||||
|
|
||||||
#define BCM63XX_CFE_BLOCK_SIZE 0x10000 /* always at least 64KiB */
|
#define BCM63XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */
|
||||||
|
|
||||||
#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
|
#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
|
||||||
|
|
||||||
|
@ -90,7 +92,8 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
||||||
BCM63XX_CFE_BLOCK_SIZE);
|
BCM63XX_CFE_BLOCK_SIZE);
|
||||||
|
|
||||||
cfelen = cfe_erasesize;
|
cfelen = cfe_erasesize;
|
||||||
nvramlen = cfe_erasesize;
|
nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K;
|
||||||
|
nvramlen = roundup(nvramlen, cfe_erasesize);
|
||||||
|
|
||||||
/* Allocate memory for buffer */
|
/* Allocate memory for buffer */
|
||||||
buf = vmalloc(sizeof(struct bcm_tag));
|
buf = vmalloc(sizeof(struct bcm_tag));
|
||||||
|
|
|
@ -1571,8 +1571,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||||
xip_enable(map, chip, adr);
|
xip_enable(map, chip, adr);
|
||||||
/* FIXME - should have reset delay before continuing */
|
/* FIXME - should have reset delay before continuing */
|
||||||
|
|
||||||
printk(KERN_WARNING "MTD %s(): software timeout\n",
|
printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n",
|
||||||
__func__ );
|
__func__, adr);
|
||||||
|
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
op_done:
|
op_done:
|
||||||
|
|
|
@ -211,9 +211,7 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map,
|
||||||
|
|
||||||
probe_function = __symbol_get(probename);
|
probe_function = __symbol_get(probename);
|
||||||
if (!probe_function) {
|
if (!probe_function) {
|
||||||
char modname[sizeof("cfi_cmdset_%4.4X")];
|
request_module("cfi_cmdset_%4.4X", type);
|
||||||
sprintf(modname, "cfi_cmdset_%4.4X", type);
|
|
||||||
request_module(modname);
|
|
||||||
probe_function = __symbol_get(probename);
|
probe_function = __symbol_get(probename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
#define PM49FL008 0x006A
|
#define PM49FL008 0x006A
|
||||||
|
|
||||||
/* Sharp */
|
/* Sharp */
|
||||||
#define LH28F640BF 0x00b0
|
#define LH28F640BF 0x00B0
|
||||||
|
|
||||||
/* ST - www.st.com */
|
/* ST - www.st.com */
|
||||||
#define M29F800AB 0x0058
|
#define M29F800AB 0x0058
|
||||||
|
@ -1299,13 +1299,14 @@ static const struct amd_flash_info jedec_table[] = {
|
||||||
.mfr_id = CFI_MFR_SHARP,
|
.mfr_id = CFI_MFR_SHARP,
|
||||||
.dev_id = LH28F640BF,
|
.dev_id = LH28F640BF,
|
||||||
.name = "LH28F640BF",
|
.name = "LH28F640BF",
|
||||||
.devtypes = CFI_DEVICETYPE_X8,
|
.devtypes = CFI_DEVICETYPE_X16,
|
||||||
.uaddr = MTD_UADDR_UNNECESSARY,
|
.uaddr = MTD_UADDR_UNNECESSARY,
|
||||||
.dev_size = SIZE_4MiB,
|
.dev_size = SIZE_8MiB,
|
||||||
.cmd_set = P_ID_INTEL_STD,
|
.cmd_set = P_ID_INTEL_EXT,
|
||||||
.nr_regions = 1,
|
.nr_regions = 2,
|
||||||
.regions = {
|
.regions = {
|
||||||
ERASEINFO(0x40000,16),
|
ERASEINFO(0x10000, 127),
|
||||||
|
ERASEINFO(0x02000, 8),
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
.mfr_id = CFI_MFR_SST,
|
.mfr_id = CFI_MFR_SST,
|
||||||
|
|
|
@ -224,59 +224,4 @@ config BCH_CONST_T
|
||||||
default 4
|
default 4
|
||||||
endif
|
endif
|
||||||
|
|
||||||
config MTD_DOCPROBE
|
|
||||||
tristate
|
|
||||||
select MTD_DOCECC
|
|
||||||
|
|
||||||
config MTD_DOCECC
|
|
||||||
tristate
|
|
||||||
|
|
||||||
config MTD_DOCPROBE_ADVANCED
|
|
||||||
bool "Advanced detection options for DiskOnChip"
|
|
||||||
depends on MTD_DOCPROBE
|
|
||||||
help
|
|
||||||
This option allows you to specify nonstandard address at which to
|
|
||||||
probe for a DiskOnChip, or to change the detection options. You
|
|
||||||
are unlikely to need any of this unless you are using LinuxBIOS.
|
|
||||||
Say 'N'.
|
|
||||||
|
|
||||||
config MTD_DOCPROBE_ADDRESS
|
|
||||||
hex "Physical address of DiskOnChip" if MTD_DOCPROBE_ADVANCED
|
|
||||||
depends on MTD_DOCPROBE
|
|
||||||
default "0x0"
|
|
||||||
---help---
|
|
||||||
By default, the probe for DiskOnChip devices will look for a
|
|
||||||
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
|
|
||||||
This option allows you to specify a single address at which to probe
|
|
||||||
for the device, which is useful if you have other devices in that
|
|
||||||
range which get upset when they are probed.
|
|
||||||
|
|
||||||
(Note that on PowerPC, the normal probe will only check at
|
|
||||||
0xE4000000.)
|
|
||||||
|
|
||||||
Normally, you should leave this set to zero, to allow the probe at
|
|
||||||
the normal addresses.
|
|
||||||
|
|
||||||
config MTD_DOCPROBE_HIGH
|
|
||||||
bool "Probe high addresses"
|
|
||||||
depends on MTD_DOCPROBE_ADVANCED
|
|
||||||
help
|
|
||||||
By default, the probe for DiskOnChip devices will look for a
|
|
||||||
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
|
|
||||||
This option changes to make it probe between 0xFFFC8000 and
|
|
||||||
0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
|
|
||||||
useful to you. Say 'N'.
|
|
||||||
|
|
||||||
config MTD_DOCPROBE_55AA
|
|
||||||
bool "Probe for 0x55 0xAA BIOS Extension Signature"
|
|
||||||
depends on MTD_DOCPROBE_ADVANCED
|
|
||||||
help
|
|
||||||
Check for the 0x55 0xAA signature of a DiskOnChip, and do not
|
|
||||||
continue with probing if it is absent. The signature will always be
|
|
||||||
present for a DiskOnChip 2000 or a normal DiskOnChip Millennium.
|
|
||||||
Only if you have overwritten the first block of a DiskOnChip
|
|
||||||
Millennium will it be absent. Enable this option if you are using
|
|
||||||
LinuxBIOS or if you need to recover a DiskOnChip Millennium on which
|
|
||||||
you have managed to wipe the first block.
|
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/bcma/bcma.h>
|
#include <linux/bcma/bcma.h>
|
||||||
|
@ -12,6 +13,93 @@ MODULE_DESCRIPTION("Serial flash driver for BCMA bus");
|
||||||
|
|
||||||
static const char * const probes[] = { "bcm47xxpart", NULL };
|
static const char * const probes[] = { "bcm47xxpart", NULL };
|
||||||
|
|
||||||
|
/**************************************************
|
||||||
|
* Various helpers
|
||||||
|
**************************************************/
|
||||||
|
|
||||||
|
static void bcm47xxsflash_cmd(struct bcm47xxsflash *b47s, u32 opcode)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode);
|
||||||
|
for (i = 0; i < 1000; i++) {
|
||||||
|
if (!(b47s->cc_read(b47s, BCMA_CC_FLASHCTL) &
|
||||||
|
BCMA_CC_FLASHCTL_BUSY))
|
||||||
|
return;
|
||||||
|
cpu_relax();
|
||||||
|
}
|
||||||
|
pr_err("Control command failed (timeout)!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout)
|
||||||
|
{
|
||||||
|
unsigned long deadline = jiffies + timeout;
|
||||||
|
|
||||||
|
do {
|
||||||
|
switch (b47s->type) {
|
||||||
|
case BCM47XXSFLASH_TYPE_ST:
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_RDSR);
|
||||||
|
if (!(b47s->cc_read(b47s, BCMA_CC_FLASHDATA) &
|
||||||
|
SR_ST_WIP))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case BCM47XXSFLASH_TYPE_ATMEL:
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_AT_STATUS);
|
||||||
|
if (b47s->cc_read(b47s, BCMA_CC_FLASHDATA) &
|
||||||
|
SR_AT_READY)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_relax();
|
||||||
|
udelay(1);
|
||||||
|
} while (!time_after_eq(jiffies, deadline));
|
||||||
|
|
||||||
|
pr_err("Timeout waiting for flash to be ready!\n");
|
||||||
|
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************
|
||||||
|
* MTD ops
|
||||||
|
**************************************************/
|
||||||
|
|
||||||
|
static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
|
||||||
|
{
|
||||||
|
struct bcm47xxsflash *b47s = mtd->priv;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (b47s->type) {
|
||||||
|
case BCM47XXSFLASH_TYPE_ST:
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN);
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr);
|
||||||
|
/* Newer flashes have "sub-sectors" which can be erased
|
||||||
|
* independently with a new command: ST_SSE. The ST_SE command
|
||||||
|
* erases 64KB just as before.
|
||||||
|
*/
|
||||||
|
if (b47s->blocksize < (64 * 1024))
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_SSE);
|
||||||
|
else
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_SE);
|
||||||
|
break;
|
||||||
|
case BCM47XXSFLASH_TYPE_ATMEL:
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr << 1);
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_AT_PAGE_ERASE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bcm47xxsflash_poll(b47s, HZ);
|
||||||
|
if (err)
|
||||||
|
erase->state = MTD_ERASE_FAILED;
|
||||||
|
else
|
||||||
|
erase->state = MTD_ERASE_DONE;
|
||||||
|
|
||||||
|
if (erase->callback)
|
||||||
|
erase->callback(erase);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
size_t *retlen, u_char *buf)
|
size_t *retlen, u_char *buf)
|
||||||
{
|
{
|
||||||
|
@ -28,6 +116,127 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len,
|
||||||
|
const u_char *buf)
|
||||||
|
{
|
||||||
|
struct bcm47xxsflash *b47s = mtd->priv;
|
||||||
|
int written = 0;
|
||||||
|
|
||||||
|
/* Enable writes */
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN);
|
||||||
|
|
||||||
|
/* Write first byte */
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHADDR, offset);
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++);
|
||||||
|
|
||||||
|
/* Program page */
|
||||||
|
if (b47s->bcma_cc->core->id.rev < 20) {
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_PP);
|
||||||
|
return 1; /* 1B written */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Program page and set CSA (on newer chips we can continue writing) */
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | OPCODE_ST_PP);
|
||||||
|
offset++;
|
||||||
|
len--;
|
||||||
|
written++;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
/* Page boundary, another function call is needed */
|
||||||
|
if ((offset & 0xFF) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | *buf++);
|
||||||
|
offset++;
|
||||||
|
len--;
|
||||||
|
written++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All done, drop CSA & poll */
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHCTL, 0);
|
||||||
|
udelay(1);
|
||||||
|
if (bcm47xxsflash_poll(b47s, HZ / 10))
|
||||||
|
pr_err("Flash rejected dropping CSA\n");
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm47xxsflash_write_at(struct mtd_info *mtd, u32 offset, size_t len,
|
||||||
|
const u_char *buf)
|
||||||
|
{
|
||||||
|
struct bcm47xxsflash *b47s = mtd->priv;
|
||||||
|
u32 mask = b47s->blocksize - 1;
|
||||||
|
u32 page = (offset & ~mask) << 1;
|
||||||
|
u32 byte = offset & mask;
|
||||||
|
int written = 0;
|
||||||
|
|
||||||
|
/* If we don't overwrite whole page, read it to the buffer first */
|
||||||
|
if (byte || (len < b47s->blocksize)) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page);
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_LOAD);
|
||||||
|
/* 250 us for AT45DB321B */
|
||||||
|
err = bcm47xxsflash_poll(b47s, HZ / 1000);
|
||||||
|
if (err) {
|
||||||
|
pr_err("Timeout reading page 0x%X info buffer\n", page);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change buffer content with our data */
|
||||||
|
while (len > 0) {
|
||||||
|
/* Page boundary, another function call is needed */
|
||||||
|
if (byte == b47s->blocksize)
|
||||||
|
break;
|
||||||
|
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHADDR, byte++);
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++);
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_WRITE);
|
||||||
|
len--;
|
||||||
|
written++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Program page with the buffer content */
|
||||||
|
b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page);
|
||||||
|
bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_PROGRAM);
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
|
size_t *retlen, const u_char *buf)
|
||||||
|
{
|
||||||
|
struct bcm47xxsflash *b47s = mtd->priv;
|
||||||
|
int written;
|
||||||
|
|
||||||
|
/* Writing functions can return without writing all passed data, for
|
||||||
|
* example when the hardware is too old or when we git page boundary.
|
||||||
|
*/
|
||||||
|
while (len > 0) {
|
||||||
|
switch (b47s->type) {
|
||||||
|
case BCM47XXSFLASH_TYPE_ST:
|
||||||
|
written = bcm47xxsflash_write_st(mtd, to, len, buf);
|
||||||
|
break;
|
||||||
|
case BCM47XXSFLASH_TYPE_ATMEL:
|
||||||
|
written = bcm47xxsflash_write_at(mtd, to, len, buf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BUG_ON(1);
|
||||||
|
}
|
||||||
|
if (written < 0) {
|
||||||
|
pr_err("Error writing at offset 0x%llX\n", to);
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
to += (loff_t)written;
|
||||||
|
len -= written;
|
||||||
|
*retlen += written;
|
||||||
|
buf += written;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s)
|
static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = &b47s->mtd;
|
struct mtd_info *mtd = &b47s->mtd;
|
||||||
|
@ -35,33 +244,48 @@ static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s)
|
||||||
mtd->priv = b47s;
|
mtd->priv = b47s;
|
||||||
mtd->name = "bcm47xxsflash";
|
mtd->name = "bcm47xxsflash";
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->owner = THIS_MODULE;
|
||||||
mtd->type = MTD_ROM;
|
|
||||||
mtd->size = b47s->size;
|
|
||||||
mtd->_read = bcm47xxsflash_read;
|
|
||||||
|
|
||||||
/* TODO: implement writing support and verify/change following code */
|
mtd->type = MTD_NORFLASH;
|
||||||
mtd->flags = MTD_CAP_ROM;
|
mtd->flags = MTD_CAP_NORFLASH;
|
||||||
mtd->writebufsize = mtd->writesize = 1;
|
mtd->size = b47s->size;
|
||||||
|
mtd->erasesize = b47s->blocksize;
|
||||||
|
mtd->writesize = 1;
|
||||||
|
mtd->writebufsize = 1;
|
||||||
|
|
||||||
|
mtd->_erase = bcm47xxsflash_erase;
|
||||||
|
mtd->_read = bcm47xxsflash_read;
|
||||||
|
mtd->_write = bcm47xxsflash_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************
|
/**************************************************
|
||||||
* BCMA
|
* BCMA
|
||||||
**************************************************/
|
**************************************************/
|
||||||
|
|
||||||
|
static int bcm47xxsflash_bcma_cc_read(struct bcm47xxsflash *b47s, u16 offset)
|
||||||
|
{
|
||||||
|
return bcma_cc_read32(b47s->bcma_cc, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset,
|
||||||
|
u32 value)
|
||||||
|
{
|
||||||
|
bcma_cc_write32(b47s->bcma_cc, offset, value);
|
||||||
|
}
|
||||||
|
|
||||||
static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
|
static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
|
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
|
||||||
struct bcm47xxsflash *b47s;
|
struct bcm47xxsflash *b47s;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
b47s = kzalloc(sizeof(*b47s), GFP_KERNEL);
|
b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL);
|
||||||
if (!b47s) {
|
if (!b47s)
|
||||||
err = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
sflash->priv = b47s;
|
sflash->priv = b47s;
|
||||||
|
|
||||||
b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash);
|
b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash);
|
||||||
|
b47s->cc_read = bcm47xxsflash_bcma_cc_read;
|
||||||
|
b47s->cc_write = bcm47xxsflash_bcma_cc_write;
|
||||||
|
|
||||||
switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) {
|
switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) {
|
||||||
case BCMA_CC_FLASHT_STSER:
|
case BCMA_CC_FLASHT_STSER:
|
||||||
|
@ -81,15 +305,13 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
|
||||||
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
|
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("Failed to register MTD device: %d\n", err);
|
pr_err("Failed to register MTD device: %d\n", err);
|
||||||
goto err_dev_reg;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (bcm47xxsflash_poll(b47s, HZ / 10))
|
||||||
|
pr_warn("Serial flash busy\n");
|
||||||
|
|
||||||
err_dev_reg:
|
return 0;
|
||||||
kfree(&b47s->mtd);
|
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
|
static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
|
||||||
|
@ -98,7 +320,6 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
|
||||||
struct bcm47xxsflash *b47s = sflash->priv;
|
struct bcm47xxsflash *b47s = sflash->priv;
|
||||||
|
|
||||||
mtd_device_unregister(&b47s->mtd);
|
mtd_device_unregister(&b47s->mtd);
|
||||||
kfree(b47s);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -116,22 +337,4 @@ static struct platform_driver bcma_sflash_driver = {
|
||||||
* Init
|
* Init
|
||||||
**************************************************/
|
**************************************************/
|
||||||
|
|
||||||
static int __init bcm47xxsflash_init(void)
|
module_platform_driver(bcma_sflash_driver);
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = platform_driver_register(&bcma_sflash_driver);
|
|
||||||
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);
|
|
||||||
|
|
|
@ -60,6 +60,8 @@ enum bcm47xxsflash_type {
|
||||||
|
|
||||||
struct bcm47xxsflash {
|
struct bcm47xxsflash {
|
||||||
struct bcma_drv_cc *bcma_cc;
|
struct bcma_drv_cc *bcma_cc;
|
||||||
|
int (*cc_read)(struct bcm47xxsflash *b47s, u16 offset);
|
||||||
|
void (*cc_write)(struct bcm47xxsflash *b47s, u16 offset, u32 value);
|
||||||
|
|
||||||
enum bcm47xxsflash_type type;
|
enum bcm47xxsflash_type type;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
*
|
*
|
||||||
* Licence: GPL
|
* Licence: GPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
|
@ -18,10 +21,6 @@
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args)
|
|
||||||
#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args)
|
|
||||||
|
|
||||||
|
|
||||||
/* Info for the block device */
|
/* Info for the block device */
|
||||||
struct block2mtd_dev {
|
struct block2mtd_dev {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
@ -84,7 +83,7 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
err = _block2mtd_erase(dev, from, len);
|
err = _block2mtd_erase(dev, from, len);
|
||||||
mutex_unlock(&dev->write_mutex);
|
mutex_unlock(&dev->write_mutex);
|
||||||
if (err) {
|
if (err) {
|
||||||
ERROR("erase failed err = %d", err);
|
pr_err("erase failed err = %d\n", err);
|
||||||
instr->state = MTD_ERASE_FAILED;
|
instr->state = MTD_ERASE_FAILED;
|
||||||
} else
|
} else
|
||||||
instr->state = MTD_ERASE_DONE;
|
instr->state = MTD_ERASE_DONE;
|
||||||
|
@ -239,13 +238,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (IS_ERR(bdev)) {
|
if (IS_ERR(bdev)) {
|
||||||
ERROR("error: cannot open device %s", devname);
|
pr_err("error: cannot open device %s\n", devname);
|
||||||
goto devinit_err;
|
goto devinit_err;
|
||||||
}
|
}
|
||||||
dev->blkdev = bdev;
|
dev->blkdev = bdev;
|
||||||
|
|
||||||
if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
|
if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
|
||||||
ERROR("attempting to use an MTD device as a block device");
|
pr_err("attempting to use an MTD device as a block device\n");
|
||||||
goto devinit_err;
|
goto devinit_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +276,8 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
||||||
goto devinit_err;
|
goto devinit_err;
|
||||||
}
|
}
|
||||||
list_add(&dev->list, &blkmtd_device_list);
|
list_add(&dev->list, &blkmtd_device_list);
|
||||||
INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index,
|
pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
|
||||||
|
dev->mtd.index,
|
||||||
dev->mtd.name + strlen("block2mtd: "),
|
dev->mtd.name + strlen("block2mtd: "),
|
||||||
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
|
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
|
||||||
return dev;
|
return dev;
|
||||||
|
@ -339,17 +339,11 @@ static inline void kill_final_newline(char *str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define parse_err(fmt, args...) do { \
|
|
||||||
ERROR(fmt, ## args); \
|
|
||||||
return 0; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#ifndef MODULE
|
#ifndef MODULE
|
||||||
static int block2mtd_init_called = 0;
|
static int block2mtd_init_called = 0;
|
||||||
static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
|
static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static int block2mtd_setup2(const char *val)
|
static int block2mtd_setup2(const char *val)
|
||||||
{
|
{
|
||||||
char buf[80 + 12]; /* 80 for device, 12 for erase size */
|
char buf[80 + 12]; /* 80 for device, 12 for erase size */
|
||||||
|
@ -359,8 +353,10 @@ static int block2mtd_setup2(const char *val)
|
||||||
size_t erase_size = PAGE_SIZE;
|
size_t erase_size = PAGE_SIZE;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
if (strnlen(val, sizeof(buf)) >= sizeof(buf))
|
if (strnlen(val, sizeof(buf)) >= sizeof(buf)) {
|
||||||
parse_err("parameter too long");
|
pr_err("parameter too long\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
strcpy(str, val);
|
strcpy(str, val);
|
||||||
kill_final_newline(str);
|
kill_final_newline(str);
|
||||||
|
@ -368,20 +364,27 @@ static int block2mtd_setup2(const char *val)
|
||||||
for (i = 0; i < 2; i++)
|
for (i = 0; i < 2; i++)
|
||||||
token[i] = strsep(&str, ",");
|
token[i] = strsep(&str, ",");
|
||||||
|
|
||||||
if (str)
|
if (str) {
|
||||||
parse_err("too many arguments");
|
pr_err("too many arguments\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!token[0])
|
if (!token[0]) {
|
||||||
parse_err("no argument");
|
pr_err("no argument\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
name = token[0];
|
name = token[0];
|
||||||
if (strlen(name) + 1 > 80)
|
if (strlen(name) + 1 > 80) {
|
||||||
parse_err("device name too long");
|
pr_err("device name too long\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (token[1]) {
|
if (token[1]) {
|
||||||
ret = parse_num(&erase_size, token[1]);
|
ret = parse_num(&erase_size, token[1]);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
parse_err("illegal erase size");
|
pr_err("illegal erase size\n");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +447,8 @@ static void block2mtd_exit(void)
|
||||||
struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
|
struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
|
||||||
block2mtd_sync(&dev->mtd);
|
block2mtd_sync(&dev->mtd);
|
||||||
mtd_device_unregister(&dev->mtd);
|
mtd_device_unregister(&dev->mtd);
|
||||||
INFO("mtd%d: [%s] removed", dev->mtd.index,
|
pr_info("mtd%d: [%s] removed\n",
|
||||||
|
dev->mtd.index,
|
||||||
dev->mtd.name + strlen("block2mtd: "));
|
dev->mtd.name + strlen("block2mtd: "));
|
||||||
list_del(&dev->list);
|
list_del(&dev->list);
|
||||||
block2mtd_free_device(dev);
|
block2mtd_free_device(dev);
|
||||||
|
|
|
@ -20,14 +20,21 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/platform_data/elm.h>
|
#include <linux/platform_data/elm.h>
|
||||||
|
|
||||||
|
#define ELM_SYSCONFIG 0x010
|
||||||
#define ELM_IRQSTATUS 0x018
|
#define ELM_IRQSTATUS 0x018
|
||||||
#define ELM_IRQENABLE 0x01c
|
#define ELM_IRQENABLE 0x01c
|
||||||
#define ELM_LOCATION_CONFIG 0x020
|
#define ELM_LOCATION_CONFIG 0x020
|
||||||
#define ELM_PAGE_CTRL 0x080
|
#define ELM_PAGE_CTRL 0x080
|
||||||
#define ELM_SYNDROME_FRAGMENT_0 0x400
|
#define ELM_SYNDROME_FRAGMENT_0 0x400
|
||||||
|
#define ELM_SYNDROME_FRAGMENT_1 0x404
|
||||||
|
#define ELM_SYNDROME_FRAGMENT_2 0x408
|
||||||
|
#define ELM_SYNDROME_FRAGMENT_3 0x40c
|
||||||
|
#define ELM_SYNDROME_FRAGMENT_4 0x410
|
||||||
|
#define ELM_SYNDROME_FRAGMENT_5 0x414
|
||||||
#define ELM_SYNDROME_FRAGMENT_6 0x418
|
#define ELM_SYNDROME_FRAGMENT_6 0x418
|
||||||
#define ELM_LOCATION_STATUS 0x800
|
#define ELM_LOCATION_STATUS 0x800
|
||||||
#define ELM_ERROR_LOCATION_0 0x880
|
#define ELM_ERROR_LOCATION_0 0x880
|
||||||
|
@ -56,12 +63,27 @@
|
||||||
#define SYNDROME_FRAGMENT_REG_SIZE 0x40
|
#define SYNDROME_FRAGMENT_REG_SIZE 0x40
|
||||||
#define ERROR_LOCATION_SIZE 0x100
|
#define ERROR_LOCATION_SIZE 0x100
|
||||||
|
|
||||||
|
struct elm_registers {
|
||||||
|
u32 elm_irqenable;
|
||||||
|
u32 elm_sysconfig;
|
||||||
|
u32 elm_location_config;
|
||||||
|
u32 elm_page_ctrl;
|
||||||
|
u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX];
|
||||||
|
u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX];
|
||||||
|
u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX];
|
||||||
|
u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX];
|
||||||
|
u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX];
|
||||||
|
u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX];
|
||||||
|
u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
struct elm_info {
|
struct elm_info {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *elm_base;
|
void __iomem *elm_base;
|
||||||
struct completion elm_completion;
|
struct completion elm_completion;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
enum bch_ecc bch_type;
|
enum bch_ecc bch_type;
|
||||||
|
struct elm_registers elm_regs;
|
||||||
};
|
};
|
||||||
|
|
||||||
static LIST_HEAD(elm_devices);
|
static LIST_HEAD(elm_devices);
|
||||||
|
@ -346,14 +368,9 @@ static int elm_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res) {
|
info->elm_base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
dev_err(&pdev->dev, "no memory resource defined\n");
|
if (IS_ERR(info->elm_base))
|
||||||
return -ENODEV;
|
return PTR_ERR(info->elm_base);
|
||||||
}
|
|
||||||
|
|
||||||
info->elm_base = devm_request_and_ioremap(&pdev->dev, res);
|
|
||||||
if (!info->elm_base)
|
|
||||||
return -EADDRNOTAVAIL;
|
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
|
ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
|
||||||
pdev->name, info);
|
pdev->name, info);
|
||||||
|
@ -381,10 +398,103 @@ static int elm_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elm_context_save
|
||||||
|
* saves ELM configurations to preserve them across Hardware powered-down
|
||||||
|
*/
|
||||||
|
static int elm_context_save(struct elm_info *info)
|
||||||
|
{
|
||||||
|
struct elm_registers *regs = &info->elm_regs;
|
||||||
|
enum bch_ecc bch_type = info->bch_type;
|
||||||
|
u32 offset = 0, i;
|
||||||
|
|
||||||
|
regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE);
|
||||||
|
regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG);
|
||||||
|
regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG);
|
||||||
|
regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL);
|
||||||
|
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||||
|
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
||||||
|
switch (bch_type) {
|
||||||
|
case BCH8_ECC:
|
||||||
|
regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
|
||||||
|
ELM_SYNDROME_FRAGMENT_3 + offset);
|
||||||
|
regs->elm_syndrome_fragment_2[i] = elm_read_reg(info,
|
||||||
|
ELM_SYNDROME_FRAGMENT_2 + offset);
|
||||||
|
case BCH4_ECC:
|
||||||
|
regs->elm_syndrome_fragment_1[i] = elm_read_reg(info,
|
||||||
|
ELM_SYNDROME_FRAGMENT_1 + offset);
|
||||||
|
regs->elm_syndrome_fragment_0[i] = elm_read_reg(info,
|
||||||
|
ELM_SYNDROME_FRAGMENT_0 + offset);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
|
||||||
|
* to be saved for all BCH schemes*/
|
||||||
|
regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
|
||||||
|
ELM_SYNDROME_FRAGMENT_6 + offset);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elm_context_restore
|
||||||
|
* writes configurations saved duing power-down back into ELM registers
|
||||||
|
*/
|
||||||
|
static int elm_context_restore(struct elm_info *info)
|
||||||
|
{
|
||||||
|
struct elm_registers *regs = &info->elm_regs;
|
||||||
|
enum bch_ecc bch_type = info->bch_type;
|
||||||
|
u32 offset = 0, i;
|
||||||
|
|
||||||
|
elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable);
|
||||||
|
elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig);
|
||||||
|
elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config);
|
||||||
|
elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl);
|
||||||
|
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||||
|
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
||||||
|
switch (bch_type) {
|
||||||
|
case BCH8_ECC:
|
||||||
|
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
|
||||||
|
regs->elm_syndrome_fragment_3[i]);
|
||||||
|
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset,
|
||||||
|
regs->elm_syndrome_fragment_2[i]);
|
||||||
|
case BCH4_ECC:
|
||||||
|
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset,
|
||||||
|
regs->elm_syndrome_fragment_1[i]);
|
||||||
|
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset,
|
||||||
|
regs->elm_syndrome_fragment_0[i]);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
|
||||||
|
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
|
||||||
|
regs->elm_syndrome_fragment_6[i] &
|
||||||
|
ELM_SYNDROME_VALID);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct elm_info *info = dev_get_drvdata(dev);
|
||||||
|
elm_context_save(info);
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct elm_info *info = dev_get_drvdata(dev);
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
|
elm_context_restore(info);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id elm_of_match[] = {
|
static const struct of_device_id elm_of_match[] = {
|
||||||
{ .compatible = "ti,am3352-elm" },
|
{ .compatible = "ti,am3352-elm" },
|
||||||
|
@ -398,6 +508,7 @@ static struct platform_driver elm_driver = {
|
||||||
.name = "elm",
|
.name = "elm",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = of_match_ptr(elm_of_match),
|
.of_match_table = of_match_ptr(elm_of_match),
|
||||||
|
.pm = &elm_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = elm_probe,
|
.probe = elm_probe,
|
||||||
.remove = elm_remove,
|
.remove = elm_remove,
|
||||||
|
|
|
@ -43,17 +43,24 @@
|
||||||
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
|
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
|
||||||
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
|
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
|
||||||
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
|
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
|
||||||
|
#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
|
||||||
#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
|
#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
|
||||||
#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */
|
#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */
|
||||||
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
|
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
|
||||||
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
|
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
|
||||||
|
|
||||||
|
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
||||||
|
#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */
|
||||||
|
#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */
|
||||||
|
#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */
|
||||||
|
#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */
|
||||||
|
|
||||||
/* Used for SST flashes only. */
|
/* Used for SST flashes only. */
|
||||||
#define OPCODE_BP 0x02 /* Byte program */
|
#define OPCODE_BP 0x02 /* Byte program */
|
||||||
#define OPCODE_WRDI 0x04 /* Write disable */
|
#define OPCODE_WRDI 0x04 /* Write disable */
|
||||||
#define OPCODE_AAI_WP 0xad /* Auto address increment word program */
|
#define OPCODE_AAI_WP 0xad /* Auto address increment word program */
|
||||||
|
|
||||||
/* Used for Macronix flashes only. */
|
/* Used for Macronix and Winbond flashes. */
|
||||||
#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */
|
#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */
|
||||||
#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */
|
#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */
|
||||||
|
|
||||||
|
@ -84,6 +91,8 @@ struct m25p {
|
||||||
u16 page_size;
|
u16 page_size;
|
||||||
u16 addr_width;
|
u16 addr_width;
|
||||||
u8 erase_opcode;
|
u8 erase_opcode;
|
||||||
|
u8 read_opcode;
|
||||||
|
u8 program_opcode;
|
||||||
u8 *command;
|
u8 *command;
|
||||||
bool fast_read;
|
bool fast_read;
|
||||||
};
|
};
|
||||||
|
@ -161,6 +170,7 @@ static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
|
||||||
{
|
{
|
||||||
switch (JEDEC_MFR(jedec_id)) {
|
switch (JEDEC_MFR(jedec_id)) {
|
||||||
case CFI_MFR_MACRONIX:
|
case CFI_MFR_MACRONIX:
|
||||||
|
case CFI_MFR_ST: /* Micron, actually */
|
||||||
case 0xEF /* winbond */:
|
case 0xEF /* winbond */:
|
||||||
flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
|
flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
|
||||||
return spi_write(flash->spi, flash->command, 1);
|
return spi_write(flash->spi, flash->command, 1);
|
||||||
|
@ -371,7 +381,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Set up the write data buffer. */
|
/* Set up the write data buffer. */
|
||||||
opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ;
|
opcode = flash->read_opcode;
|
||||||
flash->command[0] = opcode;
|
flash->command[0] = opcode;
|
||||||
m25p_addr2cmd(flash, from, flash->command);
|
m25p_addr2cmd(flash, from, flash->command);
|
||||||
|
|
||||||
|
@ -422,7 +432,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
write_enable(flash);
|
write_enable(flash);
|
||||||
|
|
||||||
/* Set up the opcode in the write buffer. */
|
/* Set up the opcode in the write buffer. */
|
||||||
flash->command[0] = OPCODE_PP;
|
flash->command[0] = flash->program_opcode;
|
||||||
m25p_addr2cmd(flash, to, flash->command);
|
m25p_addr2cmd(flash, to, flash->command);
|
||||||
|
|
||||||
page_offset = to & (flash->page_size - 1);
|
page_offset = to & (flash->page_size - 1);
|
||||||
|
@ -682,6 +692,8 @@ struct flash_info {
|
||||||
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
|
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
|
||||||
#define M25P_NO_ERASE 0x02 /* No erase command needed */
|
#define M25P_NO_ERASE 0x02 /* No erase command needed */
|
||||||
#define SST_WRITE 0x04 /* use SST byte programming */
|
#define SST_WRITE 0x04 /* use SST byte programming */
|
||||||
|
#define M25P_NO_FR 0x08 /* Can't do fastread */
|
||||||
|
#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||||
|
@ -694,13 +706,13 @@ struct flash_info {
|
||||||
.flags = (_flags), \
|
.flags = (_flags), \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \
|
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \
|
||||||
((kernel_ulong_t)&(struct flash_info) { \
|
((kernel_ulong_t)&(struct flash_info) { \
|
||||||
.sector_size = (_sector_size), \
|
.sector_size = (_sector_size), \
|
||||||
.n_sectors = (_n_sectors), \
|
.n_sectors = (_n_sectors), \
|
||||||
.page_size = (_page_size), \
|
.page_size = (_page_size), \
|
||||||
.addr_width = (_addr_width), \
|
.addr_width = (_addr_width), \
|
||||||
.flags = M25P_NO_ERASE, \
|
.flags = (_flags), \
|
||||||
})
|
})
|
||||||
|
|
||||||
/* NOTE: double check command sets and memory organization when you add
|
/* NOTE: double check command sets and memory organization when you add
|
||||||
|
@ -732,7 +744,8 @@ static const struct spi_device_id m25p_ids[] = {
|
||||||
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
||||||
|
|
||||||
/* Everspin */
|
/* Everspin */
|
||||||
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) },
|
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) },
|
||||||
|
{ "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) },
|
||||||
|
|
||||||
/* GigaDevice */
|
/* GigaDevice */
|
||||||
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
|
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
|
@ -762,6 +775,11 @@ static const struct spi_device_id m25p_ids[] = {
|
||||||
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
|
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
|
||||||
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) },
|
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) },
|
||||||
|
|
||||||
|
/* PMC */
|
||||||
|
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
|
||||||
|
{ "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) },
|
||||||
|
{ "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, 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).
|
||||||
*/
|
*/
|
||||||
|
@ -840,17 +858,18 @@ static const struct spi_device_id m25p_ids[] = {
|
||||||
{ "w25q32dw", INFO(0xef6016, 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) },
|
||||||
|
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
||||||
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
||||||
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
|
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
|
||||||
|
|
||||||
/* Catalyst / On Semiconductor -- non-JEDEC */
|
/* Catalyst / On Semiconductor -- non-JEDEC */
|
||||||
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
|
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) },
|
||||||
{ "cat25c03", CAT25_INFO( 32, 8, 16, 2) },
|
{ "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) },
|
||||||
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2) },
|
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
|
||||||
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2) },
|
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
|
||||||
{ "cat25128", CAT25_INFO(2048, 8, 64, 2) },
|
{ "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, m25p_ids);
|
MODULE_DEVICE_TABLE(spi, m25p_ids);
|
||||||
|
@ -920,7 +939,7 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
* a chip ID, try the JEDEC id commands; they'll work for most
|
* a chip ID, try the JEDEC id commands; they'll work for most
|
||||||
* newer chips, even if we don't recognize the particular chip.
|
* newer chips, even if we don't recognize the particular chip.
|
||||||
*/
|
*/
|
||||||
data = spi->dev.platform_data;
|
data = dev_get_platdata(&spi->dev);
|
||||||
if (data && data->type) {
|
if (data && data->type) {
|
||||||
const struct spi_device_id *plat_id;
|
const struct spi_device_id *plat_id;
|
||||||
|
|
||||||
|
@ -972,7 +991,7 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
|
|
||||||
flash->spi = spi;
|
flash->spi = spi;
|
||||||
mutex_init(&flash->lock);
|
mutex_init(&flash->lock);
|
||||||
dev_set_drvdata(&spi->dev, flash);
|
spi_set_drvdata(spi, flash);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Atmel, SST and Intel/Numonyx serial flash tend to power
|
* Atmel, SST and Intel/Numonyx serial flash tend to power
|
||||||
|
@ -1014,6 +1033,9 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
if (info->flags & SECT_4K) {
|
if (info->flags & SECT_4K) {
|
||||||
flash->erase_opcode = OPCODE_BE_4K;
|
flash->erase_opcode = OPCODE_BE_4K;
|
||||||
flash->mtd.erasesize = 4096;
|
flash->mtd.erasesize = 4096;
|
||||||
|
} else if (info->flags & SECT_4K_PMC) {
|
||||||
|
flash->erase_opcode = OPCODE_BE_4K_PMC;
|
||||||
|
flash->mtd.erasesize = 4096;
|
||||||
} else {
|
} else {
|
||||||
flash->erase_opcode = OPCODE_SE;
|
flash->erase_opcode = OPCODE_SE;
|
||||||
flash->mtd.erasesize = info->sector_size;
|
flash->mtd.erasesize = info->sector_size;
|
||||||
|
@ -1028,23 +1050,40 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
flash->mtd.writebufsize = flash->page_size;
|
flash->mtd.writebufsize = flash->page_size;
|
||||||
|
|
||||||
flash->fast_read = false;
|
flash->fast_read = false;
|
||||||
#ifdef CONFIG_OF
|
|
||||||
if (np && of_property_read_bool(np, "m25p,fast-read"))
|
if (np && of_property_read_bool(np, "m25p,fast-read"))
|
||||||
flash->fast_read = true;
|
flash->fast_read = true;
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_M25PXX_USE_FAST_READ
|
#ifdef CONFIG_M25PXX_USE_FAST_READ
|
||||||
flash->fast_read = true;
|
flash->fast_read = true;
|
||||||
#endif
|
#endif
|
||||||
|
if (info->flags & M25P_NO_FR)
|
||||||
|
flash->fast_read = false;
|
||||||
|
|
||||||
|
/* Default commands */
|
||||||
|
if (flash->fast_read)
|
||||||
|
flash->read_opcode = OPCODE_FAST_READ;
|
||||||
|
else
|
||||||
|
flash->read_opcode = OPCODE_NORM_READ;
|
||||||
|
|
||||||
|
flash->program_opcode = OPCODE_PP;
|
||||||
|
|
||||||
if (info->addr_width)
|
if (info->addr_width)
|
||||||
flash->addr_width = info->addr_width;
|
flash->addr_width = info->addr_width;
|
||||||
else {
|
else if (flash->mtd.size > 0x1000000) {
|
||||||
/* enable 4-byte addressing if the device exceeds 16MiB */
|
/* enable 4-byte addressing if the device exceeds 16MiB */
|
||||||
if (flash->mtd.size > 0x1000000) {
|
|
||||||
flash->addr_width = 4;
|
flash->addr_width = 4;
|
||||||
set_4byte(flash, info->jedec_id, 1);
|
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
|
||||||
|
/* Dedicated 4-byte command set */
|
||||||
|
flash->read_opcode = flash->fast_read ?
|
||||||
|
OPCODE_FAST_READ_4B :
|
||||||
|
OPCODE_NORM_READ_4B;
|
||||||
|
flash->program_opcode = OPCODE_PP_4B;
|
||||||
|
/* No small sector erase for 4-byte command set */
|
||||||
|
flash->erase_opcode = OPCODE_SE_4B;
|
||||||
|
flash->mtd.erasesize = info->sector_size;
|
||||||
} else
|
} else
|
||||||
|
set_4byte(flash, info->jedec_id, 1);
|
||||||
|
} else {
|
||||||
flash->addr_width = 3;
|
flash->addr_width = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1080,7 +1119,7 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
|
|
||||||
static int m25p_remove(struct spi_device *spi)
|
static int m25p_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct m25p *flash = dev_get_drvdata(&spi->dev);
|
struct m25p *flash = spi_get_drvdata(spi);
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
/* Clean up MTD stuff. */
|
/* Clean up MTD stuff. */
|
||||||
|
|
|
@ -622,7 +622,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
|
||||||
struct dataflash *priv;
|
struct dataflash *priv;
|
||||||
struct mtd_info *device;
|
struct mtd_info *device;
|
||||||
struct mtd_part_parser_data ppdata;
|
struct mtd_part_parser_data ppdata;
|
||||||
struct flash_platform_data *pdata = spi->dev.platform_data;
|
struct flash_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||||
char *otp_tag = "";
|
char *otp_tag = "";
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
@ -661,7 +661,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
|
||||||
dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n",
|
dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n",
|
||||||
name, (long long)((device->size + 1023) >> 10),
|
name, (long long)((device->size + 1023) >> 10),
|
||||||
pagesize, otp_tag);
|
pagesize, otp_tag);
|
||||||
dev_set_drvdata(&spi->dev, priv);
|
spi_set_drvdata(spi, priv);
|
||||||
|
|
||||||
ppdata.of_node = spi->dev.of_node;
|
ppdata.of_node = spi->dev.of_node;
|
||||||
err = mtd_device_parse_register(device, NULL, &ppdata,
|
err = mtd_device_parse_register(device, NULL, &ppdata,
|
||||||
|
@ -671,7 +671,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
|
||||||
if (!err)
|
if (!err)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
dev_set_drvdata(&spi->dev, NULL);
|
spi_set_drvdata(spi, NULL);
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -895,14 +895,14 @@ static int dataflash_probe(struct spi_device *spi)
|
||||||
|
|
||||||
static int dataflash_remove(struct spi_device *spi)
|
static int dataflash_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct dataflash *flash = dev_get_drvdata(&spi->dev);
|
struct dataflash *flash = spi_get_drvdata(spi);
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
pr_debug("%s: remove\n", dev_name(&spi->dev));
|
pr_debug("%s: remove\n", dev_name(&spi->dev));
|
||||||
|
|
||||||
status = mtd_device_unregister(&flash->mtd);
|
status = mtd_device_unregister(&flash->mtd);
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
dev_set_drvdata(&spi->dev, NULL);
|
spi_set_drvdata(spi, NULL);
|
||||||
kfree(flash);
|
kfree(flash);
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -550,7 +550,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
{
|
{
|
||||||
struct spear_snor_flash *flash = get_flash_data(mtd);
|
struct spear_snor_flash *flash = get_flash_data(mtd);
|
||||||
struct spear_smi *dev = mtd->priv;
|
struct spear_smi *dev = mtd->priv;
|
||||||
void *src;
|
void __iomem *src;
|
||||||
u32 ctrlreg1, val;
|
u32 ctrlreg1, val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -583,7 +583,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
|
|
||||||
writel(val, dev->io_base + SMI_CR1);
|
writel(val, dev->io_base + SMI_CR1);
|
||||||
|
|
||||||
memcpy_fromio(buf, (u8 *)src, len);
|
memcpy_fromio(buf, src, len);
|
||||||
|
|
||||||
/* restore ctrl reg1 */
|
/* restore ctrl reg1 */
|
||||||
writel(ctrlreg1, dev->io_base + SMI_CR1);
|
writel(ctrlreg1, dev->io_base + SMI_CR1);
|
||||||
|
@ -596,7 +596,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
|
static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
|
||||||
void *dest, const void *src, size_t len)
|
void __iomem *dest, const void *src, size_t len)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
u32 ctrlreg1;
|
u32 ctrlreg1;
|
||||||
|
@ -643,7 +643,7 @@ static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
{
|
{
|
||||||
struct spear_snor_flash *flash = get_flash_data(mtd);
|
struct spear_snor_flash *flash = get_flash_data(mtd);
|
||||||
struct spear_smi *dev = mtd->priv;
|
struct spear_smi *dev = mtd->priv;
|
||||||
void *dest;
|
void __iomem *dest;
|
||||||
u32 page_offset, page_size;
|
u32 page_offset, page_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -995,14 +995,12 @@ static int spear_smi_probe(struct platform_device *pdev)
|
||||||
ret = spear_smi_setup_banks(pdev, i, pdata->np[i]);
|
ret = spear_smi_setup_banks(pdev, i, pdata->np[i]);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&dev->pdev->dev, "bank setup failed\n");
|
dev_err(&dev->pdev->dev, "bank setup failed\n");
|
||||||
goto err_bank_setup;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_bank_setup:
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
err_irq:
|
err_irq:
|
||||||
clk_disable_unprepare(dev->clk);
|
clk_disable_unprepare(dev->clk);
|
||||||
err:
|
err:
|
||||||
|
@ -1040,12 +1038,11 @@ static int spear_smi_remove(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_disable_unprepare(dev->clk);
|
clk_disable_unprepare(dev->clk);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int spear_smi_suspend(struct device *dev)
|
static int spear_smi_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spear_smi *sdev = dev_get_drvdata(dev);
|
struct spear_smi *sdev = dev_get_drvdata(dev);
|
||||||
|
@ -1068,9 +1065,9 @@ static int spear_smi_resume(struct device *dev)
|
||||||
spear_smi_hw_init(sdev);
|
spear_smi_hw_init(sdev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume);
|
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[] = {
|
||||||
|
@ -1086,9 +1083,7 @@ 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,
|
.pm = &spear_smi_pm_ops,
|
||||||
#endif
|
|
||||||
},
|
},
|
||||||
.probe = spear_smi_probe,
|
.probe = spear_smi_probe,
|
||||||
.remove = spear_smi_remove,
|
.remove = spear_smi_remove,
|
||||||
|
|
|
@ -370,9 +370,9 @@ static int sst25l_probe(struct spi_device *spi)
|
||||||
|
|
||||||
flash->spi = spi;
|
flash->spi = spi;
|
||||||
mutex_init(&flash->lock);
|
mutex_init(&flash->lock);
|
||||||
dev_set_drvdata(&spi->dev, flash);
|
spi_set_drvdata(spi, flash);
|
||||||
|
|
||||||
data = spi->dev.platform_data;
|
data = dev_get_platdata(&spi->dev);
|
||||||
if (data && data->name)
|
if (data && data->name)
|
||||||
flash->mtd.name = data->name;
|
flash->mtd.name = data->name;
|
||||||
else
|
else
|
||||||
|
@ -404,7 +404,7 @@ static int sst25l_probe(struct spi_device *spi)
|
||||||
data ? data->nr_parts : 0);
|
data ? data->nr_parts : 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kfree(flash);
|
kfree(flash);
|
||||||
dev_set_drvdata(&spi->dev, NULL);
|
spi_set_drvdata(spi, NULL);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +413,7 @@ static int sst25l_probe(struct spi_device *spi)
|
||||||
|
|
||||||
static int sst25l_remove(struct spi_device *spi)
|
static int sst25l_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct sst25l_flash *flash = dev_get_drvdata(&spi->dev);
|
struct sst25l_flash *flash = spi_get_drvdata(spi);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = mtd_device_unregister(&flash->mtd);
|
ret = mtd_device_unregister(&flash->mtd);
|
||||||
|
|
|
@ -157,24 +157,6 @@ config MTD_PXA2XX
|
||||||
help
|
help
|
||||||
This provides a driver for the NOR flash attached to a PXA2xx chip.
|
This provides a driver for the NOR flash attached to a PXA2xx chip.
|
||||||
|
|
||||||
config MTD_OCTAGON
|
|
||||||
tristate "JEDEC Flash device mapped on Octagon 5066 SBC"
|
|
||||||
depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS
|
|
||||||
help
|
|
||||||
This provides a 'mapping' driver which supports the way in which
|
|
||||||
the flash chips are connected in the Octagon-5066 Single Board
|
|
||||||
Computer. More information on the board is available at
|
|
||||||
<http://www.octagonsystems.com/products/5066.aspx>.
|
|
||||||
|
|
||||||
config MTD_VMAX
|
|
||||||
tristate "JEDEC Flash device mapped on Tempustech VMAX SBC301"
|
|
||||||
depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS
|
|
||||||
help
|
|
||||||
This provides a 'mapping' driver which supports the way in which
|
|
||||||
the flash chips are connected in the Tempustech VMAX SBC301 Single
|
|
||||||
Board Computer. More information on the board is available at
|
|
||||||
<http://www.tempustech.com/>.
|
|
||||||
|
|
||||||
config MTD_SCx200_DOCFLASH
|
config MTD_SCx200_DOCFLASH
|
||||||
tristate "Flash device mapped with DOCCS on NatSemi SCx200"
|
tristate "Flash device mapped with DOCCS on NatSemi SCx200"
|
||||||
depends on SCx200 && MTD_CFI
|
depends on SCx200 && MTD_CFI
|
||||||
|
|
|
@ -16,7 +16,6 @@ obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
|
||||||
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
|
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
|
||||||
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
|
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
|
||||||
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
|
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
|
||||||
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
|
||||||
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
|
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
|
||||||
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
|
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
|
||||||
obj-$(CONFIG_MTD_PISMO) += pismo.o
|
obj-$(CONFIG_MTD_PISMO) += pismo.o
|
||||||
|
@ -28,7 +27,6 @@ obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
|
||||||
obj-$(CONFIG_MTD_NETSC520) += netsc520.o
|
obj-$(CONFIG_MTD_NETSC520) += netsc520.o
|
||||||
obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o
|
obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o
|
||||||
obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
|
obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
|
||||||
obj-$(CONFIG_MTD_VMAX) += vmax301.o
|
|
||||||
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
|
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
|
||||||
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
|
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
|
||||||
obj-$(CONFIG_MTD_PCI) += pci.o
|
obj-$(CONFIG_MTD_PCI) += pci.o
|
||||||
|
|
|
@ -128,7 +128,7 @@ static const char * const part_probe_types[] = {
|
||||||
static int bfin_flash_probe(struct platform_device *pdev)
|
static int bfin_flash_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct physmap_flash_data *pdata = pdev->dev.platform_data;
|
struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
struct async_state *state;
|
struct async_state *state;
|
||||||
|
|
|
@ -55,13 +55,13 @@
|
||||||
#define FLASH_PARTITION3_SIZE 0x001C0000
|
#define FLASH_PARTITION3_SIZE 0x001C0000
|
||||||
|
|
||||||
|
|
||||||
struct map_info flagadm_map = {
|
static struct map_info flagadm_map = {
|
||||||
.name = "FlagaDM flash device",
|
.name = "FlagaDM flash device",
|
||||||
.size = FLASH_SIZE,
|
.size = FLASH_SIZE,
|
||||||
.bankwidth = 2,
|
.bankwidth = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mtd_partition flagadm_parts[] = {
|
static struct mtd_partition flagadm_parts[] = {
|
||||||
{
|
{
|
||||||
.name = "Bootloader",
|
.name = "Bootloader",
|
||||||
.offset = FLASH_PARTITION0_ADDR,
|
.offset = FLASH_PARTITION0_ADDR,
|
||||||
|
@ -112,7 +112,7 @@ static int __init init_flagadm(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
iounmap((void *)flagadm_map.virt);
|
iounmap((void __iomem *)flagadm_map.virt);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +123,8 @@ static void __exit cleanup_flagadm(void)
|
||||||
map_destroy(mymtd);
|
map_destroy(mymtd);
|
||||||
}
|
}
|
||||||
if (flagadm_map.virt) {
|
if (flagadm_map.virt) {
|
||||||
iounmap((void *)flagadm_map.virt);
|
iounmap((void __iomem *)flagadm_map.virt);
|
||||||
flagadm_map.virt = 0;
|
flagadm_map.virt = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
|
||||||
struct resource *gpios;
|
struct resource *gpios;
|
||||||
struct async_state *state;
|
struct async_state *state;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ static int __init init_impa7(void)
|
||||||
}
|
}
|
||||||
simple_map_init(&impa7_map[i]);
|
simple_map_init(&impa7_map[i]);
|
||||||
|
|
||||||
impa7_mtd[i] = 0;
|
impa7_mtd[i] = NULL;
|
||||||
type = rom_probe_types;
|
type = rom_probe_types;
|
||||||
for(; !impa7_mtd[i] && *type; type++) {
|
for(; !impa7_mtd[i] && *type; type++) {
|
||||||
impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]);
|
impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]);
|
||||||
|
@ -91,9 +91,9 @@ static int __init init_impa7(void)
|
||||||
mtd_device_parse_register(impa7_mtd[i], NULL, NULL,
|
mtd_device_parse_register(impa7_mtd[i], NULL, NULL,
|
||||||
partitions,
|
partitions,
|
||||||
ARRAY_SIZE(partitions));
|
ARRAY_SIZE(partitions));
|
||||||
|
} else {
|
||||||
|
iounmap((void __iomem *)impa7_map[i].virt);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
iounmap((void *)impa7_map[i].virt);
|
|
||||||
}
|
}
|
||||||
return devicesfound == 0 ? -ENXIO : 0;
|
return devicesfound == 0 ? -ENXIO : 0;
|
||||||
}
|
}
|
||||||
|
@ -105,8 +105,8 @@ static void __exit cleanup_impa7(void)
|
||||||
if (impa7_mtd[i]) {
|
if (impa7_mtd[i]) {
|
||||||
mtd_device_unregister(impa7_mtd[i]);
|
mtd_device_unregister(impa7_mtd[i]);
|
||||||
map_destroy(impa7_mtd[i]);
|
map_destroy(impa7_mtd[i]);
|
||||||
iounmap((void *)impa7_map[i].virt);
|
iounmap((void __iomem *)impa7_map[i].virt);
|
||||||
impa7_map[i].virt = 0;
|
impa7_map[i].virt = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,11 +152,9 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||||
|
|
||||||
static int ixp4xx_flash_remove(struct platform_device *dev)
|
static int ixp4xx_flash_remove(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct flash_platform_data *plat = dev->dev.platform_data;
|
struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
|
||||||
struct ixp4xx_flash_info *info = platform_get_drvdata(dev);
|
struct ixp4xx_flash_info *info = platform_get_drvdata(dev);
|
||||||
|
|
||||||
platform_set_drvdata(dev, NULL);
|
|
||||||
|
|
||||||
if(!info)
|
if(!info)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -180,7 +178,7 @@ static int ixp4xx_flash_remove(struct platform_device *dev)
|
||||||
|
|
||||||
static int ixp4xx_flash_probe(struct platform_device *dev)
|
static int ixp4xx_flash_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct flash_platform_data *plat = dev->dev.platform_data;
|
struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
|
||||||
struct ixp4xx_flash_info *info;
|
struct ixp4xx_flash_info *info;
|
||||||
struct mtd_part_parser_data ppdata = {
|
struct mtd_part_parser_data ppdata = {
|
||||||
.origin = dev->resource->start,
|
.origin = dev->resource->start,
|
||||||
|
|
|
@ -102,9 +102,8 @@ static int latch_addr_flash_remove(struct platform_device *dev)
|
||||||
info = platform_get_drvdata(dev);
|
info = platform_get_drvdata(dev);
|
||||||
if (info == NULL)
|
if (info == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
platform_set_drvdata(dev, NULL);
|
|
||||||
|
|
||||||
latch_addr_data = dev->dev.platform_data;
|
latch_addr_data = dev_get_platdata(&dev->dev);
|
||||||
|
|
||||||
if (info->mtd != NULL) {
|
if (info->mtd != NULL) {
|
||||||
mtd_device_unregister(info->mtd);
|
mtd_device_unregister(info->mtd);
|
||||||
|
@ -135,7 +134,7 @@ static int latch_addr_flash_probe(struct platform_device *dev)
|
||||||
int chipsel;
|
int chipsel;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
latch_addr_data = dev->dev.platform_data;
|
latch_addr_data = dev_get_platdata(&dev->dev);
|
||||||
if (latch_addr_data == NULL)
|
if (latch_addr_data == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
|
|
@ -1,246 +0,0 @@
|
||||||
/* ######################################################################
|
|
||||||
|
|
||||||
Octagon 5066 MTD Driver.
|
|
||||||
|
|
||||||
The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It
|
|
||||||
comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that
|
|
||||||
is replacable by flash. Both units are mapped through a multiplexer
|
|
||||||
into a 32k memory window at 0xe8000. The control register for the
|
|
||||||
multiplexing unit is located at IO 0x208 with a bit map of
|
|
||||||
0-5 Page Selection in 32k increments
|
|
||||||
6-7 Device selection:
|
|
||||||
00 SSD off
|
|
||||||
01 SSD 0 (Socket)
|
|
||||||
10 SSD 1 (Flash chip)
|
|
||||||
11 undefined
|
|
||||||
|
|
||||||
On each SSD, the first 128k is reserved for use by the bios
|
|
||||||
(actually it IS the bios..) This only matters if you are booting off the
|
|
||||||
flash, you must not put a file system starting there.
|
|
||||||
|
|
||||||
The driver tries to do a detection algorithm to guess what sort of devices
|
|
||||||
are plugged into the sockets.
|
|
||||||
|
|
||||||
##################################################################### */
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <asm/io.h>
|
|
||||||
|
|
||||||
#include <linux/mtd/map.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
|
||||||
|
|
||||||
#define WINDOW_START 0xe8000
|
|
||||||
#define WINDOW_LENGTH 0x8000
|
|
||||||
#define WINDOW_SHIFT 27
|
|
||||||
#define WINDOW_MASK 0x7FFF
|
|
||||||
#define PAGE_IO 0x208
|
|
||||||
|
|
||||||
static volatile char page_n_dev = 0;
|
|
||||||
static unsigned long iomapadr;
|
|
||||||
static DEFINE_SPINLOCK(oct5066_spin);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We use map_priv_1 to identify which device we are.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void __oct5066_page(struct map_info *map, __u8 byte)
|
|
||||||
{
|
|
||||||
outb(byte,PAGE_IO);
|
|
||||||
page_n_dev = byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void oct5066_page(struct map_info *map, unsigned long ofs)
|
|
||||||
{
|
|
||||||
__u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT);
|
|
||||||
|
|
||||||
if (page_n_dev != byte)
|
|
||||||
__oct5066_page(map, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static map_word oct5066_read8(struct map_info *map, unsigned long ofs)
|
|
||||||
{
|
|
||||||
map_word ret;
|
|
||||||
spin_lock(&oct5066_spin);
|
|
||||||
oct5066_page(map, ofs);
|
|
||||||
ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
|
|
||||||
spin_unlock(&oct5066_spin);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
|
|
||||||
{
|
|
||||||
while(len) {
|
|
||||||
unsigned long thislen = len;
|
|
||||||
if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
|
|
||||||
thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
|
|
||||||
|
|
||||||
spin_lock(&oct5066_spin);
|
|
||||||
oct5066_page(map, from);
|
|
||||||
memcpy_fromio(to, iomapadr + from, thislen);
|
|
||||||
spin_unlock(&oct5066_spin);
|
|
||||||
to += thislen;
|
|
||||||
from += thislen;
|
|
||||||
len -= thislen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr)
|
|
||||||
{
|
|
||||||
spin_lock(&oct5066_spin);
|
|
||||||
oct5066_page(map, adr);
|
|
||||||
writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
|
|
||||||
spin_unlock(&oct5066_spin);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
|
|
||||||
{
|
|
||||||
while(len) {
|
|
||||||
unsigned long thislen = len;
|
|
||||||
if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
|
|
||||||
thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
|
|
||||||
|
|
||||||
spin_lock(&oct5066_spin);
|
|
||||||
oct5066_page(map, to);
|
|
||||||
memcpy_toio(iomapadr + to, from, thislen);
|
|
||||||
spin_unlock(&oct5066_spin);
|
|
||||||
to += thislen;
|
|
||||||
from += thislen;
|
|
||||||
len -= thislen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct map_info oct5066_map[2] = {
|
|
||||||
{
|
|
||||||
.name = "Octagon 5066 Socket",
|
|
||||||
.phys = NO_XIP,
|
|
||||||
.size = 512 * 1024,
|
|
||||||
.bankwidth = 1,
|
|
||||||
.read = oct5066_read8,
|
|
||||||
.copy_from = oct5066_copy_from,
|
|
||||||
.write = oct5066_write8,
|
|
||||||
.copy_to = oct5066_copy_to,
|
|
||||||
.map_priv_1 = 1<<6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "Octagon 5066 Internal Flash",
|
|
||||||
.phys = NO_XIP,
|
|
||||||
.size = 2 * 1024 * 1024,
|
|
||||||
.bankwidth = 1,
|
|
||||||
.read = oct5066_read8,
|
|
||||||
.copy_from = oct5066_copy_from,
|
|
||||||
.write = oct5066_write8,
|
|
||||||
.copy_to = oct5066_copy_to,
|
|
||||||
.map_priv_1 = 2<<6
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
|
|
||||||
|
|
||||||
// OctProbe - Sense if this is an octagon card
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
/* Perform a simple validity test, we map the window select SSD0 and
|
|
||||||
change pages while monitoring the window. A change in the window,
|
|
||||||
controlled by the PAGE_IO port is a functioning 5066 board. This will
|
|
||||||
fail if the thing in the socket is set to a uniform value. */
|
|
||||||
static int __init OctProbe(void)
|
|
||||||
{
|
|
||||||
unsigned int Base = (1 << 6);
|
|
||||||
unsigned long I;
|
|
||||||
unsigned long Values[10];
|
|
||||||
for (I = 0; I != 20; I++)
|
|
||||||
{
|
|
||||||
outb(Base + (I%10),PAGE_IO);
|
|
||||||
if (I < 10)
|
|
||||||
{
|
|
||||||
// Record the value and check for uniqueness
|
|
||||||
Values[I%10] = readl(iomapadr);
|
|
||||||
if (I > 0 && Values[I%10] == Values[0])
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Make sure we get the same values on the second pass
|
|
||||||
if (Values[I%10] != readl(iomapadr))
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup_oct5066(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
if (oct5066_mtd[i]) {
|
|
||||||
mtd_device_unregister(oct5066_mtd[i]);
|
|
||||||
map_destroy(oct5066_mtd[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iounmap((void *)iomapadr);
|
|
||||||
release_region(PAGE_IO, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init init_oct5066(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
// Do an autoprobe sequence
|
|
||||||
if (!request_region(PAGE_IO,1,"Octagon SSD")) {
|
|
||||||
printk(KERN_NOTICE "5066: Page Register in Use\n");
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
|
|
||||||
if (!iomapadr) {
|
|
||||||
printk(KERN_NOTICE "Failed to ioremap memory region\n");
|
|
||||||
ret = -EIO;
|
|
||||||
goto out_rel;
|
|
||||||
}
|
|
||||||
if (OctProbe() != 0) {
|
|
||||||
printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
|
|
||||||
iounmap((void *)iomapadr);
|
|
||||||
ret = -EAGAIN;
|
|
||||||
goto out_unmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print out our little header..
|
|
||||||
printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
|
|
||||||
WINDOW_START+WINDOW_LENGTH);
|
|
||||||
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]);
|
|
||||||
if (!oct5066_mtd[i])
|
|
||||||
oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
|
|
||||||
if (!oct5066_mtd[i])
|
|
||||||
oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]);
|
|
||||||
if (!oct5066_mtd[i])
|
|
||||||
oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
|
|
||||||
if (oct5066_mtd[i]) {
|
|
||||||
oct5066_mtd[i]->owner = THIS_MODULE;
|
|
||||||
mtd_device_register(oct5066_mtd[i], NULL, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
|
|
||||||
cleanup_oct5066();
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_unmap:
|
|
||||||
iounmap((void *)iomapadr);
|
|
||||||
out_rel:
|
|
||||||
release_region(PAGE_IO, 1);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(init_oct5066);
|
|
||||||
module_exit(cleanup_oct5066);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com>, David Woodhouse <dwmw2@infradead.org>");
|
|
||||||
MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer");
|
|
|
@ -40,9 +40,8 @@ static int physmap_flash_remove(struct platform_device *dev)
|
||||||
info = platform_get_drvdata(dev);
|
info = platform_get_drvdata(dev);
|
||||||
if (info == NULL)
|
if (info == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
platform_set_drvdata(dev, NULL);
|
|
||||||
|
|
||||||
physmap_data = dev->dev.platform_data;
|
physmap_data = dev_get_platdata(&dev->dev);
|
||||||
|
|
||||||
if (info->cmtd) {
|
if (info->cmtd) {
|
||||||
mtd_device_unregister(info->cmtd);
|
mtd_device_unregister(info->cmtd);
|
||||||
|
@ -69,7 +68,7 @@ static void physmap_set_vpp(struct map_info *map, int state)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
pdev = (struct platform_device *)map->map_priv_1;
|
pdev = (struct platform_device *)map->map_priv_1;
|
||||||
physmap_data = pdev->dev.platform_data;
|
physmap_data = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
if (!physmap_data->set_vpp)
|
if (!physmap_data->set_vpp)
|
||||||
return;
|
return;
|
||||||
|
@ -103,7 +102,7 @@ static int physmap_flash_probe(struct platform_device *dev)
|
||||||
int i;
|
int i;
|
||||||
int devices_found = 0;
|
int devices_found = 0;
|
||||||
|
|
||||||
physmap_data = dev->dev.platform_data;
|
physmap_data = dev_get_platdata(&dev->dev);
|
||||||
if (physmap_data == NULL)
|
if (physmap_data == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
|
|
@ -84,8 +84,6 @@ static int platram_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct platram_info *info = to_platram_info(pdev);
|
struct platram_info *info = to_platram_info(pdev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "removing device\n");
|
dev_dbg(&pdev->dev, "removing device\n");
|
||||||
|
|
||||||
if (info == NULL)
|
if (info == NULL)
|
||||||
|
@ -130,13 +128,13 @@ static int platram_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "probe entered\n");
|
dev_dbg(&pdev->dev, "probe entered\n");
|
||||||
|
|
||||||
if (pdev->dev.platform_data == NULL) {
|
if (dev_get_platdata(&pdev->dev) == NULL) {
|
||||||
dev_err(&pdev->dev, "no platform data supplied\n");
|
dev_err(&pdev->dev, "no platform data supplied\n");
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
goto exit_error;
|
goto exit_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||||
if (info == NULL) {
|
if (info == NULL) {
|
||||||
|
|
|
@ -49,7 +49,7 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||||
|
|
||||||
static int pxa2xx_flash_probe(struct platform_device *pdev)
|
static int pxa2xx_flash_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct flash_platform_data *flash = pdev->dev.platform_data;
|
struct flash_platform_data *flash = dev_get_platdata(&pdev->dev);
|
||||||
struct pxa2xx_flash_info *info;
|
struct pxa2xx_flash_info *info;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
||||||
|
@ -107,8 +107,6 @@ static int pxa2xx_flash_remove(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
|
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
|
||||||
|
|
||||||
platform_set_drvdata(dev, NULL);
|
|
||||||
|
|
||||||
mtd_device_unregister(info->mtd);
|
mtd_device_unregister(info->mtd);
|
||||||
|
|
||||||
map_destroy(info->mtd);
|
map_destroy(info->mtd);
|
||||||
|
|
|
@ -34,10 +34,9 @@ static int rbtx4939_flash_remove(struct platform_device *dev)
|
||||||
info = platform_get_drvdata(dev);
|
info = platform_get_drvdata(dev);
|
||||||
if (!info)
|
if (!info)
|
||||||
return 0;
|
return 0;
|
||||||
platform_set_drvdata(dev, NULL);
|
|
||||||
|
|
||||||
if (info->mtd) {
|
if (info->mtd) {
|
||||||
struct rbtx4939_flash_data *pdata = dev->dev.platform_data;
|
struct rbtx4939_flash_data *pdata = dev_get_platdata(&dev->dev);
|
||||||
|
|
||||||
mtd_device_unregister(info->mtd);
|
mtd_device_unregister(info->mtd);
|
||||||
map_destroy(info->mtd);
|
map_destroy(info->mtd);
|
||||||
|
@ -57,7 +56,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
|
||||||
int err = 0;
|
int err = 0;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
|
|
||||||
pdata = dev->dev.platform_data;
|
pdata = dev_get_platdata(&dev->dev);
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL };
|
||||||
|
|
||||||
static int sa1100_mtd_probe(struct platform_device *pdev)
|
static int sa1100_mtd_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct flash_platform_data *plat = pdev->dev.platform_data;
|
struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
|
||||||
struct sa_info *info;
|
struct sa_info *info;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -277,9 +277,8 @@ static int sa1100_mtd_probe(struct platform_device *pdev)
|
||||||
static int __exit sa1100_mtd_remove(struct platform_device *pdev)
|
static int __exit sa1100_mtd_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sa_info *info = platform_get_drvdata(pdev);
|
struct sa_info *info = platform_get_drvdata(pdev);
|
||||||
struct flash_platform_data *plat = pdev->dev.platform_data;
|
struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
sa1100_destroy(info, plat);
|
sa1100_destroy(info, plat);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,196 +0,0 @@
|
||||||
/* ######################################################################
|
|
||||||
|
|
||||||
Tempustech VMAX SBC301 MTD Driver.
|
|
||||||
|
|
||||||
The VMAx 301 is a SBC based on . It
|
|
||||||
comes with three builtin AMD 29F016B flash chips and a socket for SRAM or
|
|
||||||
more flash. Each unit has it's own 8k mapping into a settable region
|
|
||||||
(0xD8000). There are two 8k mappings for each MTD, the first is always set
|
|
||||||
to the lower 8k of the device the second is paged. Writing a 16 bit page
|
|
||||||
value to anywhere in the first 8k will cause the second 8k to page around.
|
|
||||||
|
|
||||||
To boot the device a bios extension must be installed into the first 8k
|
|
||||||
of flash that is smart enough to copy itself down, page in the rest of
|
|
||||||
itself and begin executing.
|
|
||||||
|
|
||||||
##################################################################### */
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <asm/io.h>
|
|
||||||
|
|
||||||
#include <linux/mtd/map.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define WINDOW_START 0xd8000
|
|
||||||
#define WINDOW_LENGTH 0x2000
|
|
||||||
#define WINDOW_SHIFT 25
|
|
||||||
#define WINDOW_MASK 0x1FFF
|
|
||||||
|
|
||||||
/* Actually we could use two spinlocks, but we'd have to have
|
|
||||||
more private space in the struct map_info. We lose a little
|
|
||||||
performance like this, but we'd probably lose more by having
|
|
||||||
the extra indirection from having one of the map->map_priv
|
|
||||||
fields pointing to yet another private struct.
|
|
||||||
*/
|
|
||||||
static DEFINE_SPINLOCK(vmax301_spin);
|
|
||||||
|
|
||||||
static void __vmax301_page(struct map_info *map, unsigned long page)
|
|
||||||
{
|
|
||||||
writew(page, map->map_priv_2 - WINDOW_LENGTH);
|
|
||||||
map->map_priv_1 = page;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void vmax301_page(struct map_info *map,
|
|
||||||
unsigned long ofs)
|
|
||||||
{
|
|
||||||
unsigned long page = (ofs >> WINDOW_SHIFT);
|
|
||||||
if (map->map_priv_1 != page)
|
|
||||||
__vmax301_page(map, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static map_word vmax301_read8(struct map_info *map, unsigned long ofs)
|
|
||||||
{
|
|
||||||
map_word ret;
|
|
||||||
spin_lock(&vmax301_spin);
|
|
||||||
vmax301_page(map, ofs);
|
|
||||||
ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
|
|
||||||
spin_unlock(&vmax301_spin);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
|
|
||||||
{
|
|
||||||
while(len) {
|
|
||||||
unsigned long thislen = len;
|
|
||||||
if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
|
|
||||||
thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
|
|
||||||
spin_lock(&vmax301_spin);
|
|
||||||
vmax301_page(map, from);
|
|
||||||
memcpy_fromio(to, map->map_priv_2 + from, thislen);
|
|
||||||
spin_unlock(&vmax301_spin);
|
|
||||||
to += thislen;
|
|
||||||
from += thislen;
|
|
||||||
len -= thislen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr)
|
|
||||||
{
|
|
||||||
spin_lock(&vmax301_spin);
|
|
||||||
vmax301_page(map, adr);
|
|
||||||
writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK));
|
|
||||||
spin_unlock(&vmax301_spin);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
|
|
||||||
{
|
|
||||||
while(len) {
|
|
||||||
unsigned long thislen = len;
|
|
||||||
if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
|
|
||||||
thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
|
|
||||||
|
|
||||||
spin_lock(&vmax301_spin);
|
|
||||||
vmax301_page(map, to);
|
|
||||||
memcpy_toio(map->map_priv_2 + to, from, thislen);
|
|
||||||
spin_unlock(&vmax301_spin);
|
|
||||||
to += thislen;
|
|
||||||
from += thislen;
|
|
||||||
len -= thislen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct map_info vmax_map[2] = {
|
|
||||||
{
|
|
||||||
.name = "VMAX301 Internal Flash",
|
|
||||||
.phys = NO_XIP,
|
|
||||||
.size = 3*2*1024*1024,
|
|
||||||
.bankwidth = 1,
|
|
||||||
.read = vmax301_read8,
|
|
||||||
.copy_from = vmax301_copy_from,
|
|
||||||
.write = vmax301_write8,
|
|
||||||
.copy_to = vmax301_copy_to,
|
|
||||||
.map_priv_1 = WINDOW_START + WINDOW_LENGTH,
|
|
||||||
.map_priv_2 = 0xFFFFFFFF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "VMAX301 Socket",
|
|
||||||
.phys = NO_XIP,
|
|
||||||
.size = 0,
|
|
||||||
.bankwidth = 1,
|
|
||||||
.read = vmax301_read8,
|
|
||||||
.copy_from = vmax301_copy_from,
|
|
||||||
.write = vmax301_write8,
|
|
||||||
.copy_to = vmax301_copy_to,
|
|
||||||
.map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
|
|
||||||
.map_priv_2 = 0xFFFFFFFF
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
|
|
||||||
|
|
||||||
static void __exit cleanup_vmax301(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
if (vmax_mtd[i]) {
|
|
||||||
mtd_device_unregister(vmax_mtd[i]);
|
|
||||||
map_destroy(vmax_mtd[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init init_vmax301(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned long iomapadr;
|
|
||||||
// Print out our little header..
|
|
||||||
printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
|
|
||||||
WINDOW_START+4*WINDOW_LENGTH);
|
|
||||||
|
|
||||||
iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
|
|
||||||
if (!iomapadr) {
|
|
||||||
printk("Failed to ioremap memory region\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
/* Put the address in the map's private data area.
|
|
||||||
We store the actual MTD IO address rather than the
|
|
||||||
address of the first half, because it's used more
|
|
||||||
often.
|
|
||||||
*/
|
|
||||||
vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
|
|
||||||
vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
|
|
||||||
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
|
|
||||||
if (!vmax_mtd[i])
|
|
||||||
vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
|
|
||||||
if (!vmax_mtd[i])
|
|
||||||
vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]);
|
|
||||||
if (!vmax_mtd[i])
|
|
||||||
vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
|
|
||||||
if (vmax_mtd[i]) {
|
|
||||||
vmax_mtd[i]->owner = THIS_MODULE;
|
|
||||||
mtd_device_register(vmax_mtd[i], NULL, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!vmax_mtd[0] && !vmax_mtd[1]) {
|
|
||||||
iounmap((void *)iomapadr);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(init_vmax301);
|
|
||||||
module_exit(cleanup_vmax301);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
|
||||||
MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");
|
|
|
@ -285,6 +285,16 @@ static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR,
|
||||||
mtd_bitflip_threshold_show,
|
mtd_bitflip_threshold_show,
|
||||||
mtd_bitflip_threshold_store);
|
mtd_bitflip_threshold_store);
|
||||||
|
|
||||||
|
static ssize_t mtd_ecc_step_size_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_step_size);
|
||||||
|
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR(ecc_step_size, S_IRUGO, mtd_ecc_step_size_show, NULL);
|
||||||
|
|
||||||
static struct attribute *mtd_attrs[] = {
|
static struct attribute *mtd_attrs[] = {
|
||||||
&dev_attr_type.attr,
|
&dev_attr_type.attr,
|
||||||
&dev_attr_flags.attr,
|
&dev_attr_flags.attr,
|
||||||
|
@ -296,6 +306,7 @@ static struct attribute *mtd_attrs[] = {
|
||||||
&dev_attr_numeraseregions.attr,
|
&dev_attr_numeraseregions.attr,
|
||||||
&dev_attr_name.attr,
|
&dev_attr_name.attr,
|
||||||
&dev_attr_ecc_strength.attr,
|
&dev_attr_ecc_strength.attr,
|
||||||
|
&dev_attr_ecc_step_size.attr,
|
||||||
&dev_attr_bitflip_threshold.attr,
|
&dev_attr_bitflip_threshold.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
|
@ -516,6 +516,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||||
}
|
}
|
||||||
|
|
||||||
slave->mtd.ecclayout = master->ecclayout;
|
slave->mtd.ecclayout = master->ecclayout;
|
||||||
|
slave->mtd.ecc_step_size = master->ecc_step_size;
|
||||||
slave->mtd.ecc_strength = master->ecc_strength;
|
slave->mtd.ecc_strength = master->ecc_strength;
|
||||||
slave->mtd.bitflip_threshold = master->bitflip_threshold;
|
slave->mtd.bitflip_threshold = master->bitflip_threshold;
|
||||||
|
|
||||||
|
|
|
@ -1425,7 +1425,7 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while ((this_opt = strsep(&parts, ",")) != NULL) {
|
while ((this_opt = strsep(&parts, ",")) != NULL) {
|
||||||
if (strict_strtoul(this_opt, 0, &part) < 0)
|
if (kstrtoul(this_opt, 0, &part) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mtd->index == part)
|
if (mtd->index == part)
|
||||||
|
|
|
@ -43,6 +43,7 @@ config MTD_SM_COMMON
|
||||||
|
|
||||||
config MTD_NAND_DENALI
|
config MTD_NAND_DENALI
|
||||||
tristate "Support Denali NAND controller"
|
tristate "Support Denali NAND controller"
|
||||||
|
depends on HAS_DMA
|
||||||
help
|
help
|
||||||
Enable support for the Denali NAND controller. This should be
|
Enable support for the Denali NAND controller. This should be
|
||||||
combined with either the PCI or platform drivers to provide device
|
combined with either the PCI or platform drivers to provide device
|
||||||
|
@ -75,7 +76,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||||
|
|
||||||
config MTD_NAND_GPIO
|
config MTD_NAND_GPIO
|
||||||
tristate "GPIO NAND Flash driver"
|
tristate "GPIO NAND Flash driver"
|
||||||
depends on GPIOLIB && ARM
|
depends on GPIOLIB
|
||||||
help
|
help
|
||||||
This enables a GPIO based NAND flash driver.
|
This enables a GPIO based NAND flash driver.
|
||||||
|
|
||||||
|
@ -354,7 +355,7 @@ config MTD_NAND_ATMEL
|
||||||
|
|
||||||
config MTD_NAND_PXA3xx
|
config MTD_NAND_PXA3xx
|
||||||
tristate "Support for NAND flash devices on PXA3xx"
|
tristate "Support for NAND flash devices on PXA3xx"
|
||||||
depends on PXA3xx || ARCH_MMP
|
depends on PXA3xx || ARCH_MMP || PLAT_ORION
|
||||||
help
|
help
|
||||||
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
|
||||||
|
@ -432,13 +433,6 @@ config MTD_NAND_PLATFORM
|
||||||
devices. You will need to provide platform-specific functions
|
devices. You will need to provide platform-specific functions
|
||||||
via platform_data.
|
via platform_data.
|
||||||
|
|
||||||
config MTD_ALAUDA
|
|
||||||
tristate "MTD driver for Olympus MAUSB-10 and Fujifilm DPC-R1"
|
|
||||||
depends on USB
|
|
||||||
help
|
|
||||||
These two (and possibly other) Alauda-based cardreaders for
|
|
||||||
SmartMedia and xD allow raw flash access.
|
|
||||||
|
|
||||||
config MTD_NAND_ORION
|
config MTD_NAND_ORION
|
||||||
tristate "NAND Flash support for Marvell Orion SoC"
|
tristate "NAND Flash support for Marvell Orion SoC"
|
||||||
depends on PLAT_ORION
|
depends on PLAT_ORION
|
||||||
|
|
|
@ -31,7 +31,6 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
|
obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
|
obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
|
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
|
||||||
obj-$(CONFIG_MTD_ALAUDA) += alauda.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
|
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
|
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
|
||||||
|
|
|
@ -1,723 +0,0 @@
|
||||||
/*
|
|
||||||
* MTD driver for Alauda chips
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 Joern Engel <joern@logfs.org>
|
|
||||||
*
|
|
||||||
* Based on drivers/usb/usb-skeleton.c which is:
|
|
||||||
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
|
|
||||||
* and on drivers/usb/storage/alauda.c, which is:
|
|
||||||
* (c) 2005 Daniel Drake <dsd@gentoo.org>
|
|
||||||
*
|
|
||||||
* Idea and initial work by Arnd Bergmann <arnd@arndb.de>
|
|
||||||
*/
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/kref.h>
|
|
||||||
#include <linux/usb.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
|
||||||
#include <linux/mtd/nand_ecc.h>
|
|
||||||
|
|
||||||
/* Control commands */
|
|
||||||
#define ALAUDA_GET_XD_MEDIA_STATUS 0x08
|
|
||||||
#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a
|
|
||||||
#define ALAUDA_GET_XD_MEDIA_SIG 0x86
|
|
||||||
|
|
||||||
/* Common prefix */
|
|
||||||
#define ALAUDA_BULK_CMD 0x40
|
|
||||||
|
|
||||||
/* The two ports */
|
|
||||||
#define ALAUDA_PORT_XD 0x00
|
|
||||||
#define ALAUDA_PORT_SM 0x01
|
|
||||||
|
|
||||||
/* Bulk commands */
|
|
||||||
#define ALAUDA_BULK_READ_PAGE 0x84
|
|
||||||
#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */
|
|
||||||
#define ALAUDA_BULK_READ_BLOCK 0x94
|
|
||||||
#define ALAUDA_BULK_ERASE_BLOCK 0xa3
|
|
||||||
#define ALAUDA_BULK_WRITE_PAGE 0xa4
|
|
||||||
#define ALAUDA_BULK_WRITE_BLOCK 0xb4
|
|
||||||
#define ALAUDA_BULK_RESET_MEDIA 0xe0
|
|
||||||
|
|
||||||
/* Address shifting */
|
|
||||||
#define PBA_LO(pba) ((pba & 0xF) << 5)
|
|
||||||
#define PBA_HI(pba) (pba >> 3)
|
|
||||||
#define PBA_ZONE(pba) (pba >> 11)
|
|
||||||
|
|
||||||
#define TIMEOUT HZ
|
|
||||||
|
|
||||||
static const struct usb_device_id alauda_table[] = {
|
|
||||||
{ USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */
|
|
||||||
{ USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(usb, alauda_table);
|
|
||||||
|
|
||||||
struct alauda_card {
|
|
||||||
u8 id; /* id byte */
|
|
||||||
u8 chipshift; /* 1<<chipshift total size */
|
|
||||||
u8 pageshift; /* 1<<pageshift page size */
|
|
||||||
u8 blockshift; /* 1<<blockshift block size */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct alauda {
|
|
||||||
struct usb_device *dev;
|
|
||||||
struct usb_interface *interface;
|
|
||||||
struct mtd_info *mtd;
|
|
||||||
struct alauda_card *card;
|
|
||||||
struct mutex card_mutex;
|
|
||||||
u32 pagemask;
|
|
||||||
u32 bytemask;
|
|
||||||
u32 blockmask;
|
|
||||||
unsigned int write_out;
|
|
||||||
unsigned int bulk_in;
|
|
||||||
unsigned int bulk_out;
|
|
||||||
u8 port;
|
|
||||||
struct kref kref;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct alauda_card alauda_card_ids[] = {
|
|
||||||
/* NAND flash */
|
|
||||||
{ 0x6e, 20, 8, 12}, /* 1 MB */
|
|
||||||
{ 0xe8, 20, 8, 12}, /* 1 MB */
|
|
||||||
{ 0xec, 20, 8, 12}, /* 1 MB */
|
|
||||||
{ 0x64, 21, 8, 12}, /* 2 MB */
|
|
||||||
{ 0xea, 21, 8, 12}, /* 2 MB */
|
|
||||||
{ 0x6b, 22, 9, 13}, /* 4 MB */
|
|
||||||
{ 0xe3, 22, 9, 13}, /* 4 MB */
|
|
||||||
{ 0xe5, 22, 9, 13}, /* 4 MB */
|
|
||||||
{ 0xe6, 23, 9, 13}, /* 8 MB */
|
|
||||||
{ 0x73, 24, 9, 14}, /* 16 MB */
|
|
||||||
{ 0x75, 25, 9, 14}, /* 32 MB */
|
|
||||||
{ 0x76, 26, 9, 14}, /* 64 MB */
|
|
||||||
{ 0x79, 27, 9, 14}, /* 128 MB */
|
|
||||||
{ 0x71, 28, 9, 14}, /* 256 MB */
|
|
||||||
|
|
||||||
/* MASK ROM */
|
|
||||||
{ 0x5d, 21, 9, 13}, /* 2 MB */
|
|
||||||
{ 0xd5, 22, 9, 13}, /* 4 MB */
|
|
||||||
{ 0xd6, 23, 9, 13}, /* 8 MB */
|
|
||||||
{ 0x57, 24, 9, 13}, /* 16 MB */
|
|
||||||
{ 0x58, 25, 9, 13}, /* 32 MB */
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct alauda_card *get_card(u8 id)
|
|
||||||
{
|
|
||||||
struct alauda_card *card;
|
|
||||||
|
|
||||||
for (card = alauda_card_ids; card->id; card++)
|
|
||||||
if (card->id == id)
|
|
||||||
return card;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void alauda_delete(struct kref *kref)
|
|
||||||
{
|
|
||||||
struct alauda *al = container_of(kref, struct alauda, kref);
|
|
||||||
|
|
||||||
if (al->mtd) {
|
|
||||||
mtd_device_unregister(al->mtd);
|
|
||||||
kfree(al->mtd);
|
|
||||||
}
|
|
||||||
usb_put_dev(al->dev);
|
|
||||||
kfree(al);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_get_media_status(struct alauda *al, void *buf)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
mutex_lock(&al->card_mutex);
|
|
||||||
ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
|
|
||||||
ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ);
|
|
||||||
mutex_unlock(&al->card_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_ack_media(struct alauda *al)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
mutex_lock(&al->card_mutex);
|
|
||||||
ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0),
|
|
||||||
ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ);
|
|
||||||
mutex_unlock(&al->card_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_get_media_signatures(struct alauda *al, void *buf)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
mutex_lock(&al->card_mutex);
|
|
||||||
ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
|
|
||||||
ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ);
|
|
||||||
mutex_unlock(&al->card_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void alauda_reset(struct alauda *al)
|
|
||||||
{
|
|
||||||
u8 command[] = {
|
|
||||||
ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0,
|
|
||||||
0, 0, 0, 0, al->port
|
|
||||||
};
|
|
||||||
mutex_lock(&al->card_mutex);
|
|
||||||
usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ);
|
|
||||||
mutex_unlock(&al->card_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void correct_data(void *buf, void *read_ecc,
|
|
||||||
int *corrected, int *uncorrected)
|
|
||||||
{
|
|
||||||
u8 calc_ecc[3];
|
|
||||||
int err;
|
|
||||||
|
|
||||||
nand_calculate_ecc(NULL, buf, calc_ecc);
|
|
||||||
err = nand_correct_data(NULL, buf, read_ecc, calc_ecc);
|
|
||||||
if (err) {
|
|
||||||
if (err > 0)
|
|
||||||
(*corrected)++;
|
|
||||||
else
|
|
||||||
(*uncorrected)++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct alauda_sg_request {
|
|
||||||
struct urb *urb[3];
|
|
||||||
struct completion comp;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void alauda_complete(struct urb *urb)
|
|
||||||
{
|
|
||||||
struct completion *comp = urb->context;
|
|
||||||
|
|
||||||
if (comp)
|
|
||||||
complete(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf,
|
|
||||||
void *oob)
|
|
||||||
{
|
|
||||||
struct alauda_sg_request sg;
|
|
||||||
struct alauda *al = mtd->priv;
|
|
||||||
u32 pba = from >> al->card->blockshift;
|
|
||||||
u32 page = (from >> al->card->pageshift) & al->pagemask;
|
|
||||||
u8 command[] = {
|
|
||||||
ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba),
|
|
||||||
PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port
|
|
||||||
};
|
|
||||||
int i, err;
|
|
||||||
|
|
||||||
for (i=0; i<3; i++)
|
|
||||||
sg.urb[i] = NULL;
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
|
|
||||||
if (!sg.urb[i])
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
init_completion(&sg.comp);
|
|
||||||
usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
|
|
||||||
alauda_complete, NULL);
|
|
||||||
usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize,
|
|
||||||
alauda_complete, NULL);
|
|
||||||
usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16,
|
|
||||||
alauda_complete, &sg.comp);
|
|
||||||
|
|
||||||
mutex_lock(&al->card_mutex);
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
err = usb_submit_urb(sg.urb[i], GFP_NOIO);
|
|
||||||
if (err)
|
|
||||||
goto cancel;
|
|
||||||
}
|
|
||||||
if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
|
|
||||||
err = -ETIMEDOUT;
|
|
||||||
cancel:
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
usb_kill_urb(sg.urb[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&al->card_mutex);
|
|
||||||
|
|
||||||
out:
|
|
||||||
usb_free_urb(sg.urb[0]);
|
|
||||||
usb_free_urb(sg.urb[1]);
|
|
||||||
usb_free_urb(sg.urb[2]);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_read_page(struct mtd_info *mtd, loff_t from,
|
|
||||||
void *buf, u8 *oob, int *corrected, int *uncorrected)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = __alauda_read_page(mtd, from, buf, oob);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
correct_data(buf, oob+13, corrected, uncorrected);
|
|
||||||
correct_data(buf+256, oob+8, corrected, uncorrected);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf,
|
|
||||||
void *oob)
|
|
||||||
{
|
|
||||||
struct alauda_sg_request sg;
|
|
||||||
struct alauda *al = mtd->priv;
|
|
||||||
u32 pba = to >> al->card->blockshift;
|
|
||||||
u32 page = (to >> al->card->pageshift) & al->pagemask;
|
|
||||||
u8 command[] = {
|
|
||||||
ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba),
|
|
||||||
PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port
|
|
||||||
};
|
|
||||||
int i, err;
|
|
||||||
|
|
||||||
for (i=0; i<3; i++)
|
|
||||||
sg.urb[i] = NULL;
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
|
|
||||||
if (!sg.urb[i])
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
init_completion(&sg.comp);
|
|
||||||
usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
|
|
||||||
alauda_complete, NULL);
|
|
||||||
usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize,
|
|
||||||
alauda_complete, NULL);
|
|
||||||
usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16,
|
|
||||||
alauda_complete, &sg.comp);
|
|
||||||
|
|
||||||
mutex_lock(&al->card_mutex);
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
err = usb_submit_urb(sg.urb[i], GFP_NOIO);
|
|
||||||
if (err)
|
|
||||||
goto cancel;
|
|
||||||
}
|
|
||||||
if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
|
|
||||||
err = -ETIMEDOUT;
|
|
||||||
cancel:
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
usb_kill_urb(sg.urb[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&al->card_mutex);
|
|
||||||
|
|
||||||
out:
|
|
||||||
usb_free_urb(sg.urb[0]);
|
|
||||||
usb_free_urb(sg.urb[1]);
|
|
||||||
usb_free_urb(sg.urb[2]);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs)
|
|
||||||
{
|
|
||||||
struct alauda_sg_request sg;
|
|
||||||
struct alauda *al = mtd->priv;
|
|
||||||
u32 pba = ofs >> al->card->blockshift;
|
|
||||||
u8 command[] = {
|
|
||||||
ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba),
|
|
||||||
PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port
|
|
||||||
};
|
|
||||||
u8 buf[2];
|
|
||||||
int i, err;
|
|
||||||
|
|
||||||
for (i=0; i<2; i++)
|
|
||||||
sg.urb[i] = NULL;
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
|
|
||||||
if (!sg.urb[i])
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
init_completion(&sg.comp);
|
|
||||||
usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
|
|
||||||
alauda_complete, NULL);
|
|
||||||
usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2,
|
|
||||||
alauda_complete, &sg.comp);
|
|
||||||
|
|
||||||
mutex_lock(&al->card_mutex);
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
err = usb_submit_urb(sg.urb[i], GFP_NOIO);
|
|
||||||
if (err)
|
|
||||||
goto cancel;
|
|
||||||
}
|
|
||||||
if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
|
|
||||||
err = -ETIMEDOUT;
|
|
||||||
cancel:
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
usb_kill_urb(sg.urb[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&al->card_mutex);
|
|
||||||
|
|
||||||
out:
|
|
||||||
usb_free_urb(sg.urb[0]);
|
|
||||||
usb_free_urb(sg.urb[1]);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob)
|
|
||||||
{
|
|
||||||
static u8 ignore_buf[512]; /* write only */
|
|
||||||
|
|
||||||
return __alauda_read_page(mtd, from, ignore_buf, oob);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_isbad(struct mtd_info *mtd, loff_t ofs)
|
|
||||||
{
|
|
||||||
u8 oob[16];
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = alauda_read_oob(mtd, ofs, oob);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* A block is marked bad if two or more bits are zero */
|
|
||||||
return hweight8(oob[5]) >= 7 ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
||||||
size_t *retlen, u_char *buf)
|
|
||||||
{
|
|
||||||
struct alauda *al = mtd->priv;
|
|
||||||
void *bounce_buf;
|
|
||||||
int err, corrected=0, uncorrected=0;
|
|
||||||
|
|
||||||
bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL);
|
|
||||||
if (!bounce_buf)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*retlen = len;
|
|
||||||
while (len) {
|
|
||||||
u8 oob[16];
|
|
||||||
size_t byte = from & al->bytemask;
|
|
||||||
size_t cplen = min(len, mtd->writesize - byte);
|
|
||||||
|
|
||||||
err = alauda_read_page(mtd, from, bounce_buf, oob,
|
|
||||||
&corrected, &uncorrected);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
memcpy(buf, bounce_buf + byte, cplen);
|
|
||||||
buf += cplen;
|
|
||||||
from += cplen;
|
|
||||||
len -= cplen;
|
|
||||||
}
|
|
||||||
err = 0;
|
|
||||||
if (corrected)
|
|
||||||
err = 1; /* return max_bitflips per ecc step */
|
|
||||||
if (uncorrected)
|
|
||||||
err = -EBADMSG;
|
|
||||||
out:
|
|
||||||
kfree(bounce_buf);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
||||||
size_t *retlen, u_char *buf)
|
|
||||||
{
|
|
||||||
struct alauda *al = mtd->priv;
|
|
||||||
int err, corrected=0, uncorrected=0;
|
|
||||||
|
|
||||||
if ((from & al->bytemask) || (len & al->bytemask))
|
|
||||||
return alauda_bounce_read(mtd, from, len, retlen, buf);
|
|
||||||
|
|
||||||
*retlen = len;
|
|
||||||
while (len) {
|
|
||||||
u8 oob[16];
|
|
||||||
|
|
||||||
err = alauda_read_page(mtd, from, buf, oob,
|
|
||||||
&corrected, &uncorrected);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
buf += mtd->writesize;
|
|
||||||
from += mtd->writesize;
|
|
||||||
len -= mtd->writesize;
|
|
||||||
}
|
|
||||||
err = 0;
|
|
||||||
if (corrected)
|
|
||||||
err = 1; /* return max_bitflips per ecc step */
|
|
||||||
if (uncorrected)
|
|
||||||
err = -EBADMSG;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|
||||||
size_t *retlen, const u_char *buf)
|
|
||||||
{
|
|
||||||
struct alauda *al = mtd->priv;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if ((to & al->bytemask) || (len & al->bytemask))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
*retlen = len;
|
|
||||||
while (len) {
|
|
||||||
u32 page = (to >> al->card->pageshift) & al->pagemask;
|
|
||||||
u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
||||||
|
|
||||||
/* don't write to bad blocks */
|
|
||||||
if (page == 0) {
|
|
||||||
err = alauda_isbad(mtd, to);
|
|
||||||
if (err) {
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nand_calculate_ecc(mtd, buf, &oob[13]);
|
|
||||||
nand_calculate_ecc(mtd, buf+256, &oob[8]);
|
|
||||||
|
|
||||||
err = alauda_write_page(mtd, to, (void*)buf, oob);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
buf += mtd->writesize;
|
|
||||||
to += mtd->writesize;
|
|
||||||
len -= mtd->writesize;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|
||||||
{
|
|
||||||
struct alauda *al = mtd->priv;
|
|
||||||
u32 ofs = instr->addr;
|
|
||||||
u32 len = instr->len;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if ((ofs & al->blockmask) || (len & al->blockmask))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
while (len) {
|
|
||||||
/* don't erase bad blocks */
|
|
||||||
err = alauda_isbad(mtd, ofs);
|
|
||||||
if (err > 0)
|
|
||||||
err = -EIO;
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
err = alauda_erase_block(mtd, ofs);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
ofs += mtd->erasesize;
|
|
||||||
len -= mtd->erasesize;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = __alauda_erase(mtd, instr);
|
|
||||||
instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_init_media(struct alauda *al)
|
|
||||||
{
|
|
||||||
u8 buf[4], *b0=buf, *b1=buf+1;
|
|
||||||
struct alauda_card *card;
|
|
||||||
struct mtd_info *mtd;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
|
|
||||||
if (!mtd)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
err = alauda_get_media_status(al, buf);
|
|
||||||
if (err < 0)
|
|
||||||
goto error;
|
|
||||||
if (*b0 & 0x10)
|
|
||||||
break;
|
|
||||||
msleep(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = alauda_ack_media(al);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
msleep(10);
|
|
||||||
|
|
||||||
err = alauda_get_media_status(al, buf);
|
|
||||||
if (err < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (*b0 != 0x14) {
|
|
||||||
/* media not ready */
|
|
||||||
err = -EIO;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
err = alauda_get_media_signatures(al, buf);
|
|
||||||
if (err < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
card = get_card(*b1);
|
|
||||||
if (!card) {
|
|
||||||
printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1);
|
|
||||||
err = -EIO;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n",
|
|
||||||
1<<card->pageshift, 1<<card->blockshift,
|
|
||||||
1<<(card->chipshift-20));
|
|
||||||
al->card = card;
|
|
||||||
al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1;
|
|
||||||
al->bytemask = (1 << card->pageshift) - 1;
|
|
||||||
al->blockmask = (1 << card->blockshift) - 1;
|
|
||||||
|
|
||||||
mtd->name = "alauda";
|
|
||||||
mtd->size = 1<<card->chipshift;
|
|
||||||
mtd->erasesize = 1<<card->blockshift;
|
|
||||||
mtd->writesize = 1<<card->pageshift;
|
|
||||||
mtd->type = MTD_NANDFLASH;
|
|
||||||
mtd->flags = MTD_CAP_NANDFLASH;
|
|
||||||
mtd->_read = alauda_read;
|
|
||||||
mtd->_write = alauda_write;
|
|
||||||
mtd->_erase = alauda_erase;
|
|
||||||
mtd->_block_isbad = alauda_isbad;
|
|
||||||
mtd->priv = al;
|
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
mtd->ecc_strength = 1;
|
|
||||||
|
|
||||||
err = mtd_device_register(mtd, NULL, 0);
|
|
||||||
if (err) {
|
|
||||||
err = -ENFILE;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
al->mtd = mtd;
|
|
||||||
alauda_reset(al); /* no clue whether this is necessary */
|
|
||||||
return 0;
|
|
||||||
error:
|
|
||||||
kfree(mtd);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_check_media(struct alauda *al)
|
|
||||||
{
|
|
||||||
u8 buf[2], *b0 = buf, *b1 = buf+1;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = alauda_get_media_status(al, buf);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
if ((*b1 & 0x01) == 0) {
|
|
||||||
/* door open */
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) {
|
|
||||||
/* no media ? */
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
if (*b0 & 0x08) {
|
|
||||||
/* media change ? */
|
|
||||||
return alauda_init_media(al);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_probe(struct usb_interface *interface,
|
|
||||||
const struct usb_device_id *id)
|
|
||||||
{
|
|
||||||
struct alauda *al;
|
|
||||||
struct usb_host_interface *iface;
|
|
||||||
struct usb_endpoint_descriptor *ep,
|
|
||||||
*ep_in=NULL, *ep_out=NULL, *ep_wr=NULL;
|
|
||||||
int i, err = -ENOMEM;
|
|
||||||
|
|
||||||
al = kzalloc(2*sizeof(*al), GFP_KERNEL);
|
|
||||||
if (!al)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
kref_init(&al->kref);
|
|
||||||
usb_set_intfdata(interface, al);
|
|
||||||
|
|
||||||
al->dev = usb_get_dev(interface_to_usbdev(interface));
|
|
||||||
al->interface = interface;
|
|
||||||
|
|
||||||
iface = interface->cur_altsetting;
|
|
||||||
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
|
|
||||||
ep = &iface->endpoint[i].desc;
|
|
||||||
|
|
||||||
if (usb_endpoint_is_bulk_in(ep)) {
|
|
||||||
ep_in = ep;
|
|
||||||
} else if (usb_endpoint_is_bulk_out(ep)) {
|
|
||||||
if (i==0)
|
|
||||||
ep_wr = ep;
|
|
||||||
else
|
|
||||||
ep_out = ep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = -EIO;
|
|
||||||
if (!ep_wr || !ep_in || !ep_out)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
al->write_out = usb_sndbulkpipe(al->dev,
|
|
||||||
usb_endpoint_num(ep_wr));
|
|
||||||
al->bulk_in = usb_rcvbulkpipe(al->dev,
|
|
||||||
usb_endpoint_num(ep_in));
|
|
||||||
al->bulk_out = usb_sndbulkpipe(al->dev,
|
|
||||||
usb_endpoint_num(ep_out));
|
|
||||||
|
|
||||||
/* second device is identical up to now */
|
|
||||||
memcpy(al+1, al, sizeof(*al));
|
|
||||||
|
|
||||||
mutex_init(&al[0].card_mutex);
|
|
||||||
mutex_init(&al[1].card_mutex);
|
|
||||||
|
|
||||||
al[0].port = ALAUDA_PORT_XD;
|
|
||||||
al[1].port = ALAUDA_PORT_SM;
|
|
||||||
|
|
||||||
dev_info(&interface->dev, "alauda probed\n");
|
|
||||||
alauda_check_media(al);
|
|
||||||
alauda_check_media(al+1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (al)
|
|
||||||
kref_put(&al->kref, alauda_delete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void alauda_disconnect(struct usb_interface *interface)
|
|
||||||
{
|
|
||||||
struct alauda *al;
|
|
||||||
|
|
||||||
al = usb_get_intfdata(interface);
|
|
||||||
usb_set_intfdata(interface, NULL);
|
|
||||||
|
|
||||||
/* FIXME: prevent more I/O from starting */
|
|
||||||
|
|
||||||
/* decrement our usage count */
|
|
||||||
if (al)
|
|
||||||
kref_put(&al->kref, alauda_delete);
|
|
||||||
|
|
||||||
dev_info(&interface->dev, "alauda gone");
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct usb_driver alauda_driver = {
|
|
||||||
.name = "alauda",
|
|
||||||
.probe = alauda_probe,
|
|
||||||
.disconnect = alauda_disconnect,
|
|
||||||
.id_table = alauda_table,
|
|
||||||
};
|
|
||||||
|
|
||||||
module_usb_driver(alauda_driver);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
|
@ -258,7 +258,6 @@ static int ams_delta_init(struct platform_device *pdev)
|
||||||
out_mtd:
|
out_mtd:
|
||||||
gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
|
gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
|
||||||
out_gpio:
|
out_gpio:
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
|
gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
|
||||||
iounmap(io_base);
|
iounmap(io_base);
|
||||||
out_free:
|
out_free:
|
||||||
|
|
File diff suppressed because it is too large
Load diff
98
drivers/mtd/nand/atmel_nand_nfc.h
Normal file
98
drivers/mtd/nand/atmel_nand_nfc.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Atmel Nand Flash Controller (NFC) - System peripherals regsters.
|
||||||
|
* Based on SAMA5D3 datasheet.
|
||||||
|
*
|
||||||
|
* © Copyright 2013 Atmel Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ATMEL_NAND_NFC_H
|
||||||
|
#define ATMEL_NAND_NFC_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HSMC NFC registers
|
||||||
|
*/
|
||||||
|
#define ATMEL_HSMC_NFC_CFG 0x00 /* NFC Configuration Register */
|
||||||
|
#define NFC_CFG_PAGESIZE (7 << 0)
|
||||||
|
#define NFC_CFG_PAGESIZE_512 (0 << 0)
|
||||||
|
#define NFC_CFG_PAGESIZE_1024 (1 << 0)
|
||||||
|
#define NFC_CFG_PAGESIZE_2048 (2 << 0)
|
||||||
|
#define NFC_CFG_PAGESIZE_4096 (3 << 0)
|
||||||
|
#define NFC_CFG_PAGESIZE_8192 (4 << 0)
|
||||||
|
#define NFC_CFG_WSPARE (1 << 8)
|
||||||
|
#define NFC_CFG_RSPARE (1 << 9)
|
||||||
|
#define NFC_CFG_NFC_DTOCYC (0xf << 16)
|
||||||
|
#define NFC_CFG_NFC_DTOMUL (0x7 << 20)
|
||||||
|
#define NFC_CFG_NFC_SPARESIZE (0x7f << 24)
|
||||||
|
#define NFC_CFG_NFC_SPARESIZE_BIT_POS 24
|
||||||
|
|
||||||
|
#define ATMEL_HSMC_NFC_CTRL 0x04 /* NFC Control Register */
|
||||||
|
#define NFC_CTRL_ENABLE (1 << 0)
|
||||||
|
#define NFC_CTRL_DISABLE (1 << 1)
|
||||||
|
|
||||||
|
#define ATMEL_HSMC_NFC_SR 0x08 /* NFC Status Register */
|
||||||
|
#define NFC_SR_XFR_DONE (1 << 16)
|
||||||
|
#define NFC_SR_CMD_DONE (1 << 17)
|
||||||
|
#define NFC_SR_RB_EDGE (1 << 24)
|
||||||
|
|
||||||
|
#define ATMEL_HSMC_NFC_IER 0x0c
|
||||||
|
#define ATMEL_HSMC_NFC_IDR 0x10
|
||||||
|
#define ATMEL_HSMC_NFC_IMR 0x14
|
||||||
|
#define ATMEL_HSMC_NFC_CYCLE0 0x18 /* NFC Address Cycle Zero */
|
||||||
|
#define ATMEL_HSMC_NFC_ADDR_CYCLE0 (0xff)
|
||||||
|
|
||||||
|
#define ATMEL_HSMC_NFC_BANK 0x1c /* NFC Bank Register */
|
||||||
|
#define ATMEL_HSMC_NFC_BANK0 (0 << 0)
|
||||||
|
#define ATMEL_HSMC_NFC_BANK1 (1 << 0)
|
||||||
|
|
||||||
|
#define nfc_writel(addr, reg, value) \
|
||||||
|
writel((value), (addr) + ATMEL_HSMC_NFC_##reg)
|
||||||
|
|
||||||
|
#define nfc_readl(addr, reg) \
|
||||||
|
readl_relaxed((addr) + ATMEL_HSMC_NFC_##reg)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NFC Address Command definitions
|
||||||
|
*/
|
||||||
|
#define NFCADDR_CMD_CMD1 (0xff << 2) /* Command for Cycle 1 */
|
||||||
|
#define NFCADDR_CMD_CMD1_BIT_POS 2
|
||||||
|
#define NFCADDR_CMD_CMD2 (0xff << 10) /* Command for Cycle 2 */
|
||||||
|
#define NFCADDR_CMD_CMD2_BIT_POS 10
|
||||||
|
#define NFCADDR_CMD_VCMD2 (0x1 << 18) /* Valid Cycle 2 Command */
|
||||||
|
#define NFCADDR_CMD_ACYCLE (0x7 << 19) /* Number of Address required */
|
||||||
|
#define NFCADDR_CMD_ACYCLE_NONE (0x0 << 19)
|
||||||
|
#define NFCADDR_CMD_ACYCLE_1 (0x1 << 19)
|
||||||
|
#define NFCADDR_CMD_ACYCLE_2 (0x2 << 19)
|
||||||
|
#define NFCADDR_CMD_ACYCLE_3 (0x3 << 19)
|
||||||
|
#define NFCADDR_CMD_ACYCLE_4 (0x4 << 19)
|
||||||
|
#define NFCADDR_CMD_ACYCLE_5 (0x5 << 19)
|
||||||
|
#define NFCADDR_CMD_ACYCLE_BIT_POS 19
|
||||||
|
#define NFCADDR_CMD_CSID (0x7 << 22) /* Chip Select Identifier */
|
||||||
|
#define NFCADDR_CMD_CSID_0 (0x0 << 22)
|
||||||
|
#define NFCADDR_CMD_CSID_1 (0x1 << 22)
|
||||||
|
#define NFCADDR_CMD_CSID_2 (0x2 << 22)
|
||||||
|
#define NFCADDR_CMD_CSID_3 (0x3 << 22)
|
||||||
|
#define NFCADDR_CMD_CSID_4 (0x4 << 22)
|
||||||
|
#define NFCADDR_CMD_CSID_5 (0x5 << 22)
|
||||||
|
#define NFCADDR_CMD_CSID_6 (0x6 << 22)
|
||||||
|
#define NFCADDR_CMD_CSID_7 (0x7 << 22)
|
||||||
|
#define NFCADDR_CMD_DATAEN (0x1 << 25) /* Data Transfer Enable */
|
||||||
|
#define NFCADDR_CMD_DATADIS (0x0 << 25) /* Data Transfer Disable */
|
||||||
|
#define NFCADDR_CMD_NFCRD (0x0 << 26) /* NFC Read Enable */
|
||||||
|
#define NFCADDR_CMD_NFCWR (0x1 << 26) /* NFC Write Enable */
|
||||||
|
#define NFCADDR_CMD_NFCBUSY (0x1 << 27) /* NFC Busy */
|
||||||
|
|
||||||
|
#define nfc_cmd_addr1234_writel(cmd, addr1234, nfc_base) \
|
||||||
|
writel((addr1234), (cmd) + nfc_base)
|
||||||
|
|
||||||
|
#define nfc_cmd_readl(bitstatus, nfc_base) \
|
||||||
|
readl_relaxed((bitstatus) + nfc_base)
|
||||||
|
|
||||||
|
#define NFC_TIME_OUT_MS 100
|
||||||
|
#define NFC_SRAM_BANK1_OFFSET 0x1200
|
||||||
|
|
||||||
|
#endif
|
|
@ -411,7 +411,7 @@ static int au1550nd_probe(struct platform_device *pdev)
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
int ret, cs;
|
int ret, cs;
|
||||||
|
|
||||||
pd = pdev->dev.platform_data;
|
pd = dev_get_platdata(&pdev->dev);
|
||||||
if (!pd) {
|
if (!pd) {
|
||||||
dev_err(&pdev->dev, "missing platform data\n");
|
dev_err(&pdev->dev, "missing platform data\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -171,7 +171,7 @@ static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
|
||||||
|
|
||||||
static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
|
static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
return pdev->dev.platform_data;
|
return dev_get_platdata(&pdev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -671,8 +671,6 @@ static int bf5xx_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct bf5xx_nand_info *info = to_nand_info(pdev);
|
struct bf5xx_nand_info *info = to_nand_info(pdev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
/* first thing we need to do is release all our mtds
|
/* first thing we need to do is release all our mtds
|
||||||
* and their partitions, then go through freeing the
|
* and their partitions, then go through freeing the
|
||||||
* resources used
|
* resources used
|
||||||
|
@ -832,7 +830,6 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
||||||
out_err_nand_scan:
|
out_err_nand_scan:
|
||||||
bf5xx_nand_dma_remove(info);
|
bf5xx_nand_dma_remove(info);
|
||||||
out_err_hw_init:
|
out_err_hw_init:
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
out_err_kzalloc:
|
out_err_kzalloc:
|
||||||
peripheral_free_list(bfin_nfc_pin_req);
|
peripheral_free_list(bfin_nfc_pin_req);
|
||||||
|
|
|
@ -197,7 +197,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for MTD device structure and private data */
|
/* Allocate memory for MTD device structure and private data */
|
||||||
new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
new_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||||
if (!new_mtd) {
|
if (!new_mtd) {
|
||||||
printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n");
|
printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n");
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -207,10 +207,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||||
/* Get pointer to private data */
|
/* Get pointer to private data */
|
||||||
this = (struct nand_chip *)(&new_mtd[1]);
|
this = (struct nand_chip *)(&new_mtd[1]);
|
||||||
|
|
||||||
/* Initialize structures */
|
|
||||||
memset(new_mtd, 0, sizeof(struct mtd_info));
|
|
||||||
memset(this, 0, sizeof(struct nand_chip));
|
|
||||||
|
|
||||||
/* Link the private data with the MTD structure */
|
/* Link the private data with the MTD structure */
|
||||||
new_mtd->priv = this;
|
new_mtd->priv = this;
|
||||||
new_mtd->owner = THIS_MODULE;
|
new_mtd->owner = THIS_MODULE;
|
||||||
|
|
|
@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, davinci_nand_of_match);
|
||||||
static struct davinci_nand_pdata
|
static struct davinci_nand_pdata
|
||||||
*nand_davinci_get_pdata(struct platform_device *pdev)
|
*nand_davinci_get_pdata(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
if (!pdev->dev.platform_data && pdev->dev.of_node) {
|
if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) {
|
||||||
struct davinci_nand_pdata *pdata;
|
struct davinci_nand_pdata *pdata;
|
||||||
const char *mode;
|
const char *mode;
|
||||||
u32 prop;
|
u32 prop;
|
||||||
|
@ -575,13 +575,13 @@ static struct davinci_nand_pdata
|
||||||
pdata->bbt_options = NAND_BBT_USE_FLASH;
|
pdata->bbt_options = NAND_BBT_USE_FLASH;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pdev->dev.platform_data;
|
return dev_get_platdata(&pdev->dev);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static struct davinci_nand_pdata
|
static struct davinci_nand_pdata
|
||||||
*nand_davinci_get_pdata(struct platform_device *pdev)
|
*nand_davinci_get_pdata(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
return pdev->dev.platform_data;
|
return dev_get_platdata(&pdev->dev);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -623,11 +623,14 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||||
goto err_nomem;
|
goto err_nomem;
|
||||||
}
|
}
|
||||||
|
|
||||||
vaddr = devm_request_and_ioremap(&pdev->dev, res1);
|
vaddr = devm_ioremap_resource(&pdev->dev, res1);
|
||||||
base = devm_request_and_ioremap(&pdev->dev, res2);
|
if (IS_ERR(vaddr)) {
|
||||||
if (!vaddr || !base) {
|
ret = PTR_ERR(vaddr);
|
||||||
dev_err(&pdev->dev, "ioremap failed\n");
|
goto err_ioremap;
|
||||||
ret = -EADDRNOTAVAIL;
|
}
|
||||||
|
base = devm_ioremap_resource(&pdev->dev, res2);
|
||||||
|
if (IS_ERR(base)) {
|
||||||
|
ret = PTR_ERR(base);
|
||||||
goto err_ioremap;
|
goto err_ioremap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1520,7 +1520,7 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
* so just let controller do 15bit ECC for MLC and 8bit ECC for
|
* so just let controller do 15bit ECC for MLC and 8bit ECC for
|
||||||
* SLC if possible.
|
* SLC if possible.
|
||||||
* */
|
* */
|
||||||
if (denali->nand.cellinfo & 0xc &&
|
if (denali->nand.cellinfo & NAND_CI_CELLTYPE_MSK &&
|
||||||
(denali->mtd.oobsize > (denali->bbtskipbytes +
|
(denali->mtd.oobsize > (denali->bbtskipbytes +
|
||||||
ECC_15BITS * (denali->mtd.writesize /
|
ECC_15BITS * (denali->mtd.writesize /
|
||||||
ECC_SECTOR_SIZE)))) {
|
ECC_SECTOR_SIZE)))) {
|
||||||
|
|
|
@ -46,13 +46,13 @@ static unsigned long __initdata doc_locations[] = {
|
||||||
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
|
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
|
||||||
0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
|
0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
|
||||||
0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
|
0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
|
||||||
#else /* CONFIG_MTD_DOCPROBE_HIGH */
|
#else
|
||||||
0xc8000, 0xca000, 0xcc000, 0xce000,
|
0xc8000, 0xca000, 0xcc000, 0xce000,
|
||||||
0xd0000, 0xd2000, 0xd4000, 0xd6000,
|
0xd0000, 0xd2000, 0xd4000, 0xd6000,
|
||||||
0xd8000, 0xda000, 0xdc000, 0xde000,
|
0xd8000, 0xda000, 0xdc000, 0xde000,
|
||||||
0xe0000, 0xe2000, 0xe4000, 0xe6000,
|
0xe0000, 0xe2000, 0xe4000, 0xe6000,
|
||||||
0xe8000, 0xea000, 0xec000, 0xee000,
|
0xe8000, 0xea000, 0xec000, 0xee000,
|
||||||
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
|
#endif
|
||||||
#endif
|
#endif
|
||||||
0xffffffff };
|
0xffffffff };
|
||||||
|
|
||||||
|
|
|
@ -1093,7 +1093,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
struct nand_chip *nand = mtd->priv;
|
struct nand_chip *nand = mtd->priv;
|
||||||
struct docg4_priv *doc = nand->priv;
|
struct docg4_priv *doc = nand->priv;
|
||||||
struct nand_bbt_descr *bbtd = nand->badblock_pattern;
|
struct nand_bbt_descr *bbtd = nand->badblock_pattern;
|
||||||
int block = (int)(ofs >> nand->bbt_erase_shift);
|
|
||||||
int page = (int)(ofs >> nand->page_shift);
|
int page = (int)(ofs >> nand->page_shift);
|
||||||
uint32_t g4_addr = mtd_to_docg4_address(page, 0);
|
uint32_t g4_addr = mtd_to_docg4_address(page, 0);
|
||||||
|
|
||||||
|
@ -1108,9 +1107,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* update bbt in memory */
|
|
||||||
nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2);
|
|
||||||
|
|
||||||
/* write bit-wise negation of pattern to oob buffer */
|
/* write bit-wise negation of pattern to oob buffer */
|
||||||
memset(nand->oob_poi, 0xff, mtd->oobsize);
|
memset(nand->oob_poi, 0xff, mtd->oobsize);
|
||||||
for (i = 0; i < bbtd->len; i++)
|
for (i = 0; i < bbtd->len; i++)
|
||||||
|
@ -1120,8 +1116,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
write_page_prologue(mtd, g4_addr);
|
write_page_prologue(mtd, g4_addr);
|
||||||
docg4_write_page(mtd, nand, buf, 1);
|
docg4_write_page(mtd, nand, buf, 1);
|
||||||
ret = pageprog(mtd);
|
ret = pageprog(mtd);
|
||||||
if (!ret)
|
|
||||||
mtd->ecc_stats.badblocks++;
|
|
||||||
|
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
|
|
||||||
|
@ -1368,7 +1362,6 @@ static int __init probe_docg4(struct platform_device *pdev)
|
||||||
struct nand_chip *nand = mtd->priv;
|
struct nand_chip *nand = mtd->priv;
|
||||||
struct docg4_priv *doc = nand->priv;
|
struct docg4_priv *doc = nand->priv;
|
||||||
nand_release(mtd); /* deletes partitions and mtd devices */
|
nand_release(mtd); /* deletes partitions and mtd devices */
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
free_bch(doc->bch);
|
free_bch(doc->bch);
|
||||||
kfree(mtd);
|
kfree(mtd);
|
||||||
}
|
}
|
||||||
|
@ -1380,7 +1373,6 @@ static int __exit cleanup_docg4(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct docg4_priv *doc = platform_get_drvdata(pdev);
|
struct docg4_priv *doc = platform_get_drvdata(pdev);
|
||||||
nand_release(doc->mtd);
|
nand_release(doc->mtd);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
free_bch(doc->bch);
|
free_bch(doc->bch);
|
||||||
kfree(doc->mtd);
|
kfree(doc->mtd);
|
||||||
iounmap(doc->virtadr);
|
iounmap(doc->virtadr);
|
||||||
|
|
|
@ -823,7 +823,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
|
||||||
|
|
||||||
/* set up nand options */
|
/* set up nand options */
|
||||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||||
|
chip->options = NAND_NO_SUBPAGE_WRITE;
|
||||||
|
|
||||||
if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) {
|
if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) {
|
||||||
chip->read_byte = fsl_ifc_read_byte16;
|
chip->read_byte = fsl_ifc_read_byte16;
|
||||||
|
@ -908,7 +908,6 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
|
||||||
|
|
||||||
ifc_nand_ctrl->chips[priv->bank] = NULL;
|
ifc_nand_ctrl->chips[priv->bank] = NULL;
|
||||||
dev_set_drvdata(priv->dev, NULL);
|
dev_set_drvdata(priv->dev, NULL);
|
||||||
kfree(priv);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -889,6 +889,24 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
|
||||||
if (of_get_property(np, "nand-skip-bbtscan", NULL))
|
if (of_get_property(np, "nand-skip-bbtscan", NULL))
|
||||||
pdata->options = NAND_SKIP_BBTSCAN;
|
pdata->options = NAND_SKIP_BBTSCAN;
|
||||||
|
|
||||||
|
pdata->nand_timings = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(*pdata->nand_timings), GFP_KERNEL);
|
||||||
|
if (!pdata->nand_timings) {
|
||||||
|
dev_err(&pdev->dev, "no memory for nand_timing\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings,
|
||||||
|
sizeof(*pdata->nand_timings));
|
||||||
|
|
||||||
|
/* Set default NAND bank to 0 */
|
||||||
|
pdata->bank = 0;
|
||||||
|
if (!of_property_read_u32(np, "bank", &val)) {
|
||||||
|
if (val > 3) {
|
||||||
|
dev_err(&pdev->dev, "invalid bank %u\n", val);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
pdata->bank = val;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -940,9 +958,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
|
||||||
if (!res)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
host->data_va = devm_ioremap_resource(&pdev->dev, res);
|
host->data_va = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->data_va))
|
if (IS_ERR(host->data_va))
|
||||||
return PTR_ERR(host->data_va);
|
return PTR_ERR(host->data_va);
|
||||||
|
@ -950,25 +965,16 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
host->data_pa = (dma_addr_t)res->start;
|
host->data_pa = (dma_addr_t)res->start;
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
|
||||||
if (!res)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
host->addr_va = devm_ioremap_resource(&pdev->dev, res);
|
host->addr_va = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->addr_va))
|
if (IS_ERR(host->addr_va))
|
||||||
return PTR_ERR(host->addr_va);
|
return PTR_ERR(host->addr_va);
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
|
||||||
if (!res)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
host->cmd_va = devm_ioremap_resource(&pdev->dev, res);
|
host->cmd_va = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->cmd_va))
|
if (IS_ERR(host->cmd_va))
|
||||||
return PTR_ERR(host->cmd_va);
|
return PTR_ERR(host->cmd_va);
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
|
||||||
if (!res)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
host->regs_va = devm_ioremap_resource(&pdev->dev, res);
|
host->regs_va = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->regs_va))
|
if (IS_ERR(host->regs_va))
|
||||||
return PTR_ERR(host->regs_va);
|
return PTR_ERR(host->regs_va);
|
||||||
|
@ -1174,8 +1180,6 @@ static int fsmc_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct fsmc_nand_data *host = platform_get_drvdata(pdev);
|
struct fsmc_nand_data *host = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
if (host) {
|
if (host) {
|
||||||
nand_release(&host->mtd);
|
nand_release(&host->mtd);
|
||||||
|
|
||||||
|
@ -1190,7 +1194,7 @@ static int fsmc_nand_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int fsmc_nand_suspend(struct device *dev)
|
static int fsmc_nand_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct fsmc_nand_data *host = dev_get_drvdata(dev);
|
struct fsmc_nand_data *host = dev_get_drvdata(dev);
|
||||||
|
@ -1210,9 +1214,9 @@ static int fsmc_nand_resume(struct device *dev)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
|
static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id fsmc_nand_id_table[] = {
|
static const struct of_device_id fsmc_nand_id_table[] = {
|
||||||
|
@ -1229,9 +1233,7 @@ static struct platform_driver fsmc_nand_driver = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "fsmc-nand",
|
.name = "fsmc-nand",
|
||||||
.of_match_table = of_match_ptr(fsmc_nand_id_table),
|
.of_match_table = of_match_ptr(fsmc_nand_id_table),
|
||||||
#ifdef CONFIG_PM
|
|
||||||
.pm = &fsmc_nand_pm_ops,
|
.pm = &fsmc_nand_pm_ops,
|
||||||
#endif
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -86,59 +87,11 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||||
gpio_nand_dosync(gpiomtd);
|
gpio_nand_dosync(gpiomtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
|
|
||||||
{
|
|
||||||
struct nand_chip *this = mtd->priv;
|
|
||||||
|
|
||||||
iowrite8_rep(this->IO_ADDR_W, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len)
|
|
||||||
{
|
|
||||||
struct nand_chip *this = mtd->priv;
|
|
||||||
|
|
||||||
ioread8_rep(this->IO_ADDR_R, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf,
|
|
||||||
int len)
|
|
||||||
{
|
|
||||||
struct nand_chip *this = mtd->priv;
|
|
||||||
|
|
||||||
if (IS_ALIGNED((unsigned long)buf, 2)) {
|
|
||||||
iowrite16_rep(this->IO_ADDR_W, buf, len>>1);
|
|
||||||
} else {
|
|
||||||
int i;
|
|
||||||
unsigned short *ptr = (unsigned short *)buf;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i += 2, ptr++)
|
|
||||||
writew(*ptr, this->IO_ADDR_W);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len)
|
|
||||||
{
|
|
||||||
struct nand_chip *this = mtd->priv;
|
|
||||||
|
|
||||||
if (IS_ALIGNED((unsigned long)buf, 2)) {
|
|
||||||
ioread16_rep(this->IO_ADDR_R, buf, len>>1);
|
|
||||||
} else {
|
|
||||||
int i;
|
|
||||||
unsigned short *ptr = (unsigned short *)buf;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i += 2, ptr++)
|
|
||||||
*ptr = readw(this->IO_ADDR_R);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
|
|
||||||
return gpio_get_value(gpiomtd->plat.gpio_rdy);
|
return gpio_get_value(gpiomtd->plat.gpio_rdy);
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
|
@ -153,6 +106,9 @@ static int gpio_nand_get_config_of(const struct device *dev,
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
|
if (!dev->of_node)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {
|
if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {
|
||||||
if (val == 2) {
|
if (val == 2) {
|
||||||
plat->options |= NAND_BUSWIDTH_16;
|
plat->options |= NAND_BUSWIDTH_16;
|
||||||
|
@ -211,8 +167,8 @@ static inline int gpio_nand_get_config(const struct device *dev,
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (dev->platform_data) {
|
if (dev_get_platdata(dev)) {
|
||||||
memcpy(plat, dev->platform_data, sizeof(*plat));
|
memcpy(plat, dev_get_platdata(dev), sizeof(*plat));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,145 +186,100 @@ gpio_nand_get_io_sync(struct platform_device *pdev)
|
||||||
return platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
return platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpio_nand_remove(struct platform_device *dev)
|
static int gpio_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct gpiomtd *gpiomtd = platform_get_drvdata(dev);
|
struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);
|
||||||
struct resource *res;
|
|
||||||
|
|
||||||
nand_release(&gpiomtd->mtd_info);
|
nand_release(&gpiomtd->mtd_info);
|
||||||
|
|
||||||
res = gpio_nand_get_io_sync(dev);
|
|
||||||
iounmap(gpiomtd->io_sync);
|
|
||||||
if (res)
|
|
||||||
release_mem_region(res->start, resource_size(res));
|
|
||||||
|
|
||||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
|
||||||
iounmap(gpiomtd->nand_chip.IO_ADDR_R);
|
|
||||||
release_mem_region(res->start, resource_size(res));
|
|
||||||
|
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
||||||
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
|
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
|
||||||
gpio_set_value(gpiomtd->plat.gpio_nce, 1);
|
gpio_set_value(gpiomtd->plat.gpio_nce, 1);
|
||||||
|
|
||||||
gpio_free(gpiomtd->plat.gpio_cle);
|
|
||||||
gpio_free(gpiomtd->plat.gpio_ale);
|
|
||||||
gpio_free(gpiomtd->plat.gpio_nce);
|
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
|
||||||
gpio_free(gpiomtd->plat.gpio_nwp);
|
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
|
|
||||||
gpio_free(gpiomtd->plat.gpio_rdy);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __iomem *request_and_remap(struct resource *res, size_t size,
|
static int gpio_nand_probe(struct platform_device *pdev)
|
||||||
const char *name, int *err)
|
|
||||||
{
|
|
||||||
void __iomem *ptr;
|
|
||||||
|
|
||||||
if (!request_mem_region(res->start, resource_size(res), name)) {
|
|
||||||
*err = -EBUSY;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = ioremap(res->start, size);
|
|
||||||
if (!ptr) {
|
|
||||||
release_mem_region(res->start, resource_size(res));
|
|
||||||
*err = -ENOMEM;
|
|
||||||
}
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gpio_nand_probe(struct platform_device *dev)
|
|
||||||
{
|
{
|
||||||
struct gpiomtd *gpiomtd;
|
struct gpiomtd *gpiomtd;
|
||||||
struct nand_chip *this;
|
struct nand_chip *chip;
|
||||||
struct resource *res0, *res1;
|
struct resource *res;
|
||||||
struct mtd_part_parser_data ppdata = {};
|
struct mtd_part_parser_data ppdata = {};
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!dev->dev.of_node && !dev->dev.platform_data)
|
if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
res0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL);
|
||||||
if (!res0)
|
if (!gpiomtd) {
|
||||||
return -EINVAL;
|
dev_err(&pdev->dev, "failed to create NAND MTD\n");
|
||||||
|
|
||||||
gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL);
|
|
||||||
if (gpiomtd == NULL) {
|
|
||||||
dev_err(&dev->dev, "failed to create NAND MTD\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
this = &gpiomtd->nand_chip;
|
chip = &gpiomtd->nand_chip;
|
||||||
this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret);
|
|
||||||
if (!this->IO_ADDR_R) {
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
dev_err(&dev->dev, "unable to map NAND\n");
|
chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
|
||||||
goto err_map;
|
if (IS_ERR(chip->IO_ADDR_R))
|
||||||
|
return PTR_ERR(chip->IO_ADDR_R);
|
||||||
|
|
||||||
|
res = gpio_nand_get_io_sync(pdev);
|
||||||
|
if (res) {
|
||||||
|
gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(gpiomtd->io_sync))
|
||||||
|
return PTR_ERR(gpiomtd->io_sync);
|
||||||
}
|
}
|
||||||
|
|
||||||
res1 = gpio_nand_get_io_sync(dev);
|
ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat);
|
||||||
if (res1) {
|
|
||||||
gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret);
|
|
||||||
if (!gpiomtd->io_sync) {
|
|
||||||
dev_err(&dev->dev, "unable to map sync NAND\n");
|
|
||||||
goto err_sync;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_nce;
|
return ret;
|
||||||
|
|
||||||
ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE");
|
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_nce;
|
return ret;
|
||||||
gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
|
gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
|
||||||
|
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) {
|
if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) {
|
||||||
ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP");
|
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp,
|
||||||
|
"NAND NWP");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_nwp;
|
return ret;
|
||||||
gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
|
|
||||||
}
|
}
|
||||||
ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE");
|
|
||||||
|
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_ale;
|
return ret;
|
||||||
gpio_direction_output(gpiomtd->plat.gpio_ale, 0);
|
gpio_direction_output(gpiomtd->plat.gpio_ale, 0);
|
||||||
ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE");
|
|
||||||
|
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_cle;
|
return ret;
|
||||||
gpio_direction_output(gpiomtd->plat.gpio_cle, 0);
|
gpio_direction_output(gpiomtd->plat.gpio_cle, 0);
|
||||||
|
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) {
|
if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) {
|
||||||
ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY");
|
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy,
|
||||||
|
"NAND RDY");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_rdy;
|
return ret;
|
||||||
gpio_direction_input(gpiomtd->plat.gpio_rdy);
|
gpio_direction_input(gpiomtd->plat.gpio_rdy);
|
||||||
|
chip->dev_ready = gpio_nand_devready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chip->IO_ADDR_W = chip->IO_ADDR_R;
|
||||||
|
chip->ecc.mode = NAND_ECC_SOFT;
|
||||||
|
chip->options = gpiomtd->plat.options;
|
||||||
|
chip->chip_delay = gpiomtd->plat.chip_delay;
|
||||||
|
chip->cmd_ctrl = gpio_nand_cmd_ctrl;
|
||||||
|
|
||||||
this->IO_ADDR_W = this->IO_ADDR_R;
|
gpiomtd->mtd_info.priv = chip;
|
||||||
this->ecc.mode = NAND_ECC_SOFT;
|
|
||||||
this->options = gpiomtd->plat.options;
|
|
||||||
this->chip_delay = gpiomtd->plat.chip_delay;
|
|
||||||
|
|
||||||
/* install our routines */
|
|
||||||
this->cmd_ctrl = gpio_nand_cmd_ctrl;
|
|
||||||
this->dev_ready = gpio_nand_devready;
|
|
||||||
|
|
||||||
if (this->options & NAND_BUSWIDTH_16) {
|
|
||||||
this->read_buf = gpio_nand_readbuf16;
|
|
||||||
this->write_buf = gpio_nand_writebuf16;
|
|
||||||
} else {
|
|
||||||
this->read_buf = gpio_nand_readbuf;
|
|
||||||
this->write_buf = gpio_nand_writebuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set the mtd private data for the nand driver */
|
|
||||||
gpiomtd->mtd_info.priv = this;
|
|
||||||
gpiomtd->mtd_info.owner = THIS_MODULE;
|
gpiomtd->mtd_info.owner = THIS_MODULE;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, gpiomtd);
|
||||||
|
|
||||||
|
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
||||||
|
gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
|
||||||
|
|
||||||
if (nand_scan(&gpiomtd->mtd_info, 1)) {
|
if (nand_scan(&gpiomtd->mtd_info, 1)) {
|
||||||
dev_err(&dev->dev, "no nand chips found?\n");
|
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto err_wp;
|
goto err_wp;
|
||||||
}
|
}
|
||||||
|
@ -377,38 +288,17 @@ static int gpio_nand_probe(struct platform_device *dev)
|
||||||
gpiomtd->plat.adjust_parts(&gpiomtd->plat,
|
gpiomtd->plat.adjust_parts(&gpiomtd->plat,
|
||||||
gpiomtd->mtd_info.size);
|
gpiomtd->mtd_info.size);
|
||||||
|
|
||||||
ppdata.of_node = dev->dev.of_node;
|
ppdata.of_node = pdev->dev.of_node;
|
||||||
ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata,
|
ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata,
|
||||||
gpiomtd->plat.parts,
|
gpiomtd->plat.parts,
|
||||||
gpiomtd->plat.num_parts);
|
gpiomtd->plat.num_parts);
|
||||||
if (ret)
|
if (!ret)
|
||||||
goto err_wp;
|
|
||||||
platform_set_drvdata(dev, gpiomtd);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_wp:
|
err_wp:
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
||||||
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
|
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
|
|
||||||
gpio_free(gpiomtd->plat.gpio_rdy);
|
|
||||||
err_rdy:
|
|
||||||
gpio_free(gpiomtd->plat.gpio_cle);
|
|
||||||
err_cle:
|
|
||||||
gpio_free(gpiomtd->plat.gpio_ale);
|
|
||||||
err_ale:
|
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
|
||||||
gpio_free(gpiomtd->plat.gpio_nwp);
|
|
||||||
err_nwp:
|
|
||||||
gpio_free(gpiomtd->plat.gpio_nce);
|
|
||||||
err_nce:
|
|
||||||
iounmap(gpiomtd->io_sync);
|
|
||||||
if (res1)
|
|
||||||
release_mem_region(res1->start, resource_size(res1));
|
|
||||||
err_sync:
|
|
||||||
iounmap(gpiomtd->nand_chip.IO_ADDR_R);
|
|
||||||
release_mem_region(res0->start, resource_size(res0));
|
|
||||||
err_map:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,6 +307,7 @@ static struct platform_driver gpio_nand_driver = {
|
||||||
.remove = gpio_nand_remove,
|
.remove = gpio_nand_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gpio-nand",
|
.name = "gpio-nand",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = of_match_ptr(gpio_nand_id_table),
|
.of_match_table = of_match_ptr(gpio_nand_id_table),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.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 <linux/of_mtd.h>
|
||||||
|
@ -112,7 +111,131 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int common_nfc_set_geometry(struct gpmi_nand_data *this)
|
/*
|
||||||
|
* If we can get the ECC information from the nand chip, we do not
|
||||||
|
* need to calculate them ourselves.
|
||||||
|
*
|
||||||
|
* We may have available oob space in this case.
|
||||||
|
*/
|
||||||
|
static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
|
||||||
|
{
|
||||||
|
struct bch_geometry *geo = &this->bch_geometry;
|
||||||
|
struct mtd_info *mtd = &this->mtd;
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree;
|
||||||
|
unsigned int block_mark_bit_offset;
|
||||||
|
|
||||||
|
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (chip->ecc_step_ds) {
|
||||||
|
case SZ_512:
|
||||||
|
geo->gf_len = 13;
|
||||||
|
break;
|
||||||
|
case SZ_1K:
|
||||||
|
geo->gf_len = 14;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(this->dev,
|
||||||
|
"unsupported nand chip. ecc bits : %d, ecc size : %d\n",
|
||||||
|
chip->ecc_strength_ds, chip->ecc_step_ds);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
geo->ecc_chunk_size = chip->ecc_step_ds;
|
||||||
|
geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
|
||||||
|
if (!gpmi_check_ecc(this))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Keep the C >= O */
|
||||||
|
if (geo->ecc_chunk_size < mtd->oobsize) {
|
||||||
|
dev_err(this->dev,
|
||||||
|
"unsupported nand chip. ecc size: %d, oob size : %d\n",
|
||||||
|
chip->ecc_step_ds, mtd->oobsize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The default value, see comment in the legacy_set_geometry(). */
|
||||||
|
geo->metadata_size = 10;
|
||||||
|
|
||||||
|
geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
|
||||||
|
*
|
||||||
|
* | P |
|
||||||
|
* |<----------------------------------------------------->|
|
||||||
|
* | |
|
||||||
|
* | (Block Mark) |
|
||||||
|
* | P' | | | |
|
||||||
|
* |<-------------------------------------------->| D | | O' |
|
||||||
|
* | |<---->| |<--->|
|
||||||
|
* V V V V V
|
||||||
|
* +---+----------+-+----------+-+----------+-+----------+-+-----+
|
||||||
|
* | M | data |E| data |E| data |E| data |E| |
|
||||||
|
* +---+----------+-+----------+-+----------+-+----------+-+-----+
|
||||||
|
* ^ ^
|
||||||
|
* | O |
|
||||||
|
* |<------------>|
|
||||||
|
* | |
|
||||||
|
*
|
||||||
|
* P : the page size for BCH module.
|
||||||
|
* E : The ECC strength.
|
||||||
|
* G : the length of Galois Field.
|
||||||
|
* N : The chunk count of per page.
|
||||||
|
* M : the metasize of per page.
|
||||||
|
* C : the ecc chunk size, aka the "data" above.
|
||||||
|
* P': the nand chip's page size.
|
||||||
|
* O : the nand chip's oob size.
|
||||||
|
* O': the free oob.
|
||||||
|
*
|
||||||
|
* The formula for P is :
|
||||||
|
*
|
||||||
|
* E * G * N
|
||||||
|
* P = ------------ + P' + M
|
||||||
|
* 8
|
||||||
|
*
|
||||||
|
* The position of block mark moves forward in the ECC-based view
|
||||||
|
* of page, and the delta is:
|
||||||
|
*
|
||||||
|
* E * G * (N - 1)
|
||||||
|
* D = (---------------- + M)
|
||||||
|
* 8
|
||||||
|
*
|
||||||
|
* Please see the comment in legacy_set_geometry().
|
||||||
|
* With the condition C >= O , we still can get same result.
|
||||||
|
* So the bit position of the physical block mark within the ECC-based
|
||||||
|
* view of the page is :
|
||||||
|
* (P' - D) * 8
|
||||||
|
*/
|
||||||
|
geo->page_size = mtd->writesize + geo->metadata_size +
|
||||||
|
(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
|
||||||
|
|
||||||
|
/* The available oob size we have. */
|
||||||
|
if (geo->page_size < mtd->writesize + mtd->oobsize) {
|
||||||
|
of->offset = geo->page_size - mtd->writesize;
|
||||||
|
of->length = mtd->oobsize - of->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
geo->payload_size = mtd->writesize;
|
||||||
|
|
||||||
|
geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
|
||||||
|
geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
|
||||||
|
+ ALIGN(geo->ecc_chunk_count, 4);
|
||||||
|
|
||||||
|
if (!this->swap_block_mark)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* For bit swap. */
|
||||||
|
block_mark_bit_offset = mtd->writesize * 8 -
|
||||||
|
(geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
|
||||||
|
+ geo->metadata_size * 8);
|
||||||
|
|
||||||
|
geo->block_mark_byte_offset = block_mark_bit_offset / 8;
|
||||||
|
geo->block_mark_bit_offset = block_mark_bit_offset % 8;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int legacy_set_geometry(struct gpmi_nand_data *this)
|
||||||
{
|
{
|
||||||
struct bch_geometry *geo = &this->bch_geometry;
|
struct bch_geometry *geo = &this->bch_geometry;
|
||||||
struct mtd_info *mtd = &this->mtd;
|
struct mtd_info *mtd = &this->mtd;
|
||||||
|
@ -224,6 +347,11 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int common_nfc_set_geometry(struct gpmi_nand_data *this)
|
||||||
|
{
|
||||||
|
return set_geometry_by_ecc_info(this) ? 0 : legacy_set_geometry(this);
|
||||||
|
}
|
||||||
|
|
||||||
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
|
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
|
||||||
{
|
{
|
||||||
int chipnr = this->current_chip;
|
int chipnr = this->current_chip;
|
||||||
|
@ -355,7 +483,7 @@ static int acquire_register_block(struct gpmi_nand_data *this,
|
||||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
|
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
pr_err("Can't get resource for %s\n", res_name);
|
pr_err("Can't get resource for %s\n", res_name);
|
||||||
return -ENXIO;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = ioremap(r->start, resource_size(r));
|
p = ioremap(r->start, resource_size(r));
|
||||||
|
@ -396,7 +524,7 @@ static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
|
||||||
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
|
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
pr_err("Can't get resource for %s\n", res_name);
|
pr_err("Can't get resource for %s\n", res_name);
|
||||||
return -ENXIO;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = request_irq(r->start, irq_h, 0, res_name, this);
|
err = request_irq(r->start, irq_h, 0, res_name, this);
|
||||||
|
@ -473,12 +601,14 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
||||||
struct resources *r = &this->resources;
|
struct resources *r = &this->resources;
|
||||||
char **extra_clks = NULL;
|
char **extra_clks = NULL;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
int i;
|
int err, i;
|
||||||
|
|
||||||
/* The main clock is stored in the first. */
|
/* The main clock is stored in the first. */
|
||||||
r->clock[0] = clk_get(this->dev, "gpmi_io");
|
r->clock[0] = clk_get(this->dev, "gpmi_io");
|
||||||
if (IS_ERR(r->clock[0]))
|
if (IS_ERR(r->clock[0])) {
|
||||||
|
err = PTR_ERR(r->clock[0]);
|
||||||
goto err_clock;
|
goto err_clock;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get extra clocks */
|
/* Get extra clocks */
|
||||||
if (GPMI_IS_MX6Q(this))
|
if (GPMI_IS_MX6Q(this))
|
||||||
|
@ -491,8 +621,10 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
clk = clk_get(this->dev, extra_clks[i - 1]);
|
clk = clk_get(this->dev, extra_clks[i - 1]);
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk)) {
|
||||||
|
err = PTR_ERR(clk);
|
||||||
goto err_clock;
|
goto err_clock;
|
||||||
|
}
|
||||||
|
|
||||||
r->clock[i] = clk;
|
r->clock[i] = clk;
|
||||||
}
|
}
|
||||||
|
@ -511,12 +643,11 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
||||||
err_clock:
|
err_clock:
|
||||||
dev_dbg(this->dev, "failed in finding the clocks.\n");
|
dev_dbg(this->dev, "failed in finding the clocks.\n");
|
||||||
gpmi_put_clks(this);
|
gpmi_put_clks(this);
|
||||||
return -ENOMEM;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acquire_resources(struct gpmi_nand_data *this)
|
static int acquire_resources(struct gpmi_nand_data *this)
|
||||||
{
|
{
|
||||||
struct pinctrl *pinctrl;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
|
ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
|
||||||
|
@ -535,19 +666,12 @@ static int acquire_resources(struct gpmi_nand_data *this)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto exit_dma_channels;
|
goto exit_dma_channels;
|
||||||
|
|
||||||
pinctrl = devm_pinctrl_get_select_default(&this->pdev->dev);
|
|
||||||
if (IS_ERR(pinctrl)) {
|
|
||||||
ret = PTR_ERR(pinctrl);
|
|
||||||
goto exit_pin;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gpmi_get_clks(this);
|
ret = gpmi_get_clks(this);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto exit_clock;
|
goto exit_clock;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exit_clock:
|
exit_clock:
|
||||||
exit_pin:
|
|
||||||
release_dma_channels(this);
|
release_dma_channels(this);
|
||||||
exit_dma_channels:
|
exit_dma_channels:
|
||||||
release_bch_irq(this);
|
release_bch_irq(this);
|
||||||
|
@ -1153,19 +1277,10 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
struct gpmi_nand_data *this = chip->priv;
|
struct gpmi_nand_data *this = chip->priv;
|
||||||
int block, ret = 0;
|
int ret = 0;
|
||||||
uint8_t *block_mark;
|
uint8_t *block_mark;
|
||||||
int column, page, status, chipnr;
|
int column, page, status, chipnr;
|
||||||
|
|
||||||
/* Get block number */
|
|
||||||
block = (int)(ofs >> chip->bbt_erase_shift);
|
|
||||||
if (chip->bbt)
|
|
||||||
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
|
||||||
|
|
||||||
/* Do we have a flash based bad block table ? */
|
|
||||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
|
||||||
ret = nand_update_bbt(mtd, ofs);
|
|
||||||
else {
|
|
||||||
chipnr = (int)(ofs >> chip->chip_shift);
|
chipnr = (int)(ofs >> chip->chip_shift);
|
||||||
chip->select_chip(mtd, chipnr);
|
chip->select_chip(mtd, chipnr);
|
||||||
|
|
||||||
|
@ -1187,9 +1302,6 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
|
|
||||||
chip->select_chip(mtd, -1);
|
chip->select_chip(mtd, -1);
|
||||||
}
|
|
||||||
if (!ret)
|
|
||||||
mtd->ecc_stats.badblocks++;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1469,19 +1581,22 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Adjust the ECC strength according to the chip. */
|
|
||||||
this->nand.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpmi_scan_bbt(struct mtd_info *mtd)
|
static void gpmi_nfc_exit(struct gpmi_nand_data *this)
|
||||||
{
|
{
|
||||||
|
nand_release(&this->mtd);
|
||||||
|
gpmi_free_dma_buffer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpmi_init_last(struct gpmi_nand_data *this)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = &this->mtd;
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
struct gpmi_nand_data *this = chip->priv;
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||||
|
struct bch_geometry *bch_geo = &this->bch_geometry;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Prepare for the BBT scan. */
|
/* Prepare for the BBT scan. */
|
||||||
|
@ -1489,6 +1604,16 @@ static int gpmi_scan_bbt(struct mtd_info *mtd)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
/* Init the nand_ecc_ctrl{} */
|
||||||
|
ecc->read_page = gpmi_ecc_read_page;
|
||||||
|
ecc->write_page = gpmi_ecc_write_page;
|
||||||
|
ecc->read_oob = gpmi_ecc_read_oob;
|
||||||
|
ecc->write_oob = gpmi_ecc_write_oob;
|
||||||
|
ecc->mode = NAND_ECC_HW;
|
||||||
|
ecc->size = bch_geo->ecc_chunk_size;
|
||||||
|
ecc->strength = bch_geo->ecc_strength;
|
||||||
|
ecc->layout = &gpmi_hw_ecclayout;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Can we enable the extra features? such as EDO or Sync mode.
|
* Can we enable the extra features? such as EDO or Sync mode.
|
||||||
*
|
*
|
||||||
|
@ -1497,14 +1622,7 @@ static int gpmi_scan_bbt(struct mtd_info *mtd)
|
||||||
*/
|
*/
|
||||||
gpmi_extra_init(this);
|
gpmi_extra_init(this);
|
||||||
|
|
||||||
/* use the default BBT implementation */
|
return 0;
|
||||||
return nand_default_bbt(mtd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gpmi_nfc_exit(struct gpmi_nand_data *this)
|
|
||||||
{
|
|
||||||
nand_release(&this->mtd);
|
|
||||||
gpmi_free_dma_buffer(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpmi_nfc_init(struct gpmi_nand_data *this)
|
static int gpmi_nfc_init(struct gpmi_nand_data *this)
|
||||||
|
@ -1530,33 +1648,33 @@ static int gpmi_nfc_init(struct gpmi_nand_data *this)
|
||||||
chip->read_byte = gpmi_read_byte;
|
chip->read_byte = gpmi_read_byte;
|
||||||
chip->read_buf = gpmi_read_buf;
|
chip->read_buf = gpmi_read_buf;
|
||||||
chip->write_buf = gpmi_write_buf;
|
chip->write_buf = gpmi_write_buf;
|
||||||
chip->ecc.read_page = gpmi_ecc_read_page;
|
|
||||||
chip->ecc.write_page = gpmi_ecc_write_page;
|
|
||||||
chip->ecc.read_oob = gpmi_ecc_read_oob;
|
|
||||||
chip->ecc.write_oob = gpmi_ecc_write_oob;
|
|
||||||
chip->scan_bbt = gpmi_scan_bbt;
|
|
||||||
chip->badblock_pattern = &gpmi_bbt_descr;
|
chip->badblock_pattern = &gpmi_bbt_descr;
|
||||||
chip->block_markbad = gpmi_block_markbad;
|
chip->block_markbad = gpmi_block_markbad;
|
||||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||||
chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
chip->ecc.size = 1;
|
|
||||||
chip->ecc.strength = 8;
|
|
||||||
chip->ecc.layout = &gpmi_hw_ecclayout;
|
|
||||||
if (of_get_nand_on_flash_bbt(this->dev->of_node))
|
if (of_get_nand_on_flash_bbt(this->dev->of_node))
|
||||||
chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
|
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_ident().
|
||||||
|
*/
|
||||||
this->bch_geometry.payload_size = 1024;
|
this->bch_geometry.payload_size = 1024;
|
||||||
this->bch_geometry.auxiliary_size = 128;
|
this->bch_geometry.auxiliary_size = 128;
|
||||||
ret = gpmi_alloc_dma_buffer(this);
|
ret = gpmi_alloc_dma_buffer(this);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
ret = nand_scan(mtd, 1);
|
ret = nand_scan_ident(mtd, 1, NULL);
|
||||||
if (ret) {
|
if (ret)
|
||||||
pr_err("Chip scan failed\n");
|
goto err_out;
|
||||||
|
|
||||||
|
ret = gpmi_init_last(this);
|
||||||
|
if (ret)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
ret = nand_scan_tail(mtd);
|
||||||
|
if (ret)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
|
||||||
|
|
||||||
ppdata.of_node = this->pdev->dev.of_node;
|
ppdata.of_node = this->pdev->dev.of_node;
|
||||||
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
|
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
|
||||||
|
@ -1601,7 +1719,7 @@ static int gpmi_nand_probe(struct platform_device *pdev)
|
||||||
pdev->id_entry = of_id->data;
|
pdev->id_entry = of_id->data;
|
||||||
} else {
|
} else {
|
||||||
pr_err("Failed to find the right device id.\n");
|
pr_err("Failed to find the right device id.\n");
|
||||||
return -ENOMEM;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
this = kzalloc(sizeof(*this), GFP_KERNEL);
|
this = kzalloc(sizeof(*this), GFP_KERNEL);
|
||||||
|
@ -1633,7 +1751,6 @@ static int gpmi_nand_probe(struct platform_device *pdev)
|
||||||
exit_nfc_init:
|
exit_nfc_init:
|
||||||
release_resources(this);
|
release_resources(this);
|
||||||
exit_acquire_resources:
|
exit_acquire_resources:
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
dev_err(this->dev, "driver registration failed: %d\n", ret);
|
dev_err(this->dev, "driver registration failed: %d\n", ret);
|
||||||
kfree(this);
|
kfree(this);
|
||||||
|
|
||||||
|
@ -1646,7 +1763,6 @@ static int gpmi_nand_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
gpmi_nfc_exit(this);
|
gpmi_nfc_exit(this);
|
||||||
release_resources(this);
|
release_resources(this);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
kfree(this);
|
kfree(this);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -411,7 +411,7 @@ static int jz_nand_probe(struct platform_device *pdev)
|
||||||
struct jz_nand *nand;
|
struct jz_nand *nand;
|
||||||
struct nand_chip *chip;
|
struct nand_chip *chip;
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
|
struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
size_t chipnr, bank_idx;
|
size_t chipnr, bank_idx;
|
||||||
uint8_t nand_maf_id = 0, nand_dev_id = 0;
|
uint8_t nand_maf_id = 0, nand_dev_id = 0;
|
||||||
|
|
||||||
|
@ -538,7 +538,6 @@ err_unclaim_banks:
|
||||||
err_gpio_busy:
|
err_gpio_busy:
|
||||||
if (pdata && gpio_is_valid(pdata->busy_gpio))
|
if (pdata && gpio_is_valid(pdata->busy_gpio))
|
||||||
gpio_free(pdata->busy_gpio);
|
gpio_free(pdata->busy_gpio);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
err_iounmap_mmio:
|
err_iounmap_mmio:
|
||||||
jz_nand_iounmap_resource(nand->mem, nand->base);
|
jz_nand_iounmap_resource(nand->mem, nand->base);
|
||||||
err_free:
|
err_free:
|
||||||
|
@ -549,7 +548,7 @@ err_free:
|
||||||
static int jz_nand_remove(struct platform_device *pdev)
|
static int jz_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct jz_nand *nand = platform_get_drvdata(pdev);
|
struct jz_nand *nand = platform_get_drvdata(pdev);
|
||||||
struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
|
struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
nand_release(&nand->mtd);
|
nand_release(&nand->mtd);
|
||||||
|
@ -570,7 +569,6 @@ static int jz_nand_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
jz_nand_iounmap_resource(nand->mem, nand->base);
|
jz_nand_iounmap_resource(nand->mem, nand->base);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
kfree(nand);
|
kfree(nand);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -696,7 +696,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
lpc32xx_wp_disable(host);
|
lpc32xx_wp_disable(host);
|
||||||
|
|
||||||
host->pdata = pdev->dev.platform_data;
|
host->pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
nand_chip->priv = host; /* link the private data structures */
|
nand_chip->priv = host; /* link the private data structures */
|
||||||
mtd->priv = nand_chip;
|
mtd->priv = nand_chip;
|
||||||
|
@ -828,7 +828,6 @@ err_exit3:
|
||||||
err_exit2:
|
err_exit2:
|
||||||
clk_disable(host->clk);
|
clk_disable(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
err_exit1:
|
err_exit1:
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
gpio_free(host->ncfg->wp_gpio);
|
gpio_free(host->ncfg->wp_gpio);
|
||||||
|
@ -851,7 +850,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
clk_disable(host->clk);
|
clk_disable(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
gpio_free(host->ncfg->wp_gpio);
|
gpio_free(host->ncfg->wp_gpio);
|
||||||
|
|
|
@ -798,7 +798,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
lpc32xx_wp_disable(host);
|
lpc32xx_wp_disable(host);
|
||||||
|
|
||||||
host->pdata = pdev->dev.platform_data;
|
host->pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
mtd = &host->mtd;
|
mtd = &host->mtd;
|
||||||
chip = &host->nand_chip;
|
chip = &host->nand_chip;
|
||||||
|
@ -936,7 +936,6 @@ err_exit3:
|
||||||
err_exit2:
|
err_exit2:
|
||||||
clk_disable(host->clk);
|
clk_disable(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
err_exit1:
|
err_exit1:
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
gpio_free(host->ncfg->wp_gpio);
|
gpio_free(host->ncfg->wp_gpio);
|
||||||
|
@ -963,7 +962,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
clk_disable(host->clk);
|
clk_disable(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
gpio_free(host->ncfg->wp_gpio);
|
gpio_free(host->ncfg->wp_gpio);
|
||||||
|
|
||||||
|
|
|
@ -266,7 +266,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char const *part_probes[] = {
|
static const char * const part_probes[] = {
|
||||||
"cmdlinepart", "RedBoot", "ofpart", NULL };
|
"cmdlinepart", "RedBoot", "ofpart", NULL };
|
||||||
|
|
||||||
static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
|
static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
|
||||||
|
@ -1432,7 +1432,8 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err = mxcnd_probe_dt(host);
|
err = mxcnd_probe_dt(host);
|
||||||
if (err > 0) {
|
if (err > 0) {
|
||||||
struct mxc_nand_platform_data *pdata = pdev->dev.platform_data;
|
struct mxc_nand_platform_data *pdata =
|
||||||
|
dev_get_platdata(&pdev->dev);
|
||||||
if (pdata) {
|
if (pdata) {
|
||||||
host->pdata = *pdata;
|
host->pdata = *pdata;
|
||||||
host->devtype_data = (struct mxc_nand_devtype_data *)
|
host->devtype_data = (struct mxc_nand_devtype_data *)
|
||||||
|
@ -1446,8 +1447,6 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if (host->devtype_data->needs_ip) {
|
if (host->devtype_data->needs_ip) {
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res)
|
|
||||||
return -ENODEV;
|
|
||||||
host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
|
host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->regs_ip))
|
if (IS_ERR(host->regs_ip))
|
||||||
return PTR_ERR(host->regs_ip);
|
return PTR_ERR(host->regs_ip);
|
||||||
|
@ -1457,9 +1456,6 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
host->base = devm_ioremap_resource(&pdev->dev, res);
|
host->base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->base))
|
if (IS_ERR(host->base))
|
||||||
return PTR_ERR(host->base);
|
return PTR_ERR(host->base);
|
||||||
|
@ -1578,8 +1574,6 @@ static int mxcnd_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mxc_nand_host *host = platform_get_drvdata(pdev);
|
struct mxc_nand_host *host = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
nand_release(&host->mtd);
|
nand_release(&host->mtd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -108,13 +108,13 @@ static int check_offs_len(struct mtd_info *mtd,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Start address must align on block boundary */
|
/* Start address must align on block boundary */
|
||||||
if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
|
if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) {
|
||||||
pr_debug("%s: unaligned address\n", __func__);
|
pr_debug("%s: unaligned address\n", __func__);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Length must align on block boundary */
|
/* Length must align on block boundary */
|
||||||
if (len & ((1 << chip->phys_erase_shift) - 1)) {
|
if (len & ((1ULL << chip->phys_erase_shift) - 1)) {
|
||||||
pr_debug("%s: length not block aligned\n", __func__);
|
pr_debug("%s: length not block aligned\n", __func__);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -211,11 +211,9 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
|
||||||
*/
|
*/
|
||||||
static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
iowrite8_rep(chip->IO_ADDR_W, buf, len);
|
||||||
writeb(buf[i], chip->IO_ADDR_W);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -228,11 +226,9 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||||
*/
|
*/
|
||||||
static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
ioread8_rep(chip->IO_ADDR_R, buf, len);
|
||||||
buf[i] = readb(chip->IO_ADDR_R);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -245,14 +241,10 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||||
*/
|
*/
|
||||||
static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
|
static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
u16 *p = (u16 *) buf;
|
u16 *p = (u16 *) buf;
|
||||||
len >>= 1;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
writew(p[i], chip->IO_ADDR_W);
|
|
||||||
|
|
||||||
|
iowrite16_rep(chip->IO_ADDR_W, p, len >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -265,13 +257,10 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||||
*/
|
*/
|
||||||
static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
|
static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
u16 *p = (u16 *) buf;
|
u16 *p = (u16 *) buf;
|
||||||
len >>= 1;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
|
||||||
p[i] = readw(chip->IO_ADDR_R);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -335,50 +324,20 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_default_block_markbad - [DEFAULT] mark a block bad
|
* nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
* @ofs: offset from device start
|
* @ofs: offset from device start
|
||||||
*
|
*
|
||||||
* This is the default implementation, which can be overridden by a hardware
|
* This is the default implementation, which can be overridden by a hardware
|
||||||
* specific driver. We try operations in the following order, according to our
|
* specific driver. It provides the details for writing a bad block marker to a
|
||||||
* bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
|
* block.
|
||||||
* (1) erase the affected block, to allow OOB marker to be written cleanly
|
*/
|
||||||
* (2) update in-memory BBT
|
|
||||||
* (3) write bad block marker to OOB area of affected block
|
|
||||||
* (4) update flash-based BBT
|
|
||||||
* Note that we retain the first error encountered in (3) or (4), finish the
|
|
||||||
* procedures, and dump the error in the end.
|
|
||||||
*/
|
|
||||||
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
uint8_t buf[2] = { 0, 0 };
|
|
||||||
int block, res, ret = 0, i = 0;
|
|
||||||
int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
|
|
||||||
|
|
||||||
if (write_oob) {
|
|
||||||
struct erase_info einfo;
|
|
||||||
|
|
||||||
/* Attempt erase before marking OOB */
|
|
||||||
memset(&einfo, 0, sizeof(einfo));
|
|
||||||
einfo.mtd = mtd;
|
|
||||||
einfo.addr = ofs;
|
|
||||||
einfo.len = 1 << chip->phys_erase_shift;
|
|
||||||
nand_erase_nand(mtd, &einfo, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get block number */
|
|
||||||
block = (int)(ofs >> chip->bbt_erase_shift);
|
|
||||||
/* Mark block bad in memory-based BBT */
|
|
||||||
if (chip->bbt)
|
|
||||||
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
|
||||||
|
|
||||||
/* Write bad block marker to OOB */
|
|
||||||
if (write_oob) {
|
|
||||||
struct mtd_oob_ops ops;
|
struct mtd_oob_ops ops;
|
||||||
loff_t wr_ofs = ofs;
|
uint8_t buf[2] = { 0, 0 };
|
||||||
|
int ret = 0, res, i = 0;
|
||||||
nand_get_device(mtd, FL_WRITING);
|
|
||||||
|
|
||||||
ops.datbuf = NULL;
|
ops.datbuf = NULL;
|
||||||
ops.oobbuf = buf;
|
ops.oobbuf = buf;
|
||||||
|
@ -393,22 +352,60 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
|
||||||
/* Write to first/last page(s) if necessary */
|
/* Write to first/last page(s) if necessary */
|
||||||
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||||
wr_ofs += mtd->erasesize - mtd->writesize;
|
ofs += mtd->erasesize - mtd->writesize;
|
||||||
do {
|
do {
|
||||||
res = nand_do_write_oob(mtd, wr_ofs, &ops);
|
res = nand_do_write_oob(mtd, ofs, &ops);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = res;
|
ret = res;
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
wr_ofs += mtd->writesize;
|
ofs += mtd->writesize;
|
||||||
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
|
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_block_markbad_lowlevel - mark a block bad
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @ofs: offset from device start
|
||||||
|
*
|
||||||
|
* This function performs the generic NAND bad block marking steps (i.e., bad
|
||||||
|
* block table(s) and/or marker(s)). We only allow the hardware driver to
|
||||||
|
* specify how to write bad block markers to OOB (chip->block_markbad).
|
||||||
|
*
|
||||||
|
* We try operations in the following order:
|
||||||
|
* (1) erase the affected block, to allow OOB marker to be written cleanly
|
||||||
|
* (2) write bad block marker to OOB area of affected block (unless flag
|
||||||
|
* NAND_BBT_NO_OOB_BBM is present)
|
||||||
|
* (3) update the BBT
|
||||||
|
* Note that we retain the first error encountered in (2) or (3), finish the
|
||||||
|
* procedures, and dump the error in the end.
|
||||||
|
*/
|
||||||
|
static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
int res, ret = 0;
|
||||||
|
|
||||||
|
if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
|
||||||
|
struct erase_info einfo;
|
||||||
|
|
||||||
|
/* Attempt erase before marking OOB */
|
||||||
|
memset(&einfo, 0, sizeof(einfo));
|
||||||
|
einfo.mtd = mtd;
|
||||||
|
einfo.addr = ofs;
|
||||||
|
einfo.len = 1ULL << chip->phys_erase_shift;
|
||||||
|
nand_erase_nand(mtd, &einfo, 0);
|
||||||
|
|
||||||
|
/* Write bad block marker to OOB */
|
||||||
|
nand_get_device(mtd, FL_WRITING);
|
||||||
|
ret = chip->block_markbad(mtd, ofs);
|
||||||
nand_release_device(mtd);
|
nand_release_device(mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update flash-based bad block table */
|
/* Mark block bad in BBT */
|
||||||
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
|
if (chip->bbt) {
|
||||||
res = nand_update_bbt(mtd, ofs);
|
res = nand_markbad_bbt(mtd, ofs);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = res;
|
ret = res;
|
||||||
}
|
}
|
||||||
|
@ -1983,13 +1980,14 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
|
* nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
|
||||||
* @mtd: mtd info structure
|
* @mtd: mtd info structure
|
||||||
* @chip: nand chip info structure
|
* @chip: nand chip info structure
|
||||||
* @column: column address of subpage within the page
|
* @offset: column address of subpage within the page
|
||||||
* @data_len: data length
|
* @data_len: data length
|
||||||
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
*/
|
*/
|
||||||
static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, uint32_t offset,
|
struct nand_chip *chip, uint32_t offset,
|
||||||
uint32_t data_len, const uint8_t *data_buf,
|
uint32_t data_len, const uint8_t *buf,
|
||||||
int oob_required)
|
int oob_required)
|
||||||
{
|
{
|
||||||
uint8_t *oob_buf = chip->oob_poi;
|
uint8_t *oob_buf = chip->oob_poi;
|
||||||
|
@ -2008,20 +2006,20 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
||||||
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
||||||
|
|
||||||
/* write data (untouched subpages already masked by 0xFF) */
|
/* write data (untouched subpages already masked by 0xFF) */
|
||||||
chip->write_buf(mtd, data_buf, ecc_size);
|
chip->write_buf(mtd, buf, ecc_size);
|
||||||
|
|
||||||
/* mask ECC of un-touched subpages by padding 0xFF */
|
/* mask ECC of un-touched subpages by padding 0xFF */
|
||||||
if ((step < start_step) || (step > end_step))
|
if ((step < start_step) || (step > end_step))
|
||||||
memset(ecc_calc, 0xff, ecc_bytes);
|
memset(ecc_calc, 0xff, ecc_bytes);
|
||||||
else
|
else
|
||||||
chip->ecc.calculate(mtd, data_buf, ecc_calc);
|
chip->ecc.calculate(mtd, buf, ecc_calc);
|
||||||
|
|
||||||
/* mask OOB of un-touched subpages by padding 0xFF */
|
/* mask OOB of un-touched subpages by padding 0xFF */
|
||||||
/* if oob_required, preserve OOB metadata of written subpage */
|
/* if oob_required, preserve OOB metadata of written subpage */
|
||||||
if (!oob_required || (step < start_step) || (step > end_step))
|
if (!oob_required || (step < start_step) || (step > end_step))
|
||||||
memset(oob_buf, 0xff, oob_bytes);
|
memset(oob_buf, 0xff, oob_bytes);
|
||||||
|
|
||||||
data_buf += ecc_size;
|
buf += ecc_size;
|
||||||
ecc_calc += ecc_bytes;
|
ecc_calc += ecc_bytes;
|
||||||
oob_buf += oob_bytes;
|
oob_buf += oob_bytes;
|
||||||
}
|
}
|
||||||
|
@ -2633,7 +2631,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Increment page address and decrement length */
|
/* Increment page address and decrement length */
|
||||||
len -= (1 << chip->phys_erase_shift);
|
len -= (1ULL << chip->phys_erase_shift);
|
||||||
page += pages_per_block;
|
page += pages_per_block;
|
||||||
|
|
||||||
/* Check, if we cross a chip boundary */
|
/* Check, if we cross a chip boundary */
|
||||||
|
@ -2694,7 +2692,6 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
|
||||||
*/
|
*/
|
||||||
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd->priv;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = nand_block_isbad(mtd, ofs);
|
ret = nand_block_isbad(mtd, ofs);
|
||||||
|
@ -2705,7 +2702,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chip->block_markbad(mtd, ofs);
|
return nand_block_markbad_lowlevel(mtd, ofs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2720,7 +2717,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (!chip->onfi_version)
|
if (!chip->onfi_version ||
|
||||||
|
!(le16_to_cpu(chip->onfi_params.opt_cmd)
|
||||||
|
& ONFI_OPT_CMD_SET_GET_FEATURES))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
|
chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
|
||||||
|
@ -2741,7 +2740,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
|
static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
int addr, uint8_t *subfeature_param)
|
int addr, uint8_t *subfeature_param)
|
||||||
{
|
{
|
||||||
if (!chip->onfi_version)
|
if (!chip->onfi_version ||
|
||||||
|
!(le16_to_cpu(chip->onfi_params.opt_cmd)
|
||||||
|
& ONFI_OPT_CMD_SET_GET_FEATURES))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* clear the sub feature parameters */
|
/* clear the sub feature parameters */
|
||||||
|
@ -2793,7 +2794,15 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||||
|
|
||||||
if (!chip->select_chip)
|
if (!chip->select_chip)
|
||||||
chip->select_chip = nand_select_chip;
|
chip->select_chip = nand_select_chip;
|
||||||
if (!chip->read_byte)
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* If called twice, pointers that depend on busw may need to be reset */
|
||||||
|
if (!chip->read_byte || chip->read_byte == nand_read_byte)
|
||||||
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
|
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
|
||||||
if (!chip->read_word)
|
if (!chip->read_word)
|
||||||
chip->read_word = nand_read_word;
|
chip->read_word = nand_read_word;
|
||||||
|
@ -2801,9 +2810,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||||
chip->block_bad = nand_block_bad;
|
chip->block_bad = nand_block_bad;
|
||||||
if (!chip->block_markbad)
|
if (!chip->block_markbad)
|
||||||
chip->block_markbad = nand_default_block_markbad;
|
chip->block_markbad = nand_default_block_markbad;
|
||||||
if (!chip->write_buf)
|
if (!chip->write_buf || chip->write_buf == nand_write_buf)
|
||||||
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 == nand_read_buf)
|
||||||
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
|
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
|
||||||
if (!chip->scan_bbt)
|
if (!chip->scan_bbt)
|
||||||
chip->scan_bbt = nand_default_bbt;
|
chip->scan_bbt = nand_default_bbt;
|
||||||
|
@ -2846,6 +2855,78 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse the Extended Parameter Page. */
|
||||||
|
static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
|
||||||
|
struct nand_chip *chip, struct nand_onfi_params *p)
|
||||||
|
{
|
||||||
|
struct onfi_ext_param_page *ep;
|
||||||
|
struct onfi_ext_section *s;
|
||||||
|
struct onfi_ext_ecc_info *ecc;
|
||||||
|
uint8_t *cursor;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
len = le16_to_cpu(p->ext_param_page_length) * 16;
|
||||||
|
ep = kmalloc(len, GFP_KERNEL);
|
||||||
|
if (!ep) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto ext_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send our own NAND_CMD_PARAM. */
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
|
||||||
|
|
||||||
|
/* Use the Change Read Column command to skip the ONFI param pages. */
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
||||||
|
sizeof(*p) * p->num_of_param_pages , -1);
|
||||||
|
|
||||||
|
/* Read out the Extended Parameter Page. */
|
||||||
|
chip->read_buf(mtd, (uint8_t *)ep, len);
|
||||||
|
if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2)
|
||||||
|
!= le16_to_cpu(ep->crc))) {
|
||||||
|
pr_debug("fail in the CRC.\n");
|
||||||
|
goto ext_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the signature.
|
||||||
|
* Do not strictly follow the ONFI spec, maybe changed in future.
|
||||||
|
*/
|
||||||
|
if (strncmp(ep->sig, "EPPS", 4)) {
|
||||||
|
pr_debug("The signature is invalid.\n");
|
||||||
|
goto ext_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the ECC section. */
|
||||||
|
cursor = (uint8_t *)(ep + 1);
|
||||||
|
for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) {
|
||||||
|
s = ep->sections + i;
|
||||||
|
if (s->type == ONFI_SECTION_TYPE_2)
|
||||||
|
break;
|
||||||
|
cursor += s->length * 16;
|
||||||
|
}
|
||||||
|
if (i == ONFI_EXT_SECTION_MAX) {
|
||||||
|
pr_debug("We can not find the ECC section.\n");
|
||||||
|
goto ext_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the info we want. */
|
||||||
|
ecc = (struct onfi_ext_ecc_info *)cursor;
|
||||||
|
|
||||||
|
if (ecc->codeword_size) {
|
||||||
|
chip->ecc_strength_ds = ecc->ecc_bits;
|
||||||
|
chip->ecc_step_ds = 1 << ecc->codeword_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("ONFI extended param page detected.\n");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ext_out:
|
||||||
|
kfree(ep);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
|
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
|
@ -2907,9 +2988,31 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||||
chip->chipsize = le32_to_cpu(p->blocks_per_lun);
|
chip->chipsize = le32_to_cpu(p->blocks_per_lun);
|
||||||
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
|
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
|
||||||
*busw = 0;
|
|
||||||
if (le16_to_cpu(p->features) & 1)
|
if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
|
||||||
*busw = NAND_BUSWIDTH_16;
|
*busw = NAND_BUSWIDTH_16;
|
||||||
|
else
|
||||||
|
*busw = 0;
|
||||||
|
|
||||||
|
if (p->ecc_bits != 0xff) {
|
||||||
|
chip->ecc_strength_ds = p->ecc_bits;
|
||||||
|
chip->ecc_step_ds = 512;
|
||||||
|
} else if (chip->onfi_version >= 21 &&
|
||||||
|
(onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The nand_flash_detect_ext_param_page() uses the
|
||||||
|
* Change Read Column command which maybe not supported
|
||||||
|
* by the chip->cmdfunc. So try to update the chip->cmdfunc
|
||||||
|
* now. We do not replace user supplied command function.
|
||||||
|
*/
|
||||||
|
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
||||||
|
chip->cmdfunc = nand_command_lp;
|
||||||
|
|
||||||
|
/* The Extended Parameter Page is supported since ONFI 2.1. */
|
||||||
|
if (nand_flash_detect_ext_param_page(mtd, chip, p))
|
||||||
|
pr_info("Failed to detect the extended param page.\n");
|
||||||
|
}
|
||||||
|
|
||||||
pr_info("ONFI flash detected\n");
|
pr_info("ONFI flash detected\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -3086,6 +3189,22 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
extid >>= 2;
|
extid >>= 2;
|
||||||
/* Get buswidth information */
|
/* Get buswidth information */
|
||||||
*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
|
||||||
|
* 512B page. For Toshiba SLC, we decode the 5th/6th byte as
|
||||||
|
* follows:
|
||||||
|
* - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
|
||||||
|
* 110b -> 24nm
|
||||||
|
* - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC
|
||||||
|
*/
|
||||||
|
if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA &&
|
||||||
|
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
|
||||||
|
(id_data[5] & 0x7) == 0x6 /* 24nm */ &&
|
||||||
|
!(id_data[4] & 0x80) /* !BENAND */) {
|
||||||
|
mtd->oobsize = 32 * mtd->writesize >> 9;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3172,6 +3291,8 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
chip->cellinfo = id_data[2];
|
chip->cellinfo = id_data[2];
|
||||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||||
chip->options |= type->options;
|
chip->options |= type->options;
|
||||||
|
chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
|
||||||
|
chip->ecc_step_ds = NAND_ECC_STEP(type);
|
||||||
|
|
||||||
*busw = type->options & NAND_BUSWIDTH_16;
|
*busw = type->options & NAND_BUSWIDTH_16;
|
||||||
|
|
||||||
|
@ -3446,12 +3567,6 @@ 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
|
||||||
|
@ -3674,6 +3789,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||||
/* propagate ecc info to mtd_info */
|
/* propagate ecc info to mtd_info */
|
||||||
mtd->ecclayout = chip->ecc.layout;
|
mtd->ecclayout = chip->ecc.layout;
|
||||||
mtd->ecc_strength = chip->ecc.strength;
|
mtd->ecc_strength = chip->ecc.strength;
|
||||||
|
mtd->ecc_step_size = chip->ecc.size;
|
||||||
/*
|
/*
|
||||||
* Initialize bitflip_threshold to its default prior scan_bbt() call.
|
* Initialize bitflip_threshold to its default prior scan_bbt() call.
|
||||||
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
|
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
|
||||||
|
|
|
@ -71,6 +71,30 @@
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
|
||||||
|
#define BBT_BLOCK_GOOD 0x00
|
||||||
|
#define BBT_BLOCK_WORN 0x01
|
||||||
|
#define BBT_BLOCK_RESERVED 0x02
|
||||||
|
#define BBT_BLOCK_FACTORY_BAD 0x03
|
||||||
|
|
||||||
|
#define BBT_ENTRY_MASK 0x03
|
||||||
|
#define BBT_ENTRY_SHIFT 2
|
||||||
|
|
||||||
|
static int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
|
||||||
|
|
||||||
|
static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block)
|
||||||
|
{
|
||||||
|
uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
|
||||||
|
entry >>= (block & BBT_ENTRY_MASK) * 2;
|
||||||
|
return entry & BBT_ENTRY_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bbt_mark_entry(struct nand_chip *chip, int block,
|
||||||
|
uint8_t mark)
|
||||||
|
{
|
||||||
|
uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
|
||||||
|
chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
if (memcmp(buf, td->pattern, td->len))
|
if (memcmp(buf, td->pattern, td->len))
|
||||||
|
@ -86,33 +110,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
|
||||||
* @td: search pattern descriptor
|
* @td: search pattern descriptor
|
||||||
*
|
*
|
||||||
* Check for a pattern at the given place. Used to search bad block tables and
|
* Check for a pattern at the given place. Used to search bad block tables and
|
||||||
* good / bad block identifiers. If the SCAN_EMPTY option is set then check, if
|
* good / bad block identifiers.
|
||||||
* all bytes except the pattern area contain 0xff.
|
|
||||||
*/
|
*/
|
||||||
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 end = 0;
|
|
||||||
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;
|
|
||||||
if (td->options & NAND_BBT_SCANEMPTY)
|
|
||||||
if (memchr_inv(p, 0xff, end))
|
|
||||||
return -1;
|
|
||||||
p += end;
|
|
||||||
|
|
||||||
/* Compare the pattern */
|
/* Compare the pattern */
|
||||||
if (memcmp(p, td->pattern, td->len))
|
if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (td->options & NAND_BBT_SCANEMPTY) {
|
|
||||||
p += td->len;
|
|
||||||
end += td->len;
|
|
||||||
if (memchr_inv(p, 0xff, len - end))
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +167,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
|
||||||
* @page: the starting page
|
* @page: the starting page
|
||||||
* @num: the number of bbt descriptors to read
|
* @num: the number of bbt descriptors to read
|
||||||
* @td: the bbt describtion table
|
* @td: the bbt describtion table
|
||||||
* @offs: offset in the memory table
|
* @offs: block number offset in the table
|
||||||
*
|
*
|
||||||
* Read the bad block table starting from page.
|
* Read the bad block table starting from page.
|
||||||
*/
|
*/
|
||||||
|
@ -209,14 +217,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||||
/* Analyse data */
|
/* Analyse data */
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
uint8_t dat = buf[i];
|
uint8_t dat = buf[i];
|
||||||
for (j = 0; j < 8; j += bits, act += 2) {
|
for (j = 0; j < 8; j += bits, act++) {
|
||||||
uint8_t tmp = (dat >> j) & msk;
|
uint8_t tmp = (dat >> j) & msk;
|
||||||
if (tmp == msk)
|
if (tmp == msk)
|
||||||
continue;
|
continue;
|
||||||
if (reserved_block_code && (tmp == reserved_block_code)) {
|
if (reserved_block_code && (tmp == reserved_block_code)) {
|
||||||
pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
|
pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
|
||||||
(loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
|
(loff_t)(offs + act) <<
|
||||||
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
|
this->bbt_erase_shift);
|
||||||
|
bbt_mark_entry(this, offs + act,
|
||||||
|
BBT_BLOCK_RESERVED);
|
||||||
mtd->ecc_stats.bbtblocks++;
|
mtd->ecc_stats.bbtblocks++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -225,12 +235,15 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||||
* move this message to pr_debug.
|
* move this message to pr_debug.
|
||||||
*/
|
*/
|
||||||
pr_info("nand_read_bbt: bad block at 0x%012llx\n",
|
pr_info("nand_read_bbt: bad block at 0x%012llx\n",
|
||||||
(loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
|
(loff_t)(offs + act) <<
|
||||||
|
this->bbt_erase_shift);
|
||||||
/* Factory marked bad or worn out? */
|
/* Factory marked bad or worn out? */
|
||||||
if (tmp == 0)
|
if (tmp == 0)
|
||||||
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
|
bbt_mark_entry(this, offs + act,
|
||||||
|
BBT_BLOCK_FACTORY_BAD);
|
||||||
else
|
else
|
||||||
this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
|
bbt_mark_entry(this, offs + act,
|
||||||
|
BBT_BLOCK_WORN);
|
||||||
mtd->ecc_stats.badblocks++;
|
mtd->ecc_stats.badblocks++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,7 +278,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
||||||
td, offs);
|
td, offs);
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
offs += this->chipsize >> (this->bbt_erase_shift + 2);
|
offs += this->chipsize >> this->bbt_erase_shift;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res = read_bbt(mtd, buf, td->pages[0],
|
res = read_bbt(mtd, buf, td->pages[0],
|
||||||
|
@ -478,22 +491,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
else
|
else
|
||||||
numpages = 1;
|
numpages = 1;
|
||||||
|
|
||||||
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 */
|
||||||
scanlen = 0;
|
scanlen = 0;
|
||||||
readlen = bd->len;
|
readlen = bd->len;
|
||||||
} else {
|
|
||||||
/* Full page content should be read */
|
|
||||||
scanlen = mtd->writesize + mtd->oobsize;
|
|
||||||
readlen = numpages * mtd->writesize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chip == -1) {
|
if (chip == -1) {
|
||||||
/*
|
numblocks = mtd->size >> this->bbt_erase_shift;
|
||||||
* Note that numblocks is 2 * (real numblocks) here, see i+=2
|
|
||||||
* below as it makes shifting and masking less painful
|
|
||||||
*/
|
|
||||||
numblocks = mtd->size >> (this->bbt_erase_shift - 1);
|
|
||||||
startblock = 0;
|
startblock = 0;
|
||||||
from = 0;
|
from = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -502,16 +505,16 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
chip + 1, this->numchips);
|
chip + 1, this->numchips);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
|
numblocks = this->chipsize >> this->bbt_erase_shift;
|
||||||
startblock = chip * numblocks;
|
startblock = chip * numblocks;
|
||||||
numblocks += startblock;
|
numblocks += startblock;
|
||||||
from = (loff_t)startblock << (this->bbt_erase_shift - 1);
|
from = (loff_t)startblock << this->bbt_erase_shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
|
if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||||
from += mtd->erasesize - (mtd->writesize * numpages);
|
from += mtd->erasesize - (mtd->writesize * numpages);
|
||||||
|
|
||||||
for (i = startblock; i < numblocks;) {
|
for (i = startblock; i < numblocks; i++) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
BUG_ON(bd->options & NAND_BBT_NO_OOB);
|
BUG_ON(bd->options & NAND_BBT_NO_OOB);
|
||||||
|
@ -526,13 +529,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
|
bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD);
|
||||||
pr_warn("Bad eraseblock %d at 0x%012llx\n",
|
pr_warn("Bad eraseblock %d at 0x%012llx\n",
|
||||||
i >> 1, (unsigned long long)from);
|
i, (unsigned long long)from);
|
||||||
mtd->ecc_stats.badblocks++;
|
mtd->ecc_stats.badblocks++;
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 2;
|
|
||||||
from += (1 << this->bbt_erase_shift);
|
from += (1 << this->bbt_erase_shift);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -655,9 +657,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
struct erase_info einfo;
|
struct erase_info einfo;
|
||||||
int i, j, res, chip = 0;
|
int i, res, chip = 0;
|
||||||
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
|
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
|
||||||
int nrchips, bbtoffs, pageoffs, ooboffs;
|
int nrchips, pageoffs, ooboffs;
|
||||||
uint8_t msk[4];
|
uint8_t msk[4];
|
||||||
uint8_t rcode = td->reserved_block_code;
|
uint8_t rcode = td->reserved_block_code;
|
||||||
size_t retlen, len = 0;
|
size_t retlen, len = 0;
|
||||||
|
@ -713,10 +715,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
for (i = 0; i < td->maxblocks; i++) {
|
for (i = 0; i < td->maxblocks; i++) {
|
||||||
int block = startblock + dir * i;
|
int block = startblock + dir * i;
|
||||||
/* Check, if the block is bad */
|
/* Check, if the block is bad */
|
||||||
switch ((this->bbt[block >> 2] >>
|
switch (bbt_get_entry(this, block)) {
|
||||||
(2 * (block & 0x03))) & 0x03) {
|
case BBT_BLOCK_WORN:
|
||||||
case 0x01:
|
case BBT_BLOCK_FACTORY_BAD:
|
||||||
case 0x03:
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
page = block <<
|
page = block <<
|
||||||
|
@ -748,8 +749,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
default: return -EINVAL;
|
default: return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bbtoffs = chip * (numblocks >> 2);
|
|
||||||
|
|
||||||
to = ((loff_t)page) << this->page_shift;
|
to = ((loff_t)page) << this->page_shift;
|
||||||
|
|
||||||
/* Must we save the block contents? */
|
/* Must we save the block contents? */
|
||||||
|
@ -814,16 +813,12 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
buf[ooboffs + td->veroffs] = td->version[chip];
|
buf[ooboffs + td->veroffs] = td->version[chip];
|
||||||
|
|
||||||
/* Walk through the memory table */
|
/* Walk through the memory table */
|
||||||
for (i = 0; i < numblocks;) {
|
for (i = 0; i < numblocks; i++) {
|
||||||
uint8_t dat;
|
uint8_t dat;
|
||||||
dat = this->bbt[bbtoffs + (i >> 2)];
|
|
||||||
for (j = 0; j < 4; j++, i++) {
|
|
||||||
int sftcnt = (i << (3 - sft)) & sftmsk;
|
int sftcnt = (i << (3 - sft)) & sftmsk;
|
||||||
|
dat = bbt_get_entry(this, chip * numblocks + i);
|
||||||
/* Do not store the reserved bbt blocks! */
|
/* Do not store the reserved bbt blocks! */
|
||||||
buf[offs + (i >> sft)] &=
|
buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt);
|
||||||
~(msk[dat & 0x03] << sftcnt);
|
|
||||||
dat >>= 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&einfo, 0, sizeof(einfo));
|
memset(&einfo, 0, sizeof(einfo));
|
||||||
|
@ -865,7 +860,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
|
|
||||||
bd->options &= ~NAND_BBT_SCANEMPTY;
|
|
||||||
return create_bbt(mtd, this->buffers->databuf, bd, -1);
|
return create_bbt(mtd, this->buffers->databuf, bd, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,7 +1003,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
int i, j, chips, block, nrblocks, update;
|
int i, j, chips, block, nrblocks, update;
|
||||||
uint8_t oldval, newval;
|
uint8_t oldval;
|
||||||
|
|
||||||
/* Do we have a bbt per chip? */
|
/* Do we have a bbt per chip? */
|
||||||
if (td->options & NAND_BBT_PERCHIP) {
|
if (td->options & NAND_BBT_PERCHIP) {
|
||||||
|
@ -1026,12 +1020,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||||
if (td->pages[i] == -1)
|
if (td->pages[i] == -1)
|
||||||
continue;
|
continue;
|
||||||
block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
|
block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
|
||||||
block <<= 1;
|
oldval = bbt_get_entry(this, block);
|
||||||
oldval = this->bbt[(block >> 3)];
|
bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
|
||||||
newval = oldval | (0x2 << (block & 0x06));
|
if ((oldval != BBT_BLOCK_RESERVED) &&
|
||||||
this->bbt[(block >> 3)] = newval;
|
td->reserved_block_code)
|
||||||
if ((oldval != newval) && td->reserved_block_code)
|
nand_update_bbt(mtd, (loff_t)block <<
|
||||||
nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1));
|
this->bbt_erase_shift);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
update = 0;
|
update = 0;
|
||||||
|
@ -1039,14 +1033,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||||
block = ((i + 1) * nrblocks) - td->maxblocks;
|
block = ((i + 1) * nrblocks) - td->maxblocks;
|
||||||
else
|
else
|
||||||
block = i * nrblocks;
|
block = i * nrblocks;
|
||||||
block <<= 1;
|
|
||||||
for (j = 0; j < td->maxblocks; j++) {
|
for (j = 0; j < td->maxblocks; j++) {
|
||||||
oldval = this->bbt[(block >> 3)];
|
oldval = bbt_get_entry(this, block);
|
||||||
newval = oldval | (0x2 << (block & 0x06));
|
bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
|
||||||
this->bbt[(block >> 3)] = newval;
|
if (oldval != BBT_BLOCK_RESERVED)
|
||||||
if (oldval != newval)
|
|
||||||
update = 1;
|
update = 1;
|
||||||
block += 2;
|
block++;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* If we want reserved blocks to be recorded to flash, and some
|
* If we want reserved blocks to be recorded to flash, and some
|
||||||
|
@ -1054,7 +1046,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||||
* bbts. This should only happen once.
|
* bbts. This should only happen once.
|
||||||
*/
|
*/
|
||||||
if (update && td->reserved_block_code)
|
if (update && td->reserved_block_code)
|
||||||
nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));
|
nand_update_bbt(mtd, (loff_t)(block - 1) <<
|
||||||
|
this->bbt_erase_shift);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,13 +1173,13 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_update_bbt - [NAND Interface] update bad block table(s)
|
* nand_update_bbt - update bad block table(s)
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
* @offs: the offset of the newly marked block
|
* @offs: the offset of the newly marked block
|
||||||
*
|
*
|
||||||
* The function updates the bad block table(s).
|
* The function updates the bad block table(s).
|
||||||
*/
|
*/
|
||||||
int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
|
static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
int len, res = 0;
|
int len, res = 0;
|
||||||
|
@ -1356,28 +1349,47 @@ int nand_default_bbt(struct mtd_info *mtd)
|
||||||
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
int block;
|
int block, res;
|
||||||
uint8_t res;
|
|
||||||
|
|
||||||
/* Get block number * 2 */
|
block = (int)(offs >> this->bbt_erase_shift);
|
||||||
block = (int)(offs >> (this->bbt_erase_shift - 1));
|
res = bbt_get_entry(this, block);
|
||||||
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
|
|
||||||
|
|
||||||
pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: "
|
pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: "
|
||||||
"(block %d) 0x%02x\n",
|
"(block %d) 0x%02x\n",
|
||||||
(unsigned int)offs, block >> 1, res);
|
(unsigned int)offs, block, res);
|
||||||
|
|
||||||
switch ((int)res) {
|
switch (res) {
|
||||||
case 0x00:
|
case BBT_BLOCK_GOOD:
|
||||||
return 0;
|
return 0;
|
||||||
case 0x01:
|
case BBT_BLOCK_WORN:
|
||||||
return 1;
|
return 1;
|
||||||
case 0x02:
|
case BBT_BLOCK_RESERVED:
|
||||||
return allowbbt ? 0 : 1;
|
return allowbbt ? 0 : 1;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @offs: offset of the bad block
|
||||||
|
*/
|
||||||
|
int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
|
||||||
|
{
|
||||||
|
struct nand_chip *this = mtd->priv;
|
||||||
|
int block, ret = 0;
|
||||||
|
|
||||||
|
block = (int)(offs >> this->bbt_erase_shift);
|
||||||
|
|
||||||
|
/* Mark bad block in memory */
|
||||||
|
bbt_mark_entry(this, block, BBT_BLOCK_WORN);
|
||||||
|
|
||||||
|
/* Update flash-based bad block table */
|
||||||
|
if (this->bbt_options & NAND_BBT_USE_FLASH)
|
||||||
|
ret = nand_update_bbt(mtd, offs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
|
@ -33,16 +33,16 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||||
*/
|
*/
|
||||||
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
||||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||||
SZ_4K, SZ_512, SZ_256K, 0, 8, 224},
|
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
|
||||||
{"TC58NVG3S0F 8G 3.3V 8-bit",
|
{"TC58NVG3S0F 8G 3.3V 8-bit",
|
||||||
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
||||||
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232},
|
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
|
||||||
{"TC58NVG5D2 32G 3.3V 8-bit",
|
{"TC58NVG5D2 32G 3.3V 8-bit",
|
||||||
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
|
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
|
||||||
SZ_8K, SZ_4K, SZ_1M, 0, 8, 640},
|
SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
|
||||||
{"TC58NVG6D2 64G 3.3V 8-bit",
|
{"TC58NVG6D2 64G 3.3V 8-bit",
|
||||||
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
|
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
|
||||||
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640},
|
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
|
||||||
|
|
||||||
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
|
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
|
||||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||||
|
|
|
@ -205,7 +205,7 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
|
||||||
|
|
||||||
/* Calculate the page offset in flash RAM image by (row, column) address */
|
/* Calculate the page offset in flash RAM image by (row, column) address */
|
||||||
#define NS_RAW_OFFSET(ns) \
|
#define NS_RAW_OFFSET(ns) \
|
||||||
(((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column)
|
(((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column)
|
||||||
|
|
||||||
/* Calculate the OOB offset in flash RAM image by (row, column) address */
|
/* Calculate the OOB offset in flash RAM image by (row, column) address */
|
||||||
#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
|
#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
|
||||||
|
@ -336,7 +336,6 @@ struct nandsim {
|
||||||
uint pgsec; /* number of pages per sector */
|
uint pgsec; /* number of pages per sector */
|
||||||
uint secshift; /* bits number in sector size */
|
uint secshift; /* bits number in sector size */
|
||||||
uint pgshift; /* bits number in page size */
|
uint pgshift; /* bits number in page size */
|
||||||
uint oobshift; /* bits number in OOB size */
|
|
||||||
uint pgaddrbytes; /* bytes per page address */
|
uint pgaddrbytes; /* bytes per page address */
|
||||||
uint secaddrbytes; /* bytes per sector address */
|
uint secaddrbytes; /* bytes per sector address */
|
||||||
uint idbytes; /* the number ID bytes that this chip outputs */
|
uint idbytes; /* the number ID bytes that this chip outputs */
|
||||||
|
@ -363,7 +362,7 @@ struct nandsim {
|
||||||
|
|
||||||
/* Fields needed when using a cache file */
|
/* Fields needed when using a cache file */
|
||||||
struct file *cfile; /* Open file */
|
struct file *cfile; /* Open file */
|
||||||
unsigned char *pages_written; /* Which pages have been written */
|
unsigned long *pages_written; /* Which pages have been written */
|
||||||
void *file_buf;
|
void *file_buf;
|
||||||
struct page *held_pages[NS_MAX_HELD_PAGES];
|
struct page *held_pages[NS_MAX_HELD_PAGES];
|
||||||
int held_cnt;
|
int held_cnt;
|
||||||
|
@ -586,7 +585,8 @@ static int alloc_device(struct nandsim *ns)
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto err_close;
|
goto err_close;
|
||||||
}
|
}
|
||||||
ns->pages_written = vzalloc(ns->geom.pgnum);
|
ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) *
|
||||||
|
sizeof(unsigned long));
|
||||||
if (!ns->pages_written) {
|
if (!ns->pages_written) {
|
||||||
NS_ERR("alloc_device: unable to allocate pages written array\n");
|
NS_ERR("alloc_device: unable to allocate pages written array\n");
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -653,9 +653,7 @@ static void free_device(struct nandsim *ns)
|
||||||
|
|
||||||
static char *get_partition_name(int i)
|
static char *get_partition_name(int i)
|
||||||
{
|
{
|
||||||
char buf[64];
|
return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i);
|
||||||
sprintf(buf, "NAND simulator partition %d", i);
|
|
||||||
return kstrdup(buf, GFP_KERNEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -690,7 +688,6 @@ static int init_nandsim(struct mtd_info *mtd)
|
||||||
ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz;
|
ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz;
|
||||||
ns->geom.secshift = ffs(ns->geom.secsz) - 1;
|
ns->geom.secshift = ffs(ns->geom.secsz) - 1;
|
||||||
ns->geom.pgshift = chip->page_shift;
|
ns->geom.pgshift = chip->page_shift;
|
||||||
ns->geom.oobshift = ffs(ns->geom.oobsz) - 1;
|
|
||||||
ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz;
|
ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz;
|
||||||
ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
|
ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
|
||||||
ns->options = 0;
|
ns->options = 0;
|
||||||
|
@ -761,12 +758,6 @@ static int init_nandsim(struct mtd_info *mtd)
|
||||||
ns->nbparts += 1;
|
ns->nbparts += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detect how many ID bytes the NAND chip outputs */
|
|
||||||
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
|
||||||
if (second_id_byte != nand_flash_ids[i].dev_id)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ns->busw == 16)
|
if (ns->busw == 16)
|
||||||
NS_WARN("16-bit flashes support wasn't tested\n");
|
NS_WARN("16-bit flashes support wasn't tested\n");
|
||||||
|
|
||||||
|
@ -780,7 +771,7 @@ static int init_nandsim(struct mtd_info *mtd)
|
||||||
printk("bus width: %u\n", ns->busw);
|
printk("bus width: %u\n", ns->busw);
|
||||||
printk("bits in sector size: %u\n", ns->geom.secshift);
|
printk("bits in sector size: %u\n", ns->geom.secshift);
|
||||||
printk("bits in page size: %u\n", ns->geom.pgshift);
|
printk("bits in page size: %u\n", ns->geom.pgshift);
|
||||||
printk("bits in OOB size: %u\n", ns->geom.oobshift);
|
printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1);
|
||||||
printk("flash size with OOB: %llu KiB\n",
|
printk("flash size with OOB: %llu KiB\n",
|
||||||
(unsigned long long)ns->geom.totszoob >> 10);
|
(unsigned long long)ns->geom.totszoob >> 10);
|
||||||
printk("page address bytes: %u\n", ns->geom.pgaddrbytes);
|
printk("page address bytes: %u\n", ns->geom.pgaddrbytes);
|
||||||
|
@ -1442,7 +1433,7 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
|
||||||
return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
|
return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_read_error(struct nandsim *ns, int num)
|
static int do_read_error(struct nandsim *ns, int num)
|
||||||
{
|
{
|
||||||
unsigned int page_no = ns->regs.row;
|
unsigned int page_no = ns->regs.row;
|
||||||
|
|
||||||
|
@ -1454,7 +1445,7 @@ int do_read_error(struct nandsim *ns, int num)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_bit_flips(struct nandsim *ns, int num)
|
static void do_bit_flips(struct nandsim *ns, int num)
|
||||||
{
|
{
|
||||||
if (bitflips && prandom_u32() < (1 << 22)) {
|
if (bitflips && prandom_u32() < (1 << 22)) {
|
||||||
int flips = 1;
|
int flips = 1;
|
||||||
|
@ -1479,7 +1470,7 @@ static void read_page(struct nandsim *ns, int num)
|
||||||
union ns_mem *mypage;
|
union ns_mem *mypage;
|
||||||
|
|
||||||
if (ns->cfile) {
|
if (ns->cfile) {
|
||||||
if (!ns->pages_written[ns->regs.row]) {
|
if (!test_bit(ns->regs.row, ns->pages_written)) {
|
||||||
NS_DBG("read_page: page %d not written\n", ns->regs.row);
|
NS_DBG("read_page: page %d not written\n", ns->regs.row);
|
||||||
memset(ns->buf.byte, 0xFF, num);
|
memset(ns->buf.byte, 0xFF, num);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1490,7 +1481,7 @@ static void read_page(struct nandsim *ns, int num)
|
||||||
ns->regs.row, ns->regs.column + ns->regs.off);
|
ns->regs.row, ns->regs.column + ns->regs.off);
|
||||||
if (do_read_error(ns, num))
|
if (do_read_error(ns, num))
|
||||||
return;
|
return;
|
||||||
pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off;
|
pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
|
||||||
tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos);
|
tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos);
|
||||||
if (tx != num) {
|
if (tx != num) {
|
||||||
NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
|
NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
|
||||||
|
@ -1525,9 +1516,9 @@ static void erase_sector(struct nandsim *ns)
|
||||||
|
|
||||||
if (ns->cfile) {
|
if (ns->cfile) {
|
||||||
for (i = 0; i < ns->geom.pgsec; i++)
|
for (i = 0; i < ns->geom.pgsec; i++)
|
||||||
if (ns->pages_written[ns->regs.row + i]) {
|
if (__test_and_clear_bit(ns->regs.row + i,
|
||||||
|
ns->pages_written)) {
|
||||||
NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i);
|
NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i);
|
||||||
ns->pages_written[ns->regs.row + i] = 0;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1559,8 +1550,8 @@ static int prog_page(struct nandsim *ns, int num)
|
||||||
|
|
||||||
NS_DBG("prog_page: writing page %d\n", ns->regs.row);
|
NS_DBG("prog_page: writing page %d\n", ns->regs.row);
|
||||||
pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
|
pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
|
||||||
off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off;
|
off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
|
||||||
if (!ns->pages_written[ns->regs.row]) {
|
if (!test_bit(ns->regs.row, ns->pages_written)) {
|
||||||
all = 1;
|
all = 1;
|
||||||
memset(ns->file_buf, 0xff, ns->geom.pgszoob);
|
memset(ns->file_buf, 0xff, ns->geom.pgszoob);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1580,7 +1571,7 @@ static int prog_page(struct nandsim *ns, int num)
|
||||||
NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
|
NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ns->pages_written[ns->regs.row] = 1;
|
__set_bit(ns->regs.row, ns->pages_written);
|
||||||
} else {
|
} else {
|
||||||
tx = write_file(ns, ns->cfile, pg_off, num, off);
|
tx = write_file(ns, ns->cfile, pg_off, num, off);
|
||||||
if (tx != num) {
|
if (tx != num) {
|
||||||
|
|
|
@ -324,8 +324,6 @@ static int nuc900_nand_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
kfree(nuc900_nand);
|
kfree(nuc900_nand);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ static struct nand_ecclayout omap_oobinfo;
|
||||||
*/
|
*/
|
||||||
static uint8_t scan_ff_pattern[] = { 0xff };
|
static uint8_t scan_ff_pattern[] = { 0xff };
|
||||||
static struct nand_bbt_descr bb_descrip_flashbased = {
|
static struct nand_bbt_descr bb_descrip_flashbased = {
|
||||||
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
|
.options = NAND_BBT_SCANALLPAGES,
|
||||||
.offs = 0,
|
.offs = 0,
|
||||||
.len = 1,
|
.len = 1,
|
||||||
.pattern = scan_ff_pattern,
|
.pattern = scan_ff_pattern,
|
||||||
|
@ -1831,7 +1831,7 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct mtd_part_parser_data ppdata = {};
|
struct mtd_part_parser_data ppdata = {};
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
if (pdata == NULL) {
|
if (pdata == NULL) {
|
||||||
dev_err(&pdev->dev, "platform data missing\n");
|
dev_err(&pdev->dev, "platform data missing\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -2087,7 +2087,6 @@ static int omap_nand_remove(struct platform_device *pdev)
|
||||||
mtd);
|
mtd);
|
||||||
omap3_free_bch(&info->mtd);
|
omap3_free_bch(&info->mtd);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
if (info->dma)
|
if (info->dma)
|
||||||
dma_release_channel(info->dma);
|
dma_release_channel(info->dma);
|
||||||
|
|
||||||
|
|
|
@ -130,8 +130,9 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||||
if (!of_property_read_u32(pdev->dev.of_node,
|
if (!of_property_read_u32(pdev->dev.of_node,
|
||||||
"chip-delay", &val))
|
"chip-delay", &val))
|
||||||
board->chip_delay = (u8)val;
|
board->chip_delay = (u8)val;
|
||||||
} else
|
} else {
|
||||||
board = pdev->dev.platform_data;
|
board = dev_get_platdata(&pdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
mtd->priv = nc;
|
mtd->priv = nc;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->owner = THIS_MODULE;
|
||||||
|
@ -186,7 +187,6 @@ no_dev:
|
||||||
clk_disable_unprepare(clk);
|
clk_disable_unprepare(clk);
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
}
|
}
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
iounmap(io_base);
|
iounmap(io_base);
|
||||||
no_res:
|
no_res:
|
||||||
kfree(nc);
|
kfree(nc);
|
||||||
|
|
|
@ -30,7 +30,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL };
|
||||||
*/
|
*/
|
||||||
static int plat_nand_probe(struct platform_device *pdev)
|
static int plat_nand_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct mtd_part_parser_data ppdata;
|
struct mtd_part_parser_data ppdata;
|
||||||
struct plat_nand_data *data;
|
struct plat_nand_data *data;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
@ -122,7 +122,6 @@ static int plat_nand_probe(struct platform_device *pdev)
|
||||||
out:
|
out:
|
||||||
if (pdata->ctrl.remove)
|
if (pdata->ctrl.remove)
|
||||||
pdata->ctrl.remove(pdev);
|
pdata->ctrl.remove(pdev);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
iounmap(data->io_base);
|
iounmap(data->io_base);
|
||||||
out_release_io:
|
out_release_io:
|
||||||
release_mem_region(res->start, resource_size(res));
|
release_mem_region(res->start, resource_size(res));
|
||||||
|
@ -137,7 +136,7 @@ out_free:
|
||||||
static int plat_nand_remove(struct platform_device *pdev)
|
static int plat_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct plat_nand_data *data = platform_get_drvdata(pdev);
|
struct plat_nand_data *data = platform_get_drvdata(pdev);
|
||||||
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
|
|
@ -25,7 +25,14 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
|
||||||
|
#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
|
||||||
|
#define ARCH_HAS_DMA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARCH_HAS_DMA
|
||||||
#include <mach/dma.h>
|
#include <mach/dma.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
||||||
|
|
||||||
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
|
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
|
||||||
|
@ -80,6 +87,7 @@
|
||||||
#define NDSR_RDDREQ (0x1 << 1)
|
#define NDSR_RDDREQ (0x1 << 1)
|
||||||
#define NDSR_WRCMDREQ (0x1)
|
#define NDSR_WRCMDREQ (0x1)
|
||||||
|
|
||||||
|
#define NDCB0_LEN_OVRD (0x1 << 28)
|
||||||
#define NDCB0_ST_ROW_EN (0x1 << 26)
|
#define NDCB0_ST_ROW_EN (0x1 << 26)
|
||||||
#define NDCB0_AUTO_RS (0x1 << 25)
|
#define NDCB0_AUTO_RS (0x1 << 25)
|
||||||
#define NDCB0_CSEL (0x1 << 24)
|
#define NDCB0_CSEL (0x1 << 24)
|
||||||
|
@ -123,9 +131,13 @@ enum {
|
||||||
STATE_READY,
|
STATE_READY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum pxa3xx_nand_variant {
|
||||||
|
PXA3XX_NAND_VARIANT_PXA,
|
||||||
|
PXA3XX_NAND_VARIANT_ARMADA370,
|
||||||
|
};
|
||||||
|
|
||||||
struct pxa3xx_nand_host {
|
struct pxa3xx_nand_host {
|
||||||
struct nand_chip chip;
|
struct nand_chip chip;
|
||||||
struct pxa3xx_nand_cmdset *cmdset;
|
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
void *info_data;
|
void *info_data;
|
||||||
|
|
||||||
|
@ -139,10 +151,6 @@ struct pxa3xx_nand_host {
|
||||||
unsigned int row_addr_cycles;
|
unsigned int row_addr_cycles;
|
||||||
size_t read_id_bytes;
|
size_t read_id_bytes;
|
||||||
|
|
||||||
/* cached register value */
|
|
||||||
uint32_t reg_ndcr;
|
|
||||||
uint32_t ndtr0cs0;
|
|
||||||
uint32_t ndtr1cs0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pxa3xx_nand_info {
|
struct pxa3xx_nand_info {
|
||||||
|
@ -171,9 +179,16 @@ struct pxa3xx_nand_info {
|
||||||
struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
|
struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
|
||||||
unsigned int state;
|
unsigned int state;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This driver supports NFCv1 (as found in PXA SoC)
|
||||||
|
* and NFCv2 (as found in Armada 370/XP SoC).
|
||||||
|
*/
|
||||||
|
enum pxa3xx_nand_variant variant;
|
||||||
|
|
||||||
int cs;
|
int cs;
|
||||||
int use_ecc; /* use HW ECC ? */
|
int use_ecc; /* use HW ECC ? */
|
||||||
int use_dma; /* use DMA ? */
|
int use_dma; /* use DMA ? */
|
||||||
|
int use_spare; /* use spare ? */
|
||||||
int is_ready;
|
int is_ready;
|
||||||
|
|
||||||
unsigned int page_size; /* page size of attached chip */
|
unsigned int page_size; /* page size of attached chip */
|
||||||
|
@ -181,33 +196,22 @@ struct pxa3xx_nand_info {
|
||||||
unsigned int oob_size;
|
unsigned int oob_size;
|
||||||
int retcode;
|
int retcode;
|
||||||
|
|
||||||
|
/* cached register value */
|
||||||
|
uint32_t reg_ndcr;
|
||||||
|
uint32_t ndtr0cs0;
|
||||||
|
uint32_t ndtr1cs0;
|
||||||
|
|
||||||
/* generated NDCBx register values */
|
/* generated NDCBx register values */
|
||||||
uint32_t ndcb0;
|
uint32_t ndcb0;
|
||||||
uint32_t ndcb1;
|
uint32_t ndcb1;
|
||||||
uint32_t ndcb2;
|
uint32_t ndcb2;
|
||||||
|
uint32_t ndcb3;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool use_dma = 1;
|
static bool use_dma = 1;
|
||||||
module_param(use_dma, bool, 0444);
|
module_param(use_dma, bool, 0444);
|
||||||
MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
|
MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
|
||||||
|
|
||||||
/*
|
|
||||||
* Default NAND flash controller configuration setup by the
|
|
||||||
* bootloader. This configuration is used only when pdata->keep_config is set
|
|
||||||
*/
|
|
||||||
static struct pxa3xx_nand_cmdset default_cmdset = {
|
|
||||||
.read1 = 0x3000,
|
|
||||||
.read2 = 0x0050,
|
|
||||||
.program = 0x1080,
|
|
||||||
.read_status = 0x0070,
|
|
||||||
.read_id = 0x0090,
|
|
||||||
.erase = 0xD060,
|
|
||||||
.reset = 0x00FF,
|
|
||||||
.lock = 0x002A,
|
|
||||||
.unlock = 0x2423,
|
|
||||||
.lock_status = 0x007A,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct pxa3xx_nand_timing timing[] = {
|
static struct pxa3xx_nand_timing timing[] = {
|
||||||
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
||||||
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
||||||
|
@ -230,8 +234,6 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
||||||
/* Define a default flash type setting serve as flash detecting only */
|
/* Define a default flash type setting serve as flash detecting only */
|
||||||
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
|
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
|
||||||
|
|
||||||
const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
|
|
||||||
|
|
||||||
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
||||||
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
||||||
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
||||||
|
@ -264,8 +266,8 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
|
||||||
NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
|
NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
|
||||||
NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
|
NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
|
||||||
|
|
||||||
host->ndtr0cs0 = ndtr0;
|
info->ndtr0cs0 = ndtr0;
|
||||||
host->ndtr1cs0 = ndtr1;
|
info->ndtr1cs0 = ndtr1;
|
||||||
nand_writel(info, NDTR0CS0, ndtr0);
|
nand_writel(info, NDTR0CS0, ndtr0);
|
||||||
nand_writel(info, NDTR1CS0, ndtr1);
|
nand_writel(info, NDTR1CS0, ndtr1);
|
||||||
}
|
}
|
||||||
|
@ -273,7 +275,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
|
||||||
static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
|
static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct pxa3xx_nand_host *host = info->host[info->cs];
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
||||||
int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
|
int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
|
||||||
|
|
||||||
info->data_size = host->page_size;
|
info->data_size = host->page_size;
|
||||||
if (!oob_enable) {
|
if (!oob_enable) {
|
||||||
|
@ -299,12 +301,25 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
|
||||||
*/
|
*/
|
||||||
static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
|
static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct pxa3xx_nand_host *host = info->host[info->cs];
|
|
||||||
uint32_t ndcr;
|
uint32_t ndcr;
|
||||||
|
|
||||||
ndcr = host->reg_ndcr;
|
ndcr = info->reg_ndcr;
|
||||||
ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
|
|
||||||
ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
|
if (info->use_ecc)
|
||||||
|
ndcr |= NDCR_ECC_EN;
|
||||||
|
else
|
||||||
|
ndcr &= ~NDCR_ECC_EN;
|
||||||
|
|
||||||
|
if (info->use_dma)
|
||||||
|
ndcr |= NDCR_DMA_EN;
|
||||||
|
else
|
||||||
|
ndcr &= ~NDCR_DMA_EN;
|
||||||
|
|
||||||
|
if (info->use_spare)
|
||||||
|
ndcr |= NDCR_SPARE_EN;
|
||||||
|
else
|
||||||
|
ndcr &= ~NDCR_SPARE_EN;
|
||||||
|
|
||||||
ndcr |= NDCR_ND_RUN;
|
ndcr |= NDCR_ND_RUN;
|
||||||
|
|
||||||
/* clear status bits and run */
|
/* clear status bits and run */
|
||||||
|
@ -333,7 +348,8 @@ static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
|
||||||
nand_writel(info, NDSR, NDSR_MASK);
|
nand_writel(info, NDSR, NDSR_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
|
static void __maybe_unused
|
||||||
|
enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
|
||||||
{
|
{
|
||||||
uint32_t ndcr;
|
uint32_t ndcr;
|
||||||
|
|
||||||
|
@ -373,6 +389,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ARCH_HAS_DMA
|
||||||
static void start_data_dma(struct pxa3xx_nand_info *info)
|
static void start_data_dma(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct pxa_dma_desc *desc = info->data_desc;
|
struct pxa_dma_desc *desc = info->data_desc;
|
||||||
|
@ -419,6 +436,10 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data)
|
||||||
enable_int(info, NDCR_INT_MASK);
|
enable_int(info, NDCR_INT_MASK);
|
||||||
nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
|
nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static void start_data_dma(struct pxa3xx_nand_info *info)
|
||||||
|
{}
|
||||||
|
#endif
|
||||||
|
|
||||||
static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
|
static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
|
||||||
{
|
{
|
||||||
|
@ -467,9 +488,22 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
|
||||||
nand_writel(info, NDSR, NDSR_WRCMDREQ);
|
nand_writel(info, NDSR, NDSR_WRCMDREQ);
|
||||||
status &= ~NDSR_WRCMDREQ;
|
status &= ~NDSR_WRCMDREQ;
|
||||||
info->state = STATE_CMD_HANDLE;
|
info->state = STATE_CMD_HANDLE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Command buffer registers NDCB{0-2} (and optionally NDCB3)
|
||||||
|
* must be loaded by writing directly either 12 or 16
|
||||||
|
* bytes directly to NDCB0, four bytes at a time.
|
||||||
|
*
|
||||||
|
* Direct write access to NDCB1, NDCB2 and NDCB3 is ignored
|
||||||
|
* but each NDCBx register can be read.
|
||||||
|
*/
|
||||||
nand_writel(info, NDCB0, info->ndcb0);
|
nand_writel(info, NDCB0, info->ndcb0);
|
||||||
nand_writel(info, NDCB0, info->ndcb1);
|
nand_writel(info, NDCB0, info->ndcb1);
|
||||||
nand_writel(info, NDCB0, info->ndcb2);
|
nand_writel(info, NDCB0, info->ndcb2);
|
||||||
|
|
||||||
|
/* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */
|
||||||
|
if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
|
||||||
|
nand_writel(info, NDCB0, info->ndcb3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clear NDSR to let the controller exit the IRQ */
|
/* clear NDSR to let the controller exit the IRQ */
|
||||||
|
@ -491,7 +525,6 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
|
||||||
static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
||||||
uint16_t column, int page_addr)
|
uint16_t column, int page_addr)
|
||||||
{
|
{
|
||||||
uint16_t cmd;
|
|
||||||
int addr_cycle, exec_cmd;
|
int addr_cycle, exec_cmd;
|
||||||
struct pxa3xx_nand_host *host;
|
struct pxa3xx_nand_host *host;
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
|
@ -506,6 +539,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
||||||
info->buf_count = 0;
|
info->buf_count = 0;
|
||||||
info->oob_size = 0;
|
info->oob_size = 0;
|
||||||
info->use_ecc = 0;
|
info->use_ecc = 0;
|
||||||
|
info->use_spare = 1;
|
||||||
|
info->use_dma = (use_dma) ? 1 : 0;
|
||||||
info->is_ready = 0;
|
info->is_ready = 0;
|
||||||
info->retcode = ERR_NONE;
|
info->retcode = ERR_NONE;
|
||||||
if (info->cs != 0)
|
if (info->cs != 0)
|
||||||
|
@ -520,12 +555,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
||||||
case NAND_CMD_READOOB:
|
case NAND_CMD_READOOB:
|
||||||
pxa3xx_set_datasize(info);
|
pxa3xx_set_datasize(info);
|
||||||
break;
|
break;
|
||||||
|
case NAND_CMD_PARAM:
|
||||||
|
info->use_spare = 0;
|
||||||
|
break;
|
||||||
case NAND_CMD_SEQIN:
|
case NAND_CMD_SEQIN:
|
||||||
exec_cmd = 0;
|
exec_cmd = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
info->ndcb1 = 0;
|
info->ndcb1 = 0;
|
||||||
info->ndcb2 = 0;
|
info->ndcb2 = 0;
|
||||||
|
info->ndcb3 = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,21 +574,17 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case NAND_CMD_READOOB:
|
case NAND_CMD_READOOB:
|
||||||
case NAND_CMD_READ0:
|
case NAND_CMD_READ0:
|
||||||
cmd = host->cmdset->read1;
|
|
||||||
if (command == NAND_CMD_READOOB)
|
|
||||||
info->buf_start = mtd->writesize + column;
|
|
||||||
else
|
|
||||||
info->buf_start = column;
|
info->buf_start = column;
|
||||||
|
info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
||||||
|
| addr_cycle
|
||||||
|
| NAND_CMD_READ0;
|
||||||
|
|
||||||
if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
|
if (command == NAND_CMD_READOOB)
|
||||||
info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
info->buf_start += mtd->writesize;
|
||||||
| addr_cycle
|
|
||||||
| (cmd & NDCB0_CMD1_MASK);
|
/* Second command setting for large pages */
|
||||||
else
|
if (host->page_size >= PAGE_CHUNK_SIZE)
|
||||||
info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
|
||||||
| NDCB0_DBC
|
|
||||||
| addr_cycle
|
|
||||||
| cmd;
|
|
||||||
|
|
||||||
case NAND_CMD_SEQIN:
|
case NAND_CMD_SEQIN:
|
||||||
/* small page addr setting */
|
/* small page addr setting */
|
||||||
|
@ -580,49 +615,58 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = host->cmdset->program;
|
|
||||||
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
|
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
|
||||||
| NDCB0_AUTO_RS
|
| NDCB0_AUTO_RS
|
||||||
| NDCB0_ST_ROW_EN
|
| NDCB0_ST_ROW_EN
|
||||||
| NDCB0_DBC
|
| NDCB0_DBC
|
||||||
| cmd
|
| (NAND_CMD_PAGEPROG << 8)
|
||||||
|
| NAND_CMD_SEQIN
|
||||||
| addr_cycle;
|
| addr_cycle;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_PARAM:
|
||||||
|
info->buf_count = 256;
|
||||||
|
info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
||||||
|
| NDCB0_ADDR_CYC(1)
|
||||||
|
| NDCB0_LEN_OVRD
|
||||||
|
| command;
|
||||||
|
info->ndcb1 = (column & 0xFF);
|
||||||
|
info->ndcb3 = 256;
|
||||||
|
info->data_size = 256;
|
||||||
|
break;
|
||||||
|
|
||||||
case NAND_CMD_READID:
|
case NAND_CMD_READID:
|
||||||
cmd = host->cmdset->read_id;
|
|
||||||
info->buf_count = host->read_id_bytes;
|
info->buf_count = host->read_id_bytes;
|
||||||
info->ndcb0 |= NDCB0_CMD_TYPE(3)
|
info->ndcb0 |= NDCB0_CMD_TYPE(3)
|
||||||
| NDCB0_ADDR_CYC(1)
|
| NDCB0_ADDR_CYC(1)
|
||||||
| cmd;
|
| command;
|
||||||
|
info->ndcb1 = (column & 0xFF);
|
||||||
|
|
||||||
info->data_size = 8;
|
info->data_size = 8;
|
||||||
break;
|
break;
|
||||||
case NAND_CMD_STATUS:
|
case NAND_CMD_STATUS:
|
||||||
cmd = host->cmdset->read_status;
|
|
||||||
info->buf_count = 1;
|
info->buf_count = 1;
|
||||||
info->ndcb0 |= NDCB0_CMD_TYPE(4)
|
info->ndcb0 |= NDCB0_CMD_TYPE(4)
|
||||||
| NDCB0_ADDR_CYC(1)
|
| NDCB0_ADDR_CYC(1)
|
||||||
| cmd;
|
| command;
|
||||||
|
|
||||||
info->data_size = 8;
|
info->data_size = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NAND_CMD_ERASE1:
|
case NAND_CMD_ERASE1:
|
||||||
cmd = host->cmdset->erase;
|
|
||||||
info->ndcb0 |= NDCB0_CMD_TYPE(2)
|
info->ndcb0 |= NDCB0_CMD_TYPE(2)
|
||||||
| NDCB0_AUTO_RS
|
| NDCB0_AUTO_RS
|
||||||
| NDCB0_ADDR_CYC(3)
|
| NDCB0_ADDR_CYC(3)
|
||||||
| NDCB0_DBC
|
| NDCB0_DBC
|
||||||
| cmd;
|
| (NAND_CMD_ERASE2 << 8)
|
||||||
|
| NAND_CMD_ERASE1;
|
||||||
info->ndcb1 = page_addr;
|
info->ndcb1 = page_addr;
|
||||||
info->ndcb2 = 0;
|
info->ndcb2 = 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NAND_CMD_RESET:
|
case NAND_CMD_RESET:
|
||||||
cmd = host->cmdset->reset;
|
|
||||||
info->ndcb0 |= NDCB0_CMD_TYPE(5)
|
info->ndcb0 |= NDCB0_CMD_TYPE(5)
|
||||||
| cmd;
|
| command;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -652,7 +696,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
||||||
* "byte" address into a "word" address appropriate
|
* "byte" address into a "word" address appropriate
|
||||||
* for indexing a word-oriented device
|
* for indexing a word-oriented device
|
||||||
*/
|
*/
|
||||||
if (host->reg_ndcr & NDCR_DWIDTH_M)
|
if (info->reg_ndcr & NDCR_DWIDTH_M)
|
||||||
column /= 2;
|
column /= 2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -662,8 +706,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
||||||
*/
|
*/
|
||||||
if (info->cs != host->cs) {
|
if (info->cs != host->cs) {
|
||||||
info->cs = host->cs;
|
info->cs = host->cs;
|
||||||
nand_writel(info, NDTR0CS0, host->ndtr0cs0);
|
nand_writel(info, NDTR0CS0, info->ndtr0cs0);
|
||||||
nand_writel(info, NDTR1CS0, host->ndtr1cs0);
|
nand_writel(info, NDTR1CS0, info->ndtr1cs0);
|
||||||
}
|
}
|
||||||
|
|
||||||
info->state = STATE_PREPARED;
|
info->state = STATE_PREPARED;
|
||||||
|
@ -803,7 +847,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
|
||||||
const struct pxa3xx_nand_flash *f)
|
const struct pxa3xx_nand_flash *f)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = info->pdev;
|
struct platform_device *pdev = info->pdev;
|
||||||
struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
|
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct pxa3xx_nand_host *host = info->host[info->cs];
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
||||||
uint32_t ndcr = 0x0; /* enable all interrupts */
|
uint32_t ndcr = 0x0; /* enable all interrupts */
|
||||||
|
|
||||||
|
@ -818,7 +862,6 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calculate flash information */
|
/* calculate flash information */
|
||||||
host->cmdset = &default_cmdset;
|
|
||||||
host->page_size = f->page_size;
|
host->page_size = f->page_size;
|
||||||
host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
|
host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
|
||||||
|
|
||||||
|
@ -840,7 +883,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
|
||||||
ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
|
ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
|
||||||
ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
||||||
|
|
||||||
host->reg_ndcr = ndcr;
|
info->reg_ndcr = ndcr;
|
||||||
|
|
||||||
pxa3xx_nand_set_timing(host, f->timing);
|
pxa3xx_nand_set_timing(host, f->timing);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -863,12 +906,9 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
||||||
host->read_id_bytes = 2;
|
host->read_id_bytes = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
|
info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
|
||||||
host->cmdset = &default_cmdset;
|
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
||||||
|
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
||||||
host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
|
||||||
host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,6 +918,7 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
||||||
*/
|
*/
|
||||||
#define MAX_BUFF_SIZE PAGE_SIZE
|
#define MAX_BUFF_SIZE PAGE_SIZE
|
||||||
|
|
||||||
|
#ifdef ARCH_HAS_DMA
|
||||||
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = info->pdev;
|
struct platform_device *pdev = info->pdev;
|
||||||
|
@ -912,6 +953,32 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = info->pdev;
|
||||||
|
if (use_dma) {
|
||||||
|
pxa_free_dma(info->data_dma_ch);
|
||||||
|
dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
|
||||||
|
info->data_buff, info->data_buff_phys);
|
||||||
|
} else {
|
||||||
|
kfree(info->data_buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
||||||
|
{
|
||||||
|
info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
|
||||||
|
if (info->data_buff == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
|
||||||
|
{
|
||||||
|
kfree(info->data_buff);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
|
static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
|
@ -934,7 +1001,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
||||||
struct pxa3xx_nand_host *host = mtd->priv;
|
struct pxa3xx_nand_host *host = mtd->priv;
|
||||||
struct pxa3xx_nand_info *info = host->info_data;
|
struct pxa3xx_nand_info *info = host->info_data;
|
||||||
struct platform_device *pdev = info->pdev;
|
struct platform_device *pdev = info->pdev;
|
||||||
struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
|
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
|
struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
|
||||||
const struct pxa3xx_nand_flash *f = NULL;
|
const struct pxa3xx_nand_flash *f = NULL;
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
@ -1003,7 +1070,7 @@ KEEP_CONFIG:
|
||||||
chip->ecc.size = host->page_size;
|
chip->ecc.size = host->page_size;
|
||||||
chip->ecc.strength = 1;
|
chip->ecc.strength = 1;
|
||||||
|
|
||||||
if (host->reg_ndcr & NDCR_DWIDTH_M)
|
if (info->reg_ndcr & NDCR_DWIDTH_M)
|
||||||
chip->options |= NAND_BUSWIDTH_16;
|
chip->options |= NAND_BUSWIDTH_16;
|
||||||
|
|
||||||
if (nand_scan_ident(mtd, 1, def))
|
if (nand_scan_ident(mtd, 1, def))
|
||||||
|
@ -1019,8 +1086,6 @@ KEEP_CONFIG:
|
||||||
host->row_addr_cycles = 3;
|
host->row_addr_cycles = 3;
|
||||||
else
|
else
|
||||||
host->row_addr_cycles = 2;
|
host->row_addr_cycles = 2;
|
||||||
|
|
||||||
mtd->name = mtd_names[0];
|
|
||||||
return nand_scan_tail(mtd);
|
return nand_scan_tail(mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1034,13 +1099,11 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
int ret, irq, cs;
|
int ret, irq, cs;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
|
info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) +
|
||||||
sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
|
sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
|
||||||
if (!info) {
|
if (!info)
|
||||||
dev_err(&pdev->dev, "failed to allocate memory\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
info->pdev = pdev;
|
info->pdev = pdev;
|
||||||
for (cs = 0; cs < pdata->num_cs; cs++) {
|
for (cs = 0; cs < pdata->num_cs; cs++) {
|
||||||
|
@ -1069,72 +1132,64 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
|
|
||||||
spin_lock_init(&chip->controller->lock);
|
spin_lock_init(&chip->controller->lock);
|
||||||
init_waitqueue_head(&chip->controller->wq);
|
init_waitqueue_head(&chip->controller->wq);
|
||||||
info->clk = clk_get(&pdev->dev, NULL);
|
info->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(info->clk)) {
|
if (IS_ERR(info->clk)) {
|
||||||
dev_err(&pdev->dev, "failed to get nand clock\n");
|
dev_err(&pdev->dev, "failed to get nand clock\n");
|
||||||
ret = PTR_ERR(info->clk);
|
return PTR_ERR(info->clk);
|
||||||
goto fail_free_mtd;
|
|
||||||
}
|
}
|
||||||
clk_enable(info->clk);
|
ret = clk_prepare_enable(info->clk);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (use_dma) {
|
||||||
/*
|
/*
|
||||||
* This is a dirty hack to make this driver work from devicetree
|
* This is a dirty hack to make this driver work from
|
||||||
* bindings. It can be removed once we have a prober DMA controller
|
* devicetree bindings. It can be removed once we have
|
||||||
* framework for DT.
|
* a prober DMA controller framework for DT.
|
||||||
*/
|
*/
|
||||||
if (pdev->dev.of_node && cpu_is_pxa3xx()) {
|
if (pdev->dev.of_node &&
|
||||||
|
of_machine_is_compatible("marvell,pxa3xx")) {
|
||||||
info->drcmr_dat = 97;
|
info->drcmr_dat = 97;
|
||||||
info->drcmr_cmd = 99;
|
info->drcmr_cmd = 99;
|
||||||
} else {
|
} else {
|
||||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||||
if (r == NULL) {
|
if (r == NULL) {
|
||||||
dev_err(&pdev->dev, "no resource defined for data DMA\n");
|
dev_err(&pdev->dev,
|
||||||
|
"no resource defined for data DMA\n");
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto fail_put_clk;
|
goto fail_disable_clk;
|
||||||
}
|
}
|
||||||
info->drcmr_dat = r->start;
|
info->drcmr_dat = r->start;
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||||
if (r == NULL) {
|
if (r == NULL) {
|
||||||
dev_err(&pdev->dev, "no resource defined for command DMA\n");
|
dev_err(&pdev->dev,
|
||||||
|
"no resource defined for cmd DMA\n");
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto fail_put_clk;
|
goto fail_disable_clk;
|
||||||
}
|
}
|
||||||
info->drcmr_cmd = r->start;
|
info->drcmr_cmd = r->start;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0) {
|
if (irq < 0) {
|
||||||
dev_err(&pdev->dev, "no IRQ resource defined\n");
|
dev_err(&pdev->dev, "no IRQ resource defined\n");
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto fail_put_clk;
|
goto fail_disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (r == NULL) {
|
info->mmio_base = devm_ioremap_resource(&pdev->dev, r);
|
||||||
dev_err(&pdev->dev, "no IO memory resource defined\n");
|
if (IS_ERR(info->mmio_base)) {
|
||||||
ret = -ENODEV;
|
ret = PTR_ERR(info->mmio_base);
|
||||||
goto fail_put_clk;
|
goto fail_disable_clk;
|
||||||
}
|
|
||||||
|
|
||||||
r = request_mem_region(r->start, resource_size(r), pdev->name);
|
|
||||||
if (r == NULL) {
|
|
||||||
dev_err(&pdev->dev, "failed to request memory resource\n");
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto fail_put_clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
info->mmio_base = ioremap(r->start, resource_size(r));
|
|
||||||
if (info->mmio_base == NULL) {
|
|
||||||
dev_err(&pdev->dev, "ioremap() failed\n");
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto fail_free_res;
|
|
||||||
}
|
}
|
||||||
info->mmio_phys = r->start;
|
info->mmio_phys = r->start;
|
||||||
|
|
||||||
ret = pxa3xx_nand_init_buff(info);
|
ret = pxa3xx_nand_init_buff(info);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto fail_free_io;
|
goto fail_disable_clk;
|
||||||
|
|
||||||
/* initialize all interrupts to be disabled */
|
/* initialize all interrupts to be disabled */
|
||||||
disable_int(info, NDSR_MASK);
|
disable_int(info, NDSR_MASK);
|
||||||
|
@ -1152,21 +1207,9 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
|
|
||||||
fail_free_buf:
|
fail_free_buf:
|
||||||
free_irq(irq, info);
|
free_irq(irq, info);
|
||||||
if (use_dma) {
|
pxa3xx_nand_free_buff(info);
|
||||||
pxa_free_dma(info->data_dma_ch);
|
fail_disable_clk:
|
||||||
dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
|
clk_disable_unprepare(info->clk);
|
||||||
info->data_buff, info->data_buff_phys);
|
|
||||||
} else
|
|
||||||
kfree(info->data_buff);
|
|
||||||
fail_free_io:
|
|
||||||
iounmap(info->mmio_base);
|
|
||||||
fail_free_res:
|
|
||||||
release_mem_region(r->start, resource_size(r));
|
|
||||||
fail_put_clk:
|
|
||||||
clk_disable(info->clk);
|
|
||||||
clk_put(info->clk);
|
|
||||||
fail_free_mtd:
|
|
||||||
kfree(info);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1174,44 +1217,48 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
|
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
|
||||||
struct pxa3xx_nand_platform_data *pdata;
|
struct pxa3xx_nand_platform_data *pdata;
|
||||||
struct resource *r;
|
|
||||||
int irq, cs;
|
int irq, cs;
|
||||||
|
|
||||||
if (!info)
|
if (!info)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq >= 0)
|
if (irq >= 0)
|
||||||
free_irq(irq, info);
|
free_irq(irq, info);
|
||||||
if (use_dma) {
|
pxa3xx_nand_free_buff(info);
|
||||||
pxa_free_dma(info->data_dma_ch);
|
|
||||||
dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
|
|
||||||
info->data_buff, info->data_buff_phys);
|
|
||||||
} else
|
|
||||||
kfree(info->data_buff);
|
|
||||||
|
|
||||||
iounmap(info->mmio_base);
|
clk_disable_unprepare(info->clk);
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
release_mem_region(r->start, resource_size(r));
|
|
||||||
|
|
||||||
clk_disable(info->clk);
|
|
||||||
clk_put(info->clk);
|
|
||||||
|
|
||||||
for (cs = 0; cs < pdata->num_cs; cs++)
|
for (cs = 0; cs < pdata->num_cs; cs++)
|
||||||
nand_release(info->host[cs]->mtd);
|
nand_release(info->host[cs]->mtd);
|
||||||
kfree(info);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static struct of_device_id pxa3xx_nand_dt_ids[] = {
|
static struct of_device_id pxa3xx_nand_dt_ids[] = {
|
||||||
{ .compatible = "marvell,pxa3xx-nand" },
|
{
|
||||||
|
.compatible = "marvell,pxa3xx-nand",
|
||||||
|
.data = (void *)PXA3XX_NAND_VARIANT_PXA,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "marvell,armada370-nand",
|
||||||
|
.data = (void *)PXA3XX_NAND_VARIANT_ARMADA370,
|
||||||
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
|
MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids);
|
||||||
|
|
||||||
|
static enum pxa3xx_nand_variant
|
||||||
|
pxa3xx_nand_get_variant(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
const struct of_device_id *of_id =
|
||||||
|
of_match_device(pxa3xx_nand_dt_ids, &pdev->dev);
|
||||||
|
if (!of_id)
|
||||||
|
return PXA3XX_NAND_VARIANT_PXA;
|
||||||
|
return (enum pxa3xx_nand_variant)of_id->data;
|
||||||
|
}
|
||||||
|
|
||||||
static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
|
static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
@ -1251,11 +1298,18 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||||
struct pxa3xx_nand_info *info;
|
struct pxa3xx_nand_info *info;
|
||||||
int ret, cs, probe_success;
|
int ret, cs, probe_success;
|
||||||
|
|
||||||
|
#ifndef ARCH_HAS_DMA
|
||||||
|
if (use_dma) {
|
||||||
|
use_dma = 0;
|
||||||
|
dev_warn(&pdev->dev,
|
||||||
|
"This platform can't do DMA on this device\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
ret = pxa3xx_nand_probe_dt(pdev);
|
ret = pxa3xx_nand_probe_dt(pdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
if (!pdata) {
|
if (!pdata) {
|
||||||
dev_err(&pdev->dev, "no platform data defined\n");
|
dev_err(&pdev->dev, "no platform data defined\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1268,10 +1322,14 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
info = platform_get_drvdata(pdev);
|
info = platform_get_drvdata(pdev);
|
||||||
|
info->variant = pxa3xx_nand_get_variant(pdev);
|
||||||
probe_success = 0;
|
probe_success = 0;
|
||||||
for (cs = 0; cs < pdata->num_cs; cs++) {
|
for (cs = 0; cs < pdata->num_cs; cs++) {
|
||||||
|
struct mtd_info *mtd = info->host[cs]->mtd;
|
||||||
|
|
||||||
|
mtd->name = pdev->name;
|
||||||
info->cs = cs;
|
info->cs = cs;
|
||||||
ret = pxa3xx_nand_scan(info->host[cs]->mtd);
|
ret = pxa3xx_nand_scan(mtd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
|
dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
|
||||||
cs);
|
cs);
|
||||||
|
@ -1279,7 +1337,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ppdata.of_node = pdev->dev.of_node;
|
ppdata.of_node = pdev->dev.of_node;
|
||||||
ret = mtd_device_parse_register(info->host[cs]->mtd, NULL,
|
ret = mtd_device_parse_register(mtd, NULL,
|
||||||
&ppdata, pdata->parts[cs],
|
&ppdata, pdata->parts[cs],
|
||||||
pdata->nr_parts[cs]);
|
pdata->nr_parts[cs]);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
|
@ -1302,7 +1360,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
int cs;
|
int cs;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
if (info->state) {
|
if (info->state) {
|
||||||
dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
|
dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
@ -1323,7 +1381,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
int cs;
|
int cs;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
/* We don't want to handle interrupt without calling mtd routine */
|
/* We don't want to handle interrupt without calling mtd routine */
|
||||||
disable_int(info, NDCR_INT_MASK);
|
disable_int(info, NDCR_INT_MASK);
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read)
|
||||||
/*
|
/*
|
||||||
* Program data lines of the nand chip to send data to it
|
* Program data lines of the nand chip to send data to it
|
||||||
*/
|
*/
|
||||||
void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = r852_get_dev(mtd);
|
struct r852_device *dev = r852_get_dev(mtd);
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
|
@ -261,7 +261,7 @@ void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||||
/*
|
/*
|
||||||
* Read data lines of the nand chip to retrieve data
|
* Read data lines of the nand chip to retrieve data
|
||||||
*/
|
*/
|
||||||
void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = r852_get_dev(mtd);
|
struct r852_device *dev = r852_get_dev(mtd);
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
|
@ -312,7 +312,7 @@ static uint8_t r852_read_byte(struct mtd_info *mtd)
|
||||||
/*
|
/*
|
||||||
* Control several chip lines & send commands
|
* Control several chip lines & send commands
|
||||||
*/
|
*/
|
||||||
void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
|
static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = r852_get_dev(mtd);
|
struct r852_device *dev = r852_get_dev(mtd);
|
||||||
|
|
||||||
|
@ -357,7 +357,7 @@ void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
|
||||||
* Wait till card is ready.
|
* Wait till card is ready.
|
||||||
* based on nand_wait, but returns errors on DMA error
|
* based on nand_wait, but returns errors on DMA error
|
||||||
*/
|
*/
|
||||||
int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = chip->priv;
|
struct r852_device *dev = chip->priv;
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
* Check if card is ready
|
* Check if card is ready
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int r852_ready(struct mtd_info *mtd)
|
static int r852_ready(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = r852_get_dev(mtd);
|
struct r852_device *dev = r852_get_dev(mtd);
|
||||||
return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY);
|
return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY);
|
||||||
|
@ -397,7 +397,7 @@ int r852_ready(struct mtd_info *mtd)
|
||||||
* Set ECC engine mode
|
* Set ECC engine mode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void r852_ecc_hwctl(struct mtd_info *mtd, int mode)
|
static void r852_ecc_hwctl(struct mtd_info *mtd, int mode)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = r852_get_dev(mtd);
|
struct r852_device *dev = r852_get_dev(mtd);
|
||||||
|
|
||||||
|
@ -429,7 +429,7 @@ void r852_ecc_hwctl(struct mtd_info *mtd, int mode)
|
||||||
* Calculate ECC, only used for writes
|
* Calculate ECC, only used for writes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
|
static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
|
||||||
uint8_t *ecc_code)
|
uint8_t *ecc_code)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = r852_get_dev(mtd);
|
struct r852_device *dev = r852_get_dev(mtd);
|
||||||
|
@ -461,7 +461,7 @@ int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
|
||||||
* Correct the data using ECC, hw did almost everything for us
|
* Correct the data using ECC, hw did almost everything for us
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
|
static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
|
||||||
uint8_t *read_ecc, uint8_t *calc_ecc)
|
uint8_t *read_ecc, uint8_t *calc_ecc)
|
||||||
{
|
{
|
||||||
uint16_t ecc_reg;
|
uint16_t ecc_reg;
|
||||||
|
@ -529,7 +529,7 @@ static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* Start the nand engine
|
* Start the nand engine
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void r852_engine_enable(struct r852_device *dev)
|
static void r852_engine_enable(struct r852_device *dev)
|
||||||
{
|
{
|
||||||
if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) {
|
if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) {
|
||||||
r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
|
r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
|
||||||
|
@ -547,7 +547,7 @@ void r852_engine_enable(struct r852_device *dev)
|
||||||
* Stop the nand engine
|
* Stop the nand engine
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void r852_engine_disable(struct r852_device *dev)
|
static void r852_engine_disable(struct r852_device *dev)
|
||||||
{
|
{
|
||||||
r852_write_reg_dword(dev, R852_HW, 0);
|
r852_write_reg_dword(dev, R852_HW, 0);
|
||||||
r852_write_reg(dev, R852_CTL, R852_CTL_RESET);
|
r852_write_reg(dev, R852_CTL, R852_CTL_RESET);
|
||||||
|
@ -557,7 +557,7 @@ void r852_engine_disable(struct r852_device *dev)
|
||||||
* Test if card is present
|
* Test if card is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void r852_card_update_present(struct r852_device *dev)
|
static void r852_card_update_present(struct r852_device *dev)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
uint8_t reg;
|
uint8_t reg;
|
||||||
|
@ -572,7 +572,7 @@ void r852_card_update_present(struct r852_device *dev)
|
||||||
* Update card detection IRQ state according to current card state
|
* Update card detection IRQ state according to current card state
|
||||||
* which is read in r852_card_update_present
|
* which is read in r852_card_update_present
|
||||||
*/
|
*/
|
||||||
void r852_update_card_detect(struct r852_device *dev)
|
static void r852_update_card_detect(struct r852_device *dev)
|
||||||
{
|
{
|
||||||
int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
|
int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
|
||||||
dev->card_unstable = 0;
|
dev->card_unstable = 0;
|
||||||
|
@ -586,7 +586,7 @@ void r852_update_card_detect(struct r852_device *dev)
|
||||||
r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg);
|
r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t r852_media_type_show(struct device *sys_dev,
|
static ssize_t r852_media_type_show(struct device *sys_dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev);
|
struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev);
|
||||||
|
@ -597,11 +597,11 @@ ssize_t r852_media_type_show(struct device *sys_dev,
|
||||||
return strlen(data);
|
return strlen(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL);
|
static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL);
|
||||||
|
|
||||||
|
|
||||||
/* Detect properties of card in slot */
|
/* Detect properties of card in slot */
|
||||||
void r852_update_media_status(struct r852_device *dev)
|
static void r852_update_media_status(struct r852_device *dev)
|
||||||
{
|
{
|
||||||
uint8_t reg;
|
uint8_t reg;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -630,7 +630,7 @@ void r852_update_media_status(struct r852_device *dev)
|
||||||
* Register the nand device
|
* Register the nand device
|
||||||
* Called when the card is detected
|
* Called when the card is detected
|
||||||
*/
|
*/
|
||||||
int r852_register_nand_device(struct r852_device *dev)
|
static int r852_register_nand_device(struct r852_device *dev)
|
||||||
{
|
{
|
||||||
dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
|
dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
|
||||||
|
|
||||||
|
@ -668,7 +668,7 @@ error1:
|
||||||
* Unregister the card
|
* Unregister the card
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void r852_unregister_nand_device(struct r852_device *dev)
|
static void r852_unregister_nand_device(struct r852_device *dev)
|
||||||
{
|
{
|
||||||
if (!dev->card_registred)
|
if (!dev->card_registred)
|
||||||
return;
|
return;
|
||||||
|
@ -682,7 +682,7 @@ void r852_unregister_nand_device(struct r852_device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Card state updater */
|
/* Card state updater */
|
||||||
void r852_card_detect_work(struct work_struct *work)
|
static void r852_card_detect_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct r852_device *dev =
|
struct r852_device *dev =
|
||||||
container_of(work, struct r852_device, card_detect_work.work);
|
container_of(work, struct r852_device, card_detect_work.work);
|
||||||
|
@ -821,7 +821,7 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
|
static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
struct nand_chip *chip;
|
struct nand_chip *chip;
|
||||||
|
@ -961,7 +961,7 @@ error1:
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void r852_remove(struct pci_dev *pci_dev)
|
static void r852_remove(struct pci_dev *pci_dev)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = pci_get_drvdata(pci_dev);
|
struct r852_device *dev = pci_get_drvdata(pci_dev);
|
||||||
|
|
||||||
|
@ -992,7 +992,7 @@ void r852_remove(struct pci_dev *pci_dev)
|
||||||
pci_disable_device(pci_dev);
|
pci_disable_device(pci_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void r852_shutdown(struct pci_dev *pci_dev)
|
static void r852_shutdown(struct pci_dev *pci_dev)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = pci_get_drvdata(pci_dev);
|
struct r852_device *dev = pci_get_drvdata(pci_dev);
|
||||||
|
|
||||||
|
@ -1002,7 +1002,7 @@ void r852_shutdown(struct pci_dev *pci_dev)
|
||||||
pci_disable_device(pci_dev);
|
pci_disable_device(pci_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int r852_suspend(struct device *device)
|
static int r852_suspend(struct device *device)
|
||||||
{
|
{
|
||||||
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
|
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
|
||||||
|
@ -1055,9 +1055,6 @@ static int r852_resume(struct device *device)
|
||||||
r852_update_card_detect(dev);
|
r852_update_card_detect(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
#define r852_suspend NULL
|
|
||||||
#define r852_resume NULL
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct pci_device_id r852_pci_id_tbl[] = {
|
static const struct pci_device_id r852_pci_id_tbl[] = {
|
||||||
|
|
|
@ -150,7 +150,7 @@ static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev)
|
||||||
|
|
||||||
static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
|
static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
return dev->dev.platform_data;
|
return dev_get_platdata(&dev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int allow_clk_suspend(struct s3c2410_nand_info *info)
|
static inline int allow_clk_suspend(struct s3c2410_nand_info *info)
|
||||||
|
@ -697,8 +697,6 @@ static int s3c24xx_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct s3c2410_nand_info *info = to_nand_info(pdev);
|
struct s3c2410_nand_info *info = to_nand_info(pdev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
if (info == NULL)
|
if (info == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl)
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
struct dma_slave_config cfg;
|
struct dma_slave_config cfg;
|
||||||
struct platform_device *pdev = flctl->pdev;
|
struct platform_device *pdev = flctl->pdev;
|
||||||
struct sh_flctl_platform_data *pdata = pdev->dev.platform_data;
|
struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
|
@ -1131,7 +1131,7 @@ static int flctl_probe(struct platform_device *pdev)
|
||||||
if (pdev->dev.of_node)
|
if (pdev->dev.of_node)
|
||||||
pdata = flctl_parse_dt(&pdev->dev);
|
pdata = flctl_parse_dt(&pdev->dev);
|
||||||
else
|
else
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
if (!pdata) {
|
if (!pdata) {
|
||||||
dev_err(&pdev->dev, "no setup data defined\n");
|
dev_err(&pdev->dev, "no setup data defined\n");
|
||||||
|
|
|
@ -112,7 +112,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct sharpsl_nand *sharpsl;
|
struct sharpsl_nand *sharpsl;
|
||||||
struct sharpsl_nand_platform_data *data = pdev->dev.platform_data;
|
struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
dev_err(&pdev->dev, "no platform data!\n");
|
dev_err(&pdev->dev, "no platform data!\n");
|
||||||
|
@ -194,7 +194,6 @@ err_add:
|
||||||
nand_release(&sharpsl->mtd);
|
nand_release(&sharpsl->mtd);
|
||||||
|
|
||||||
err_scan:
|
err_scan:
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
iounmap(sharpsl->io);
|
iounmap(sharpsl->io);
|
||||||
err_ioremap:
|
err_ioremap:
|
||||||
err_get_res:
|
err_get_res:
|
||||||
|
@ -212,8 +211,6 @@ static int sharpsl_nand_remove(struct platform_device *pdev)
|
||||||
/* Release resources, unregister device */
|
/* Release resources, unregister device */
|
||||||
nand_release(&sharpsl->mtd);
|
nand_release(&sharpsl->mtd);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
iounmap(sharpsl->io);
|
iounmap(sharpsl->io);
|
||||||
|
|
||||||
/* Free the MTD device structure */
|
/* Free the MTD device structure */
|
||||||
|
|
|
@ -42,7 +42,7 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct mtd_oob_ops ops;
|
struct mtd_oob_ops ops;
|
||||||
struct sm_oob oob;
|
struct sm_oob oob;
|
||||||
int ret, error = 0;
|
int ret;
|
||||||
|
|
||||||
memset(&oob, -1, SM_OOB_SIZE);
|
memset(&oob, -1, SM_OOB_SIZE);
|
||||||
oob.block_status = 0x0F;
|
oob.block_status = 0x0F;
|
||||||
|
@ -61,11 +61,10 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
printk(KERN_NOTICE
|
printk(KERN_NOTICE
|
||||||
"sm_common: can't mark sector at %i as bad\n",
|
"sm_common: can't mark sector at %i as bad\n",
|
||||||
(int)ofs);
|
(int)ofs);
|
||||||
error = -EIO;
|
return -EIO;
|
||||||
} else
|
}
|
||||||
mtd->ecc_stats.badblocks++;
|
|
||||||
|
|
||||||
return error;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
|
static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
|
||||||
|
|
|
@ -357,7 +357,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
|
||||||
|
|
||||||
static int tmio_probe(struct platform_device *dev)
|
static int tmio_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct tmio_nand_data *data = dev->dev.platform_data;
|
struct tmio_nand_data *data = dev_get_platdata(&dev->dev);
|
||||||
struct resource *fcr = platform_get_resource(dev,
|
struct resource *fcr = platform_get_resource(dev,
|
||||||
IORESOURCE_MEM, 0);
|
IORESOURCE_MEM, 0);
|
||||||
struct resource *ccr = platform_get_resource(dev,
|
struct resource *ccr = platform_get_resource(dev,
|
||||||
|
|
|
@ -87,7 +87,7 @@ static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
|
||||||
static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)
|
static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)
|
||||||
{
|
{
|
||||||
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
||||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
|
||||||
|
|
||||||
return drvdata->base + (reg << plat->shift);
|
return drvdata->base + (reg << plat->shift);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
struct txx9ndfmc_priv *txx9_priv = chip->priv;
|
struct txx9ndfmc_priv *txx9_priv = chip->priv;
|
||||||
struct platform_device *dev = txx9_priv->dev;
|
struct platform_device *dev = txx9_priv->dev;
|
||||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
|
||||||
|
|
||||||
if (ctrl & NAND_CTRL_CHANGE) {
|
if (ctrl & NAND_CTRL_CHANGE) {
|
||||||
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
|
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
|
||||||
|
@ -225,7 +225,7 @@ static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||||
|
|
||||||
static void txx9ndfmc_initialize(struct platform_device *dev)
|
static void txx9ndfmc_initialize(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
|
||||||
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
||||||
int tmout = 100;
|
int tmout = 100;
|
||||||
|
|
||||||
|
@ -274,19 +274,17 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
|
||||||
|
|
||||||
static int __init txx9ndfmc_probe(struct platform_device *dev)
|
static int __init txx9ndfmc_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
|
||||||
int hold, spw;
|
int hold, spw;
|
||||||
int i;
|
int i;
|
||||||
struct txx9ndfmc_drvdata *drvdata;
|
struct txx9ndfmc_drvdata *drvdata;
|
||||||
unsigned long gbusclk = plat->gbus_clock;
|
unsigned long gbusclk = plat->gbus_clock;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
||||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
|
||||||
if (!res)
|
|
||||||
return -ENODEV;
|
|
||||||
drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);
|
drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||||
if (!drvdata)
|
if (!drvdata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||||
drvdata->base = devm_ioremap_resource(&dev->dev, res);
|
drvdata->base = devm_ioremap_resource(&dev->dev, res);
|
||||||
if (IS_ERR(drvdata->base))
|
if (IS_ERR(drvdata->base))
|
||||||
return PTR_ERR(drvdata->base);
|
return PTR_ERR(drvdata->base);
|
||||||
|
@ -387,7 +385,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
|
||||||
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
platform_set_drvdata(dev, NULL);
|
|
||||||
if (!drvdata)
|
if (!drvdata)
|
||||||
return 0;
|
return 0;
|
||||||
for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
|
for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
|
||||||
|
|
|
@ -20,6 +20,11 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
|
static bool node_has_compatible(struct device_node *pp)
|
||||||
|
{
|
||||||
|
return of_get_property(pp, "compatible", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_ofpart_partitions(struct mtd_info *master,
|
static int parse_ofpart_partitions(struct mtd_info *master,
|
||||||
struct mtd_partition **pparts,
|
struct mtd_partition **pparts,
|
||||||
struct mtd_part_parser_data *data)
|
struct mtd_part_parser_data *data)
|
||||||
|
@ -38,10 +43,13 @@ static int parse_ofpart_partitions(struct mtd_info *master,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* First count the subnodes */
|
/* First count the subnodes */
|
||||||
pp = NULL;
|
|
||||||
nr_parts = 0;
|
nr_parts = 0;
|
||||||
while ((pp = of_get_next_child(node, pp)))
|
for_each_child_of_node(node, pp) {
|
||||||
|
if (node_has_compatible(pp))
|
||||||
|
continue;
|
||||||
|
|
||||||
nr_parts++;
|
nr_parts++;
|
||||||
|
}
|
||||||
|
|
||||||
if (nr_parts == 0)
|
if (nr_parts == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -50,13 +58,15 @@ static int parse_ofpart_partitions(struct mtd_info *master,
|
||||||
if (!*pparts)
|
if (!*pparts)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
pp = NULL;
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while ((pp = of_get_next_child(node, pp))) {
|
for_each_child_of_node(node, pp) {
|
||||||
const __be32 *reg;
|
const __be32 *reg;
|
||||||
int len;
|
int len;
|
||||||
int a_cells, s_cells;
|
int a_cells, s_cells;
|
||||||
|
|
||||||
|
if (node_has_compatible(pp))
|
||||||
|
continue;
|
||||||
|
|
||||||
reg = of_get_property(pp, "reg", &len);
|
reg = of_get_property(pp, "reg", &len);
|
||||||
if (!reg) {
|
if (!reg) {
|
||||||
nr_parts--;
|
nr_parts--;
|
||||||
|
|
|
@ -38,7 +38,7 @@ struct onenand_info {
|
||||||
static int generic_onenand_probe(struct platform_device *pdev)
|
static int generic_onenand_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct onenand_info *info;
|
struct onenand_info *info;
|
||||||
struct onenand_platform_data *pdata = pdev->dev.platform_data;
|
struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct resource *res = pdev->resource;
|
struct resource *res = pdev->resource;
|
||||||
unsigned long size = resource_size(res);
|
unsigned long size = resource_size(res);
|
||||||
int err;
|
int err;
|
||||||
|
@ -94,8 +94,6 @@ static int generic_onenand_remove(struct platform_device *pdev)
|
||||||
struct resource *res = pdev->resource;
|
struct resource *res = pdev->resource;
|
||||||
unsigned long size = resource_size(res);
|
unsigned long size = resource_size(res);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
onenand_release(&info->mtd);
|
onenand_release(&info->mtd);
|
||||||
release_mem_region(res->start, size);
|
release_mem_region(res->start, size);
|
||||||
|
|
|
@ -639,7 +639,7 @@ static int omap2_onenand_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct mtd_part_parser_data ppdata = {};
|
struct mtd_part_parser_data ppdata = {};
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
if (pdata == NULL) {
|
if (pdata == NULL) {
|
||||||
dev_err(&pdev->dev, "platform data missing\n");
|
dev_err(&pdev->dev, "platform data missing\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -810,7 +810,6 @@ static int omap2_onenand_remove(struct platform_device *pdev)
|
||||||
if (c->dma_channel != -1)
|
if (c->dma_channel != -1)
|
||||||
omap_free_dma(c->dma_channel);
|
omap_free_dma(c->dma_channel);
|
||||||
omap2_onenand_shutdown(pdev);
|
omap2_onenand_shutdown(pdev);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
if (c->gpio_irq) {
|
if (c->gpio_irq) {
|
||||||
free_irq(gpio_to_irq(c->gpio_irq), c);
|
free_irq(gpio_to_irq(c->gpio_irq), c);
|
||||||
gpio_free(c->gpio_irq);
|
gpio_free(c->gpio_irq);
|
||||||
|
|
|
@ -133,7 +133,6 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc
|
||||||
{
|
{
|
||||||
struct onenand_chip *this = mtd->priv;
|
struct onenand_chip *this = mtd->priv;
|
||||||
|
|
||||||
bd->options &= ~NAND_BBT_SCANEMPTY;
|
|
||||||
return create_bbt(mtd, this->page_buf, bd, -1);
|
return create_bbt(mtd, this->page_buf, bd, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -867,7 +867,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
int size, err;
|
int size, err;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
/* No need to check pdata. the platform data is optional */
|
/* No need to check pdata. the platform data is optional */
|
||||||
|
|
||||||
size = sizeof(struct mtd_info) + sizeof(struct onenand_chip);
|
size = sizeof(struct mtd_info) + sizeof(struct onenand_chip);
|
||||||
|
@ -1073,7 +1073,6 @@ static int s3c_onenand_remove(struct platform_device *pdev)
|
||||||
release_mem_region(onenand->base_res->start,
|
release_mem_region(onenand->base_res->start,
|
||||||
resource_size(onenand->base_res));
|
resource_size(onenand->base_res));
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
kfree(onenand->oob_buf);
|
kfree(onenand->oob_buf);
|
||||||
kfree(onenand->page_buf);
|
kfree(onenand->page_buf);
|
||||||
kfree(onenand);
|
kfree(onenand);
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct workqueue_struct *cache_flush_workqueue;
|
static struct workqueue_struct *cache_flush_workqueue;
|
||||||
|
|
||||||
static int cache_timeout = 1000;
|
static int cache_timeout = 1000;
|
||||||
module_param(cache_timeout, int, S_IRUGO);
|
module_param(cache_timeout, int, S_IRUGO);
|
||||||
|
@ -41,7 +41,7 @@ struct sm_sysfs_attribute {
|
||||||
int len;
|
int len;
|
||||||
};
|
};
|
||||||
|
|
||||||
ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr,
|
static ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct sm_sysfs_attribute *sm_attr =
|
struct sm_sysfs_attribute *sm_attr =
|
||||||
|
@ -54,7 +54,7 @@ ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
|
||||||
#define NUM_ATTRIBUTES 1
|
#define NUM_ATTRIBUTES 1
|
||||||
#define SM_CIS_VENDOR_OFFSET 0x59
|
#define SM_CIS_VENDOR_OFFSET 0x59
|
||||||
struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
|
static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
|
||||||
{
|
{
|
||||||
struct attribute_group *attr_group;
|
struct attribute_group *attr_group;
|
||||||
struct attribute **attributes;
|
struct attribute **attributes;
|
||||||
|
@ -107,7 +107,7 @@ error1:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sm_delete_sysfs_attributes(struct sm_ftl *ftl)
|
static void sm_delete_sysfs_attributes(struct sm_ftl *ftl)
|
||||||
{
|
{
|
||||||
struct attribute **attributes = ftl->disk_attributes->attrs;
|
struct attribute **attributes = ftl->disk_attributes->attrs;
|
||||||
int i;
|
int i;
|
||||||
|
@ -571,7 +571,7 @@ static const uint8_t cis_signature[] = {
|
||||||
};
|
};
|
||||||
/* Find out media parameters.
|
/* Find out media parameters.
|
||||||
* This ideally has to be based on nand id, but for now device size is enough */
|
* This ideally has to be based on nand id, but for now device size is enough */
|
||||||
int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd)
|
static int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int size_in_megs = mtd->size / (1024 * 1024);
|
int size_in_megs = mtd->size / (1024 * 1024);
|
||||||
|
@ -878,7 +878,7 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get and automatically initialize an FTL mapping for one zone */
|
/* Get and automatically initialize an FTL mapping for one zone */
|
||||||
struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num)
|
static struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num)
|
||||||
{
|
{
|
||||||
struct ftl_zone *zone;
|
struct ftl_zone *zone;
|
||||||
int error;
|
int error;
|
||||||
|
@ -899,7 +899,7 @@ struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num)
|
||||||
/* ----------------- cache handling ------------------------------------------*/
|
/* ----------------- cache handling ------------------------------------------*/
|
||||||
|
|
||||||
/* Initialize the one block cache */
|
/* Initialize the one block cache */
|
||||||
void sm_cache_init(struct sm_ftl *ftl)
|
static void sm_cache_init(struct sm_ftl *ftl)
|
||||||
{
|
{
|
||||||
ftl->cache_data_invalid_bitmap = 0xFFFFFFFF;
|
ftl->cache_data_invalid_bitmap = 0xFFFFFFFF;
|
||||||
ftl->cache_clean = 1;
|
ftl->cache_clean = 1;
|
||||||
|
@ -909,7 +909,7 @@ void sm_cache_init(struct sm_ftl *ftl)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Put sector in one block cache */
|
/* Put sector in one block cache */
|
||||||
void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset)
|
static void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset)
|
||||||
{
|
{
|
||||||
memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE);
|
memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE);
|
||||||
clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap);
|
clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap);
|
||||||
|
@ -917,7 +917,7 @@ void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read a sector from the cache */
|
/* Read a sector from the cache */
|
||||||
int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset)
|
static int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset)
|
||||||
{
|
{
|
||||||
if (test_bit(boffset / SM_SECTOR_SIZE,
|
if (test_bit(boffset / SM_SECTOR_SIZE,
|
||||||
&ftl->cache_data_invalid_bitmap))
|
&ftl->cache_data_invalid_bitmap))
|
||||||
|
@ -928,7 +928,7 @@ int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write the cache to hardware */
|
/* Write the cache to hardware */
|
||||||
int sm_cache_flush(struct sm_ftl *ftl)
|
static int sm_cache_flush(struct sm_ftl *ftl)
|
||||||
{
|
{
|
||||||
struct ftl_zone *zone;
|
struct ftl_zone *zone;
|
||||||
|
|
||||||
|
@ -1274,10 +1274,10 @@ static struct mtd_blktrans_ops sm_ftl_ops = {
|
||||||
static __init int sm_module_init(void)
|
static __init int sm_module_init(void)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
cache_flush_workqueue = create_freezable_workqueue("smflush");
|
|
||||||
|
|
||||||
if (IS_ERR(cache_flush_workqueue))
|
cache_flush_workqueue = create_freezable_workqueue("smflush");
|
||||||
return PTR_ERR(cache_flush_workqueue);
|
if (!cache_flush_workqueue)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
error = register_mtd_blktrans(&sm_ftl_ops);
|
error = register_mtd_blktrans(&sm_ftl_ops);
|
||||||
if (error)
|
if (error)
|
||||||
|
|
|
@ -7,3 +7,12 @@ 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
|
obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o
|
||||||
|
|
||||||
|
mtd_oobtest-objs := oobtest.o mtd_test.o
|
||||||
|
mtd_pagetest-objs := pagetest.o mtd_test.o
|
||||||
|
mtd_readtest-objs := readtest.o mtd_test.o
|
||||||
|
mtd_speedtest-objs := speedtest.o mtd_test.o
|
||||||
|
mtd_stresstest-objs := stresstest.o mtd_test.o
|
||||||
|
mtd_subpagetest-objs := subpagetest.o mtd_test.o
|
||||||
|
mtd_torturetest-objs := torturetest.o mtd_test.o
|
||||||
|
mtd_nandbiterrs-objs := nandbiterrs.o mtd_test.o
|
||||||
|
|
114
drivers/mtd/tests/mtd_test.c
Normal file
114
drivers/mtd/tests/mtd_test.c
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#define pr_fmt(fmt) "mtd_test: " fmt
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
|
int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct erase_info ei;
|
||||||
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
|
|
||||||
|
memset(&ei, 0, sizeof(struct erase_info));
|
||||||
|
ei.mtd = mtd;
|
||||||
|
ei.addr = addr;
|
||||||
|
ei.len = mtd->erasesize;
|
||||||
|
|
||||||
|
err = mtd_erase(mtd, &ei);
|
||||||
|
if (err) {
|
||||||
|
pr_info("error %d while erasing EB %d\n", err, ebnum);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ei.state == MTD_ERASE_FAILED) {
|
||||||
|
pr_info("some erase error occurred at EB %d\n", ebnum);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
|
|
||||||
|
ret = mtd_block_isbad(mtd, addr);
|
||||||
|
if (ret)
|
||||||
|
pr_info("block %d is bad\n", ebnum);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
|
||||||
|
unsigned int eb, int ebcnt)
|
||||||
|
{
|
||||||
|
int i, bad = 0;
|
||||||
|
|
||||||
|
if (!mtd_can_have_bb(mtd))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_info("scanning for bad eraseblocks\n");
|
||||||
|
for (i = 0; i < ebcnt; ++i) {
|
||||||
|
bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0;
|
||||||
|
if (bbt[i])
|
||||||
|
bad += 1;
|
||||||
|
cond_resched();
|
||||||
|
}
|
||||||
|
pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
|
||||||
|
unsigned int eb, int ebcnt)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ebcnt; ++i) {
|
||||||
|
if (bbt[i])
|
||||||
|
continue;
|
||||||
|
err = mtdtest_erase_eraseblock(mtd, eb + i);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
cond_resched();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf)
|
||||||
|
{
|
||||||
|
size_t read;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mtd_read(mtd, addr, size, &read, buf);
|
||||||
|
/* Ignore corrected ECC errors */
|
||||||
|
if (mtd_is_bitflip(err))
|
||||||
|
err = 0;
|
||||||
|
if (!err && read != size)
|
||||||
|
err = -EIO;
|
||||||
|
if (err)
|
||||||
|
pr_err("error: read failed at %#llx\n", addr);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size,
|
||||||
|
const void *buf)
|
||||||
|
{
|
||||||
|
size_t written;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mtd_write(mtd, addr, size, &written, buf);
|
||||||
|
if (!err && written != size)
|
||||||
|
err = -EIO;
|
||||||
|
if (err)
|
||||||
|
pr_err("error: write failed at %#llx\n", addr);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
11
drivers/mtd/tests/mtd_test.h
Normal file
11
drivers/mtd/tests/mtd_test.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
|
||||||
|
int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum);
|
||||||
|
int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
|
||||||
|
unsigned int eb, int ebcnt);
|
||||||
|
int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
|
||||||
|
unsigned int eb, int ebcnt);
|
||||||
|
|
||||||
|
int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf);
|
||||||
|
int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size,
|
||||||
|
const void *buf);
|
|
@ -49,6 +49,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
static int dev;
|
static int dev;
|
||||||
module_param(dev, int, S_IRUGO);
|
module_param(dev, int, S_IRUGO);
|
||||||
|
@ -98,47 +99,13 @@ static uint8_t hash(unsigned offset)
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int erase_block(void)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct erase_info ei;
|
|
||||||
loff_t addr = eraseblock * mtd->erasesize;
|
|
||||||
|
|
||||||
pr_info("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) {
|
|
||||||
pr_err("error %d while erasing\n", err);
|
|
||||||
if (!err)
|
|
||||||
err = -EIO;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes wbuffer to page */
|
/* Writes wbuffer to page */
|
||||||
static int write_page(int log)
|
static int write_page(int log)
|
||||||
{
|
{
|
||||||
int err = 0;
|
|
||||||
size_t written;
|
|
||||||
|
|
||||||
if (log)
|
if (log)
|
||||||
pr_info("write_page\n");
|
pr_info("write_page\n");
|
||||||
|
|
||||||
err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer);
|
return mtdtest_write(mtd, offset, mtd->writesize, wbuffer);
|
||||||
if (err || written != mtd->writesize) {
|
|
||||||
pr_err("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. */
|
/* Re-writes the data area while leaving the OOB alone. */
|
||||||
|
@ -415,7 +382,7 @@ static int __init mtd_nandbiterrs_init(void)
|
||||||
goto exit_rbuffer;
|
goto exit_rbuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = erase_block();
|
err = mtdtest_erase_eraseblock(mtd, eraseblock);
|
||||||
if (err)
|
if (err)
|
||||||
goto exit_error;
|
goto exit_error;
|
||||||
|
|
||||||
|
@ -428,7 +395,7 @@ static int __init mtd_nandbiterrs_init(void)
|
||||||
goto exit_error;
|
goto exit_error;
|
||||||
|
|
||||||
/* We leave the block un-erased in case of test failure. */
|
/* We leave the block un-erased in case of test failure. */
|
||||||
err = erase_block();
|
err = mtdtest_erase_eraseblock(mtd, eraseblock);
|
||||||
if (err)
|
if (err)
|
||||||
goto exit_error;
|
goto exit_error;
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
static int dev = -EINVAL;
|
static int dev = -EINVAL;
|
||||||
module_param(dev, int, S_IRUGO);
|
module_param(dev, int, S_IRUGO);
|
||||||
MODULE_PARM_DESC(dev, "MTD device number to use");
|
MODULE_PARM_DESC(dev, "MTD device number to use");
|
||||||
|
@ -49,49 +51,6 @@ static int use_len_max;
|
||||||
static int vary_offset;
|
static int vary_offset;
|
||||||
static struct rnd_state rnd_state;
|
static struct rnd_state rnd_state;
|
||||||
|
|
||||||
static int erase_eraseblock(int ebnum)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct erase_info ei;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
|
|
||||||
memset(&ei, 0, sizeof(struct erase_info));
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = mtd->erasesize;
|
|
||||||
|
|
||||||
err = mtd_erase(mtd, &ei);
|
|
||||||
if (err) {
|
|
||||||
pr_err("error %d while erasing EB %d\n", err, ebnum);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ei.state == MTD_ERASE_FAILED) {
|
|
||||||
pr_err("some erase error occurred at EB %d\n", ebnum);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int erase_whole_device(void)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
pr_info("erasing whole device\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
if (bbt[i])
|
|
||||||
continue;
|
|
||||||
err = erase_eraseblock(i);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("erased %u eraseblocks\n", i);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_vary_offset(void)
|
static void do_vary_offset(void)
|
||||||
{
|
{
|
||||||
use_len -= 1;
|
use_len -= 1;
|
||||||
|
@ -304,38 +263,6 @@ static int verify_all_eraseblocks(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_block_bad(int ebnum)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
|
|
||||||
ret = mtd_block_isbad(mtd, addr);
|
|
||||||
if (ret)
|
|
||||||
pr_info("block %d is bad\n", ebnum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int scan_for_bad_eraseblocks(void)
|
|
||||||
{
|
|
||||||
int i, bad = 0;
|
|
||||||
|
|
||||||
bbt = kmalloc(ebcnt, GFP_KERNEL);
|
|
||||||
if (!bbt) {
|
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("scanning for bad eraseblocks\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
bbt[i] = is_block_bad(i) ? 1 : 0;
|
|
||||||
if (bbt[i])
|
|
||||||
bad += 1;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init mtd_oobtest_init(void)
|
static int __init mtd_oobtest_init(void)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -380,17 +307,16 @@ static int __init mtd_oobtest_init(void)
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||||
if (!readbuf) {
|
if (!readbuf)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||||
if (!writebuf) {
|
if (!writebuf)
|
||||||
pr_err("error: cannot allocate memory\n");
|
goto out;
|
||||||
|
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
||||||
|
if (!bbt)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
err = scan_for_bad_eraseblocks();
|
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -402,7 +328,7 @@ static int __init mtd_oobtest_init(void)
|
||||||
/* First test: write all OOB, read it back and verify */
|
/* First test: write all OOB, read it back and verify */
|
||||||
pr_info("test 1 of 5\n");
|
pr_info("test 1 of 5\n");
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -422,7 +348,7 @@ static int __init mtd_oobtest_init(void)
|
||||||
*/
|
*/
|
||||||
pr_info("test 2 of 5\n");
|
pr_info("test 2 of 5\n");
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -452,7 +378,7 @@ static int __init mtd_oobtest_init(void)
|
||||||
*/
|
*/
|
||||||
pr_info("test 3 of 5\n");
|
pr_info("test 3 of 5\n");
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -485,7 +411,7 @@ static int __init mtd_oobtest_init(void)
|
||||||
/* Fourth test: try to write off end of device */
|
/* Fourth test: try to write off end of device */
|
||||||
pr_info("test 4 of 5\n");
|
pr_info("test 4 of 5\n");
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -577,7 +503,7 @@ static int __init mtd_oobtest_init(void)
|
||||||
errcnt += 1;
|
errcnt += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = erase_eraseblock(ebcnt - 1);
|
err = mtdtest_erase_eraseblock(mtd, ebcnt - 1);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -626,7 +552,7 @@ static int __init mtd_oobtest_init(void)
|
||||||
pr_info("test 5 of 5\n");
|
pr_info("test 5 of 5\n");
|
||||||
|
|
||||||
/* Erase all eraseblocks */
|
/* Erase all eraseblocks */
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
static int dev = -EINVAL;
|
static int dev = -EINVAL;
|
||||||
module_param(dev, int, S_IRUGO);
|
module_param(dev, int, S_IRUGO);
|
||||||
MODULE_PARM_DESC(dev, "MTD device number to use");
|
MODULE_PARM_DESC(dev, "MTD device number to use");
|
||||||
|
@ -48,52 +50,18 @@ static int pgcnt;
|
||||||
static int errcnt;
|
static int errcnt;
|
||||||
static struct rnd_state rnd_state;
|
static struct rnd_state rnd_state;
|
||||||
|
|
||||||
static int erase_eraseblock(int ebnum)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct erase_info ei;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
|
|
||||||
memset(&ei, 0, sizeof(struct erase_info));
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = mtd->erasesize;
|
|
||||||
|
|
||||||
err = mtd_erase(mtd, &ei);
|
|
||||||
if (err) {
|
|
||||||
pr_err("error %d while erasing EB %d\n", err, ebnum);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ei.state == MTD_ERASE_FAILED) {
|
|
||||||
pr_err("some erase error occurred at EB %d\n",
|
|
||||||
ebnum);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int write_eraseblock(int ebnum)
|
static int write_eraseblock(int ebnum)
|
||||||
{
|
{
|
||||||
int err = 0;
|
|
||||||
size_t written;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
|
|
||||||
prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
|
prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
|
||||||
cond_resched();
|
cond_resched();
|
||||||
err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf);
|
return mtdtest_write(mtd, addr, mtd->erasesize, writebuf);
|
||||||
if (err || written != mtd->erasesize)
|
|
||||||
pr_err("error: write failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verify_eraseblock(int ebnum)
|
static int verify_eraseblock(int ebnum)
|
||||||
{
|
{
|
||||||
uint32_t j;
|
uint32_t j;
|
||||||
size_t read;
|
|
||||||
int err = 0, i;
|
int err = 0, i;
|
||||||
loff_t addr0, addrn;
|
loff_t addr0, addrn;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
|
@ -109,31 +77,16 @@ static int verify_eraseblock(int ebnum)
|
||||||
prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
|
prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
|
||||||
for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
|
for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
|
||||||
/* Do a read to set the internal dataRAMs to different data */
|
/* Do a read to set the internal dataRAMs to different data */
|
||||||
err = mtd_read(mtd, addr0, bufsize, &read, twopages);
|
err = mtdtest_read(mtd, addr0, bufsize, twopages);
|
||||||
if (mtd_is_bitflip(err))
|
if (err)
|
||||||
err = 0;
|
|
||||||
if (err || read != bufsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr0);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
|
||||||
err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
|
if (err)
|
||||||
if (mtd_is_bitflip(err))
|
|
||||||
err = 0;
|
|
||||||
if (err || read != bufsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)(addrn - bufsize));
|
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
memset(twopages, 0, bufsize);
|
memset(twopages, 0, bufsize);
|
||||||
err = mtd_read(mtd, addr, bufsize, &read, twopages);
|
err = mtdtest_read(mtd, addr, bufsize, twopages);
|
||||||
if (mtd_is_bitflip(err))
|
if (err)
|
||||||
err = 0;
|
|
||||||
if (err || read != bufsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
|
if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
|
||||||
pr_err("error: verify failed at %#llx\n",
|
pr_err("error: verify failed at %#llx\n",
|
||||||
(long long)addr);
|
(long long)addr);
|
||||||
|
@ -145,31 +98,16 @@ static int verify_eraseblock(int ebnum)
|
||||||
struct rnd_state old_state = rnd_state;
|
struct rnd_state old_state = rnd_state;
|
||||||
|
|
||||||
/* Do a read to set the internal dataRAMs to different data */
|
/* Do a read to set the internal dataRAMs to different data */
|
||||||
err = mtd_read(mtd, addr0, bufsize, &read, twopages);
|
err = mtdtest_read(mtd, addr0, bufsize, twopages);
|
||||||
if (mtd_is_bitflip(err))
|
if (err)
|
||||||
err = 0;
|
|
||||||
if (err || read != bufsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr0);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
|
||||||
err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
|
if (err)
|
||||||
if (mtd_is_bitflip(err))
|
|
||||||
err = 0;
|
|
||||||
if (err || read != bufsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)(addrn - bufsize));
|
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
memset(twopages, 0, bufsize);
|
memset(twopages, 0, bufsize);
|
||||||
err = mtd_read(mtd, addr, bufsize, &read, twopages);
|
err = mtdtest_read(mtd, addr, bufsize, twopages);
|
||||||
if (mtd_is_bitflip(err))
|
if (err)
|
||||||
err = 0;
|
|
||||||
if (err || read != bufsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
|
memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
|
||||||
prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
|
prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
|
||||||
if (memcmp(twopages, boundary, bufsize)) {
|
if (memcmp(twopages, boundary, bufsize)) {
|
||||||
|
@ -184,17 +122,14 @@ static int verify_eraseblock(int ebnum)
|
||||||
|
|
||||||
static int crosstest(void)
|
static int crosstest(void)
|
||||||
{
|
{
|
||||||
size_t read;
|
|
||||||
int err = 0, i;
|
int err = 0, i;
|
||||||
loff_t addr, addr0, addrn;
|
loff_t addr, addr0, addrn;
|
||||||
unsigned char *pp1, *pp2, *pp3, *pp4;
|
unsigned char *pp1, *pp2, *pp3, *pp4;
|
||||||
|
|
||||||
pr_info("crosstest\n");
|
pr_info("crosstest\n");
|
||||||
pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
|
pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
|
||||||
if (!pp1) {
|
if (!pp1)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
pp2 = pp1 + pgsize;
|
pp2 = pp1 + pgsize;
|
||||||
pp3 = pp2 + pgsize;
|
pp3 = pp2 + pgsize;
|
||||||
pp4 = pp3 + pgsize;
|
pp4 = pp3 + pgsize;
|
||||||
|
@ -210,24 +145,16 @@ static int crosstest(void)
|
||||||
|
|
||||||
/* Read 2nd-to-last page to pp1 */
|
/* Read 2nd-to-last page to pp1 */
|
||||||
addr = addrn - pgsize - pgsize;
|
addr = addrn - pgsize - pgsize;
|
||||||
err = mtd_read(mtd, addr, pgsize, &read, pp1);
|
err = mtdtest_read(mtd, addr, pgsize, pp1);
|
||||||
if (mtd_is_bitflip(err))
|
if (err) {
|
||||||
err = 0;
|
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
kfree(pp1);
|
kfree(pp1);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read 3rd-to-last page to pp1 */
|
/* Read 3rd-to-last page to pp1 */
|
||||||
addr = addrn - pgsize - pgsize - pgsize;
|
addr = addrn - pgsize - pgsize - pgsize;
|
||||||
err = mtd_read(mtd, addr, pgsize, &read, pp1);
|
err = mtdtest_read(mtd, addr, pgsize, pp1);
|
||||||
if (mtd_is_bitflip(err))
|
if (err) {
|
||||||
err = 0;
|
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
kfree(pp1);
|
kfree(pp1);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -235,12 +162,8 @@ static int crosstest(void)
|
||||||
/* Read first page to pp2 */
|
/* Read first page to pp2 */
|
||||||
addr = addr0;
|
addr = addr0;
|
||||||
pr_info("reading page at %#llx\n", (long long)addr);
|
pr_info("reading page at %#llx\n", (long long)addr);
|
||||||
err = mtd_read(mtd, addr, pgsize, &read, pp2);
|
err = mtdtest_read(mtd, addr, pgsize, pp2);
|
||||||
if (mtd_is_bitflip(err))
|
if (err) {
|
||||||
err = 0;
|
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
kfree(pp1);
|
kfree(pp1);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -248,12 +171,8 @@ static int crosstest(void)
|
||||||
/* Read last page to pp3 */
|
/* Read last page to pp3 */
|
||||||
addr = addrn - pgsize;
|
addr = addrn - pgsize;
|
||||||
pr_info("reading page at %#llx\n", (long long)addr);
|
pr_info("reading page at %#llx\n", (long long)addr);
|
||||||
err = mtd_read(mtd, addr, pgsize, &read, pp3);
|
err = mtdtest_read(mtd, addr, pgsize, pp3);
|
||||||
if (mtd_is_bitflip(err))
|
if (err) {
|
||||||
err = 0;
|
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
kfree(pp1);
|
kfree(pp1);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -261,12 +180,8 @@ static int crosstest(void)
|
||||||
/* Read first page again to pp4 */
|
/* Read first page again to pp4 */
|
||||||
addr = addr0;
|
addr = addr0;
|
||||||
pr_info("reading page at %#llx\n", (long long)addr);
|
pr_info("reading page at %#llx\n", (long long)addr);
|
||||||
err = mtd_read(mtd, addr, pgsize, &read, pp4);
|
err = mtdtest_read(mtd, addr, pgsize, pp4);
|
||||||
if (mtd_is_bitflip(err))
|
if (err) {
|
||||||
err = 0;
|
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
kfree(pp1);
|
kfree(pp1);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -285,7 +200,6 @@ static int crosstest(void)
|
||||||
|
|
||||||
static int erasecrosstest(void)
|
static int erasecrosstest(void)
|
||||||
{
|
{
|
||||||
size_t read, written;
|
|
||||||
int err = 0, i, ebnum, ebnum2;
|
int err = 0, i, ebnum, ebnum2;
|
||||||
loff_t addr0;
|
loff_t addr0;
|
||||||
char *readbuf = twopages;
|
char *readbuf = twopages;
|
||||||
|
@ -304,30 +218,22 @@ static int erasecrosstest(void)
|
||||||
ebnum2 -= 1;
|
ebnum2 -= 1;
|
||||||
|
|
||||||
pr_info("erasing block %d\n", ebnum);
|
pr_info("erasing block %d\n", ebnum);
|
||||||
err = erase_eraseblock(ebnum);
|
err = mtdtest_erase_eraseblock(mtd, ebnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
pr_info("writing 1st page of block %d\n", ebnum);
|
pr_info("writing 1st page of block %d\n", ebnum);
|
||||||
prandom_bytes_state(&rnd_state, writebuf, pgsize);
|
prandom_bytes_state(&rnd_state, writebuf, pgsize);
|
||||||
strcpy(writebuf, "There is no data like this!");
|
strcpy(writebuf, "There is no data like this!");
|
||||||
err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
|
err = mtdtest_write(mtd, addr0, pgsize, writebuf);
|
||||||
if (err || written != pgsize) {
|
if (err)
|
||||||
pr_info("error: write failed at %#llx\n",
|
return err;
|
||||||
(long long)addr0);
|
|
||||||
return err ? err : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("reading 1st page of block %d\n", ebnum);
|
pr_info("reading 1st page of block %d\n", ebnum);
|
||||||
memset(readbuf, 0, pgsize);
|
memset(readbuf, 0, pgsize);
|
||||||
err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
|
err = mtdtest_read(mtd, addr0, pgsize, readbuf);
|
||||||
if (mtd_is_bitflip(err))
|
if (err)
|
||||||
err = 0;
|
return err;
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr0);
|
|
||||||
return err ? err : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("verifying 1st page of block %d\n", ebnum);
|
pr_info("verifying 1st page of block %d\n", ebnum);
|
||||||
if (memcmp(writebuf, readbuf, pgsize)) {
|
if (memcmp(writebuf, readbuf, pgsize)) {
|
||||||
|
@ -337,35 +243,27 @@ static int erasecrosstest(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("erasing block %d\n", ebnum);
|
pr_info("erasing block %d\n", ebnum);
|
||||||
err = erase_eraseblock(ebnum);
|
err = mtdtest_erase_eraseblock(mtd, ebnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
pr_info("writing 1st page of block %d\n", ebnum);
|
pr_info("writing 1st page of block %d\n", ebnum);
|
||||||
prandom_bytes_state(&rnd_state, writebuf, pgsize);
|
prandom_bytes_state(&rnd_state, writebuf, pgsize);
|
||||||
strcpy(writebuf, "There is no data like this!");
|
strcpy(writebuf, "There is no data like this!");
|
||||||
err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
|
err = mtdtest_write(mtd, addr0, pgsize, writebuf);
|
||||||
if (err || written != pgsize) {
|
if (err)
|
||||||
pr_err("error: write failed at %#llx\n",
|
return err;
|
||||||
(long long)addr0);
|
|
||||||
return err ? err : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("erasing block %d\n", ebnum2);
|
pr_info("erasing block %d\n", ebnum2);
|
||||||
err = erase_eraseblock(ebnum2);
|
err = mtdtest_erase_eraseblock(mtd, ebnum2);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
pr_info("reading 1st page of block %d\n", ebnum);
|
pr_info("reading 1st page of block %d\n", ebnum);
|
||||||
memset(readbuf, 0, pgsize);
|
memset(readbuf, 0, pgsize);
|
||||||
err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
|
err = mtdtest_read(mtd, addr0, pgsize, readbuf);
|
||||||
if (mtd_is_bitflip(err))
|
if (err)
|
||||||
err = 0;
|
return err;
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr0);
|
|
||||||
return err ? err : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("verifying 1st page of block %d\n", ebnum);
|
pr_info("verifying 1st page of block %d\n", ebnum);
|
||||||
if (memcmp(writebuf, readbuf, pgsize)) {
|
if (memcmp(writebuf, readbuf, pgsize)) {
|
||||||
|
@ -381,7 +279,6 @@ static int erasecrosstest(void)
|
||||||
|
|
||||||
static int erasetest(void)
|
static int erasetest(void)
|
||||||
{
|
{
|
||||||
size_t read, written;
|
|
||||||
int err = 0, i, ebnum, ok = 1;
|
int err = 0, i, ebnum, ok = 1;
|
||||||
loff_t addr0;
|
loff_t addr0;
|
||||||
|
|
||||||
|
@ -395,33 +292,25 @@ static int erasetest(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("erasing block %d\n", ebnum);
|
pr_info("erasing block %d\n", ebnum);
|
||||||
err = erase_eraseblock(ebnum);
|
err = mtdtest_erase_eraseblock(mtd, ebnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
pr_info("writing 1st page of block %d\n", ebnum);
|
pr_info("writing 1st page of block %d\n", ebnum);
|
||||||
prandom_bytes_state(&rnd_state, writebuf, pgsize);
|
prandom_bytes_state(&rnd_state, writebuf, pgsize);
|
||||||
err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
|
err = mtdtest_write(mtd, addr0, pgsize, writebuf);
|
||||||
if (err || written != pgsize) {
|
if (err)
|
||||||
pr_err("error: write failed at %#llx\n",
|
return err;
|
||||||
(long long)addr0);
|
|
||||||
return err ? err : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("erasing block %d\n", ebnum);
|
pr_info("erasing block %d\n", ebnum);
|
||||||
err = erase_eraseblock(ebnum);
|
err = mtdtest_erase_eraseblock(mtd, ebnum);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
pr_info("reading 1st page of block %d\n", ebnum);
|
pr_info("reading 1st page of block %d\n", ebnum);
|
||||||
err = mtd_read(mtd, addr0, pgsize, &read, twopages);
|
err = mtdtest_read(mtd, addr0, pgsize, twopages);
|
||||||
if (mtd_is_bitflip(err))
|
if (err)
|
||||||
err = 0;
|
return err;
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr0);
|
|
||||||
return err ? err : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("verifying 1st page of block %d is all 0xff\n",
|
pr_info("verifying 1st page of block %d is all 0xff\n",
|
||||||
ebnum);
|
ebnum);
|
||||||
|
@ -440,38 +329,6 @@ static int erasetest(void)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_block_bad(int ebnum)
|
|
||||||
{
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mtd_block_isbad(mtd, addr);
|
|
||||||
if (ret)
|
|
||||||
pr_info("block %d is bad\n", ebnum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int scan_for_bad_eraseblocks(void)
|
|
||||||
{
|
|
||||||
int i, bad = 0;
|
|
||||||
|
|
||||||
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
|
||||||
if (!bbt) {
|
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("scanning for bad eraseblocks\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
bbt[i] = is_block_bad(i) ? 1 : 0;
|
|
||||||
if (bbt[i])
|
|
||||||
bad += 1;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init mtd_pagetest_init(void)
|
static int __init mtd_pagetest_init(void)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -516,36 +373,28 @@ static int __init mtd_pagetest_init(void)
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
bufsize = pgsize * 2;
|
bufsize = pgsize * 2;
|
||||||
writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||||
if (!writebuf) {
|
if (!writebuf)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
twopages = kmalloc(bufsize, GFP_KERNEL);
|
twopages = kmalloc(bufsize, GFP_KERNEL);
|
||||||
if (!twopages) {
|
if (!twopages)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
boundary = kmalloc(bufsize, GFP_KERNEL);
|
boundary = kmalloc(bufsize, GFP_KERNEL);
|
||||||
if (!boundary) {
|
if (!boundary)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
err = scan_for_bad_eraseblocks();
|
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
||||||
|
if (!bbt)
|
||||||
|
goto out;
|
||||||
|
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Erase all eraseblocks */
|
/* Erase all eraseblocks */
|
||||||
pr_info("erasing whole device\n");
|
pr_info("erasing whole device\n");
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (bbt[i])
|
|
||||||
continue;
|
|
||||||
err = erase_eraseblock(i);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
cond_resched();
|
pr_info("erased %u eraseblocks\n", ebcnt);
|
||||||
}
|
|
||||||
pr_info("erased %u eraseblocks\n", i);
|
|
||||||
|
|
||||||
/* Write all eraseblocks */
|
/* Write all eraseblocks */
|
||||||
prandom_seed_state(&rnd_state, 1);
|
prandom_seed_state(&rnd_state, 1);
|
|
@ -29,6 +29,8 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
static int dev = -EINVAL;
|
static int dev = -EINVAL;
|
||||||
module_param(dev, int, S_IRUGO);
|
module_param(dev, int, S_IRUGO);
|
||||||
MODULE_PARM_DESC(dev, "MTD device number to use");
|
MODULE_PARM_DESC(dev, "MTD device number to use");
|
||||||
|
@ -44,7 +46,6 @@ static int pgcnt;
|
||||||
|
|
||||||
static int read_eraseblock_by_page(int ebnum)
|
static int read_eraseblock_by_page(int ebnum)
|
||||||
{
|
{
|
||||||
size_t read;
|
|
||||||
int i, ret, err = 0;
|
int i, ret, err = 0;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
void *buf = iobuf;
|
void *buf = iobuf;
|
||||||
|
@ -52,16 +53,10 @@ static int read_eraseblock_by_page(int ebnum)
|
||||||
|
|
||||||
for (i = 0; i < pgcnt; i++) {
|
for (i = 0; i < pgcnt; i++) {
|
||||||
memset(buf, 0 , pgsize);
|
memset(buf, 0 , pgsize);
|
||||||
ret = mtd_read(mtd, addr, pgsize, &read, buf);
|
ret = mtdtest_read(mtd, addr, pgsize, buf);
|
||||||
if (ret == -EUCLEAN)
|
if (ret) {
|
||||||
ret = 0;
|
|
||||||
if (ret || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
if (!err)
|
if (!err)
|
||||||
err = ret;
|
err = ret;
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
}
|
}
|
||||||
if (mtd->oobsize) {
|
if (mtd->oobsize) {
|
||||||
struct mtd_oob_ops ops;
|
struct mtd_oob_ops ops;
|
||||||
|
@ -127,41 +122,6 @@ static void dump_eraseblock(int ebnum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_block_bad(int ebnum)
|
|
||||||
{
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mtd_block_isbad(mtd, addr);
|
|
||||||
if (ret)
|
|
||||||
pr_info("block %d is bad\n", ebnum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int scan_for_bad_eraseblocks(void)
|
|
||||||
{
|
|
||||||
int i, bad = 0;
|
|
||||||
|
|
||||||
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
|
||||||
if (!bbt) {
|
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mtd_can_have_bb(mtd))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pr_info("scanning for bad eraseblocks\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
bbt[i] = is_block_bad(i) ? 1 : 0;
|
|
||||||
if (bbt[i])
|
|
||||||
bad += 1;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init mtd_readtest_init(void)
|
static int __init mtd_readtest_init(void)
|
||||||
{
|
{
|
||||||
uint64_t tmp;
|
uint64_t tmp;
|
||||||
|
@ -204,17 +164,16 @@ static int __init mtd_readtest_init(void)
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||||
if (!iobuf) {
|
if (!iobuf)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);
|
iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||||
if (!iobuf1) {
|
if (!iobuf1)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
err = scan_for_bad_eraseblocks();
|
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
||||||
|
if (!bbt)
|
||||||
|
goto out;
|
||||||
|
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
static int dev = -EINVAL;
|
static int dev = -EINVAL;
|
||||||
module_param(dev, int, S_IRUGO);
|
module_param(dev, int, S_IRUGO);
|
||||||
MODULE_PARM_DESC(dev, "MTD device number to use");
|
MODULE_PARM_DESC(dev, "MTD device number to use");
|
||||||
|
@ -49,33 +51,6 @@ static int pgcnt;
|
||||||
static int goodebcnt;
|
static int goodebcnt;
|
||||||
static struct timeval start, finish;
|
static struct timeval start, finish;
|
||||||
|
|
||||||
|
|
||||||
static int erase_eraseblock(int ebnum)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct erase_info ei;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
|
|
||||||
memset(&ei, 0, sizeof(struct erase_info));
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = mtd->erasesize;
|
|
||||||
|
|
||||||
err = mtd_erase(mtd, &ei);
|
|
||||||
if (err) {
|
|
||||||
pr_err("error %d while erasing EB %d\n", err, ebnum);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ei.state == MTD_ERASE_FAILED) {
|
|
||||||
pr_err("some erase error occurred at EB %d\n",
|
|
||||||
ebnum);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int multiblock_erase(int ebnum, int blocks)
|
static int multiblock_erase(int ebnum, int blocks)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -103,54 +78,23 @@ static int multiblock_erase(int ebnum, int blocks)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int erase_whole_device(void)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
if (bbt[i])
|
|
||||||
continue;
|
|
||||||
err = erase_eraseblock(i);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int write_eraseblock(int ebnum)
|
static int write_eraseblock(int ebnum)
|
||||||
{
|
{
|
||||||
size_t written;
|
|
||||||
int err = 0;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
|
|
||||||
err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf);
|
return mtdtest_write(mtd, addr, mtd->erasesize, iobuf);
|
||||||
if (err || written != mtd->erasesize) {
|
|
||||||
pr_err("error: write failed at %#llx\n", addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_eraseblock_by_page(int ebnum)
|
static int write_eraseblock_by_page(int ebnum)
|
||||||
{
|
{
|
||||||
size_t written;
|
|
||||||
int i, err = 0;
|
int i, err = 0;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
void *buf = iobuf;
|
void *buf = iobuf;
|
||||||
|
|
||||||
for (i = 0; i < pgcnt; i++) {
|
for (i = 0; i < pgcnt; i++) {
|
||||||
err = mtd_write(mtd, addr, pgsize, &written, buf);
|
err = mtdtest_write(mtd, addr, pgsize, buf);
|
||||||
if (err || written != pgsize) {
|
if (err)
|
||||||
pr_err("error: write failed at %#llx\n",
|
|
||||||
addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
addr += pgsize;
|
addr += pgsize;
|
||||||
buf += pgsize;
|
buf += pgsize;
|
||||||
}
|
}
|
||||||
|
@ -160,74 +104,41 @@ static int write_eraseblock_by_page(int ebnum)
|
||||||
|
|
||||||
static int write_eraseblock_by_2pages(int ebnum)
|
static int write_eraseblock_by_2pages(int ebnum)
|
||||||
{
|
{
|
||||||
size_t written, sz = pgsize * 2;
|
size_t sz = pgsize * 2;
|
||||||
int i, n = pgcnt / 2, err = 0;
|
int i, n = pgcnt / 2, err = 0;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
void *buf = iobuf;
|
void *buf = iobuf;
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
err = mtd_write(mtd, addr, sz, &written, buf);
|
err = mtdtest_write(mtd, addr, sz, buf);
|
||||||
if (err || written != sz) {
|
if (err)
|
||||||
pr_err("error: write failed at %#llx\n",
|
|
||||||
addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
addr += sz;
|
addr += sz;
|
||||||
buf += sz;
|
buf += sz;
|
||||||
}
|
}
|
||||||
if (pgcnt % 2) {
|
if (pgcnt % 2)
|
||||||
err = mtd_write(mtd, addr, pgsize, &written, buf);
|
err = mtdtest_write(mtd, addr, pgsize, buf);
|
||||||
if (err || written != pgsize) {
|
|
||||||
pr_err("error: write failed at %#llx\n",
|
|
||||||
addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_eraseblock(int ebnum)
|
static int read_eraseblock(int ebnum)
|
||||||
{
|
{
|
||||||
size_t read;
|
|
||||||
int err = 0;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
|
|
||||||
err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf);
|
return mtdtest_read(mtd, addr, mtd->erasesize, iobuf);
|
||||||
/* Ignore corrected ECC errors */
|
|
||||||
if (mtd_is_bitflip(err))
|
|
||||||
err = 0;
|
|
||||||
if (err || read != mtd->erasesize) {
|
|
||||||
pr_err("error: read failed at %#llx\n", addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_eraseblock_by_page(int ebnum)
|
static int read_eraseblock_by_page(int ebnum)
|
||||||
{
|
{
|
||||||
size_t read;
|
|
||||||
int i, err = 0;
|
int i, err = 0;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
void *buf = iobuf;
|
void *buf = iobuf;
|
||||||
|
|
||||||
for (i = 0; i < pgcnt; i++) {
|
for (i = 0; i < pgcnt; i++) {
|
||||||
err = mtd_read(mtd, addr, pgsize, &read, buf);
|
err = mtdtest_read(mtd, addr, pgsize, buf);
|
||||||
/* Ignore corrected ECC errors */
|
if (err)
|
||||||
if (mtd_is_bitflip(err))
|
|
||||||
err = 0;
|
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
addr += pgsize;
|
addr += pgsize;
|
||||||
buf += pgsize;
|
buf += pgsize;
|
||||||
}
|
}
|
||||||
|
@ -237,53 +148,24 @@ static int read_eraseblock_by_page(int ebnum)
|
||||||
|
|
||||||
static int read_eraseblock_by_2pages(int ebnum)
|
static int read_eraseblock_by_2pages(int ebnum)
|
||||||
{
|
{
|
||||||
size_t read, sz = pgsize * 2;
|
size_t sz = pgsize * 2;
|
||||||
int i, n = pgcnt / 2, err = 0;
|
int i, n = pgcnt / 2, err = 0;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
loff_t addr = ebnum * mtd->erasesize;
|
||||||
void *buf = iobuf;
|
void *buf = iobuf;
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
err = mtd_read(mtd, addr, sz, &read, buf);
|
err = mtdtest_read(mtd, addr, sz, buf);
|
||||||
/* Ignore corrected ECC errors */
|
if (err)
|
||||||
if (mtd_is_bitflip(err))
|
|
||||||
err = 0;
|
|
||||||
if (err || read != sz) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
addr += sz;
|
addr += sz;
|
||||||
buf += sz;
|
buf += sz;
|
||||||
}
|
}
|
||||||
if (pgcnt % 2) {
|
if (pgcnt % 2)
|
||||||
err = mtd_read(mtd, addr, pgsize, &read, buf);
|
err = mtdtest_read(mtd, addr, pgsize, buf);
|
||||||
/* Ignore corrected ECC errors */
|
|
||||||
if (mtd_is_bitflip(err))
|
|
||||||
err = 0;
|
|
||||||
if (err || read != pgsize) {
|
|
||||||
pr_err("error: read failed at %#llx\n",
|
|
||||||
addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_block_bad(int ebnum)
|
|
||||||
{
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mtd_block_isbad(mtd, addr);
|
|
||||||
if (ret)
|
|
||||||
pr_info("block %d is bad\n", ebnum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void start_timing(void)
|
static inline void start_timing(void)
|
||||||
{
|
{
|
||||||
do_gettimeofday(&start);
|
do_gettimeofday(&start);
|
||||||
|
@ -308,32 +190,6 @@ static long calc_speed(void)
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int scan_for_bad_eraseblocks(void)
|
|
||||||
{
|
|
||||||
int i, bad = 0;
|
|
||||||
|
|
||||||
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
|
||||||
if (!bbt) {
|
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mtd_can_have_bb(mtd))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
pr_info("scanning for bad eraseblocks\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
bbt[i] = is_block_bad(i) ? 1 : 0;
|
|
||||||
if (bbt[i])
|
|
||||||
bad += 1;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
|
||||||
out:
|
|
||||||
goodebcnt = ebcnt - bad;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init mtd_speedtest_init(void)
|
static int __init mtd_speedtest_init(void)
|
||||||
{
|
{
|
||||||
int err, i, blocks, j, k;
|
int err, i, blocks, j, k;
|
||||||
|
@ -384,18 +240,23 @@ static int __init mtd_speedtest_init(void)
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||||
if (!iobuf) {
|
if (!iobuf)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
prandom_bytes(iobuf, mtd->erasesize);
|
prandom_bytes(iobuf, mtd->erasesize);
|
||||||
|
|
||||||
err = scan_for_bad_eraseblocks();
|
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
||||||
|
if (!bbt)
|
||||||
|
goto out;
|
||||||
|
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
for (i = 0; i < ebcnt; i++) {
|
||||||
|
if (!bbt[i])
|
||||||
|
goodebcnt++;
|
||||||
|
}
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -429,7 +290,7 @@ static int __init mtd_speedtest_init(void)
|
||||||
speed = calc_speed();
|
speed = calc_speed();
|
||||||
pr_info("eraseblock read speed is %ld KiB/s\n", speed);
|
pr_info("eraseblock read speed is %ld KiB/s\n", speed);
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -463,7 +324,7 @@ static int __init mtd_speedtest_init(void)
|
||||||
speed = calc_speed();
|
speed = calc_speed();
|
||||||
pr_info("page read speed is %ld KiB/s\n", speed);
|
pr_info("page read speed is %ld KiB/s\n", speed);
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -500,14 +361,9 @@ static int __init mtd_speedtest_init(void)
|
||||||
/* Erase all eraseblocks */
|
/* Erase all eraseblocks */
|
||||||
pr_info("Testing erase speed\n");
|
pr_info("Testing erase speed\n");
|
||||||
start_timing();
|
start_timing();
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (bbt[i])
|
|
||||||
continue;
|
|
||||||
err = erase_eraseblock(i);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
stop_timing();
|
stop_timing();
|
||||||
speed = calc_speed();
|
speed = calc_speed();
|
||||||
pr_info("erase speed is %ld KiB/s\n", speed);
|
pr_info("erase speed is %ld KiB/s\n", speed);
|
|
@ -31,6 +31,8 @@
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
static int dev = -EINVAL;
|
static int dev = -EINVAL;
|
||||||
module_param(dev, int, S_IRUGO);
|
module_param(dev, int, S_IRUGO);
|
||||||
MODULE_PARM_DESC(dev, "MTD device number to use");
|
MODULE_PARM_DESC(dev, "MTD device number to use");
|
||||||
|
@ -81,49 +83,11 @@ static int rand_len(int offs)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int erase_eraseblock(int ebnum)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct erase_info ei;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
|
|
||||||
memset(&ei, 0, sizeof(struct erase_info));
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = mtd->erasesize;
|
|
||||||
|
|
||||||
err = mtd_erase(mtd, &ei);
|
|
||||||
if (unlikely(err)) {
|
|
||||||
pr_err("error %d while erasing EB %d\n", err, ebnum);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(ei.state == MTD_ERASE_FAILED)) {
|
|
||||||
pr_err("some erase error occurred at EB %d\n",
|
|
||||||
ebnum);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_block_bad(int ebnum)
|
|
||||||
{
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mtd_block_isbad(mtd, addr);
|
|
||||||
if (ret)
|
|
||||||
pr_info("block %d is bad\n", ebnum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int do_read(void)
|
static int do_read(void)
|
||||||
{
|
{
|
||||||
size_t read;
|
|
||||||
int eb = rand_eb();
|
int eb = rand_eb();
|
||||||
int offs = rand_offs();
|
int offs = rand_offs();
|
||||||
int len = rand_len(offs), err;
|
int len = rand_len(offs);
|
||||||
loff_t addr;
|
loff_t addr;
|
||||||
|
|
||||||
if (bbt[eb + 1]) {
|
if (bbt[eb + 1]) {
|
||||||
|
@ -133,28 +97,17 @@ static int do_read(void)
|
||||||
len = mtd->erasesize - offs;
|
len = mtd->erasesize - offs;
|
||||||
}
|
}
|
||||||
addr = eb * mtd->erasesize + offs;
|
addr = eb * mtd->erasesize + offs;
|
||||||
err = mtd_read(mtd, addr, len, &read, readbuf);
|
return mtdtest_read(mtd, addr, len, readbuf);
|
||||||
if (mtd_is_bitflip(err))
|
|
||||||
err = 0;
|
|
||||||
if (unlikely(err || read != len)) {
|
|
||||||
pr_err("error: read failed at 0x%llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_write(void)
|
static int do_write(void)
|
||||||
{
|
{
|
||||||
int eb = rand_eb(), offs, err, len;
|
int eb = rand_eb(), offs, err, len;
|
||||||
size_t written;
|
|
||||||
loff_t addr;
|
loff_t addr;
|
||||||
|
|
||||||
offs = offsets[eb];
|
offs = offsets[eb];
|
||||||
if (offs >= mtd->erasesize) {
|
if (offs >= mtd->erasesize) {
|
||||||
err = erase_eraseblock(eb);
|
err = mtdtest_erase_eraseblock(mtd, eb);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
offs = offsets[eb] = 0;
|
offs = offsets[eb] = 0;
|
||||||
|
@ -165,21 +118,16 @@ static int do_write(void)
|
||||||
if (bbt[eb + 1])
|
if (bbt[eb + 1])
|
||||||
len = mtd->erasesize - offs;
|
len = mtd->erasesize - offs;
|
||||||
else {
|
else {
|
||||||
err = erase_eraseblock(eb + 1);
|
err = mtdtest_erase_eraseblock(mtd, eb + 1);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
offsets[eb + 1] = 0;
|
offsets[eb + 1] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addr = eb * mtd->erasesize + offs;
|
addr = eb * mtd->erasesize + offs;
|
||||||
err = mtd_write(mtd, addr, len, &written, writebuf);
|
err = mtdtest_write(mtd, addr, len, writebuf);
|
||||||
if (unlikely(err || written != len)) {
|
if (unlikely(err))
|
||||||
pr_err("error: write failed at 0x%llx\n",
|
|
||||||
(long long)addr);
|
|
||||||
if (!err)
|
|
||||||
err = -EINVAL;
|
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
offs += len;
|
offs += len;
|
||||||
while (offs > mtd->erasesize) {
|
while (offs > mtd->erasesize) {
|
||||||
offsets[eb++] = mtd->erasesize;
|
offsets[eb++] = mtd->erasesize;
|
||||||
|
@ -197,30 +145,6 @@ static int do_operation(void)
|
||||||
return do_write();
|
return do_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int scan_for_bad_eraseblocks(void)
|
|
||||||
{
|
|
||||||
int i, bad = 0;
|
|
||||||
|
|
||||||
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
|
||||||
if (!bbt) {
|
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mtd_can_have_bb(mtd))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pr_info("scanning for bad eraseblocks\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
bbt[i] = is_block_bad(i) ? 1 : 0;
|
|
||||||
if (bbt[i])
|
|
||||||
bad += 1;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init mtd_stresstest_init(void)
|
static int __init mtd_stresstest_init(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -276,15 +200,16 @@ static int __init mtd_stresstest_init(void)
|
||||||
readbuf = vmalloc(bufsize);
|
readbuf = vmalloc(bufsize);
|
||||||
writebuf = vmalloc(bufsize);
|
writebuf = vmalloc(bufsize);
|
||||||
offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
|
offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
|
||||||
if (!readbuf || !writebuf || !offsets) {
|
if (!readbuf || !writebuf || !offsets)
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
for (i = 0; i < ebcnt; i++)
|
for (i = 0; i < ebcnt; i++)
|
||||||
offsets[i] = mtd->erasesize;
|
offsets[i] = mtd->erasesize;
|
||||||
prandom_bytes(writebuf, bufsize);
|
prandom_bytes(writebuf, bufsize);
|
||||||
|
|
||||||
err = scan_for_bad_eraseblocks();
|
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
||||||
|
if (!bbt)
|
||||||
|
goto out;
|
||||||
|
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
static int dev = -EINVAL;
|
static int dev = -EINVAL;
|
||||||
module_param(dev, int, S_IRUGO);
|
module_param(dev, int, S_IRUGO);
|
||||||
MODULE_PARM_DESC(dev, "MTD device number to use");
|
MODULE_PARM_DESC(dev, "MTD device number to use");
|
||||||
|
@ -51,50 +53,6 @@ static inline void clear_data(unsigned char *buf, size_t len)
|
||||||
memset(buf, 0, len);
|
memset(buf, 0, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int erase_eraseblock(int ebnum)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct erase_info ei;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
|
|
||||||
memset(&ei, 0, sizeof(struct erase_info));
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = mtd->erasesize;
|
|
||||||
|
|
||||||
err = mtd_erase(mtd, &ei);
|
|
||||||
if (err) {
|
|
||||||
pr_err("error %d while erasing EB %d\n", err, ebnum);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ei.state == MTD_ERASE_FAILED) {
|
|
||||||
pr_err("some erase error occurred at EB %d\n",
|
|
||||||
ebnum);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int erase_whole_device(void)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
pr_info("erasing whole device\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
if (bbt[i])
|
|
||||||
continue;
|
|
||||||
err = erase_eraseblock(i);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("erased %u eraseblocks\n", i);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int write_eraseblock(int ebnum)
|
static int write_eraseblock(int ebnum)
|
||||||
{
|
{
|
||||||
size_t written;
|
size_t written;
|
||||||
|
@ -317,38 +275,6 @@ static int verify_all_eraseblocks_ff(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_block_bad(int ebnum)
|
|
||||||
{
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mtd_block_isbad(mtd, addr);
|
|
||||||
if (ret)
|
|
||||||
pr_info("block %d is bad\n", ebnum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int scan_for_bad_eraseblocks(void)
|
|
||||||
{
|
|
||||||
int i, bad = 0;
|
|
||||||
|
|
||||||
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
|
||||||
if (!bbt) {
|
|
||||||
pr_err("error: cannot allocate memory\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("scanning for bad eraseblocks\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
|
||||||
bbt[i] = is_block_bad(i) ? 1 : 0;
|
|
||||||
if (bbt[i])
|
|
||||||
bad += 1;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init mtd_subpagetest_init(void)
|
static int __init mtd_subpagetest_init(void)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -393,21 +319,20 @@ static int __init mtd_subpagetest_init(void)
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
bufsize = subpgsize * 32;
|
bufsize = subpgsize * 32;
|
||||||
writebuf = kmalloc(bufsize, GFP_KERNEL);
|
writebuf = kmalloc(bufsize, GFP_KERNEL);
|
||||||
if (!writebuf) {
|
if (!writebuf)
|
||||||
pr_info("error: cannot allocate memory\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
readbuf = kmalloc(bufsize, GFP_KERNEL);
|
readbuf = kmalloc(bufsize, GFP_KERNEL);
|
||||||
if (!readbuf) {
|
if (!readbuf)
|
||||||
pr_info("error: cannot allocate memory\n");
|
goto out;
|
||||||
|
bbt = kzalloc(ebcnt, GFP_KERNEL);
|
||||||
|
if (!bbt)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
err = scan_for_bad_eraseblocks();
|
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -439,7 +364,7 @@ static int __init mtd_subpagetest_init(void)
|
||||||
}
|
}
|
||||||
pr_info("verified %u eraseblocks\n", i);
|
pr_info("verified %u eraseblocks\n", i);
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -477,7 +402,7 @@ static int __init mtd_subpagetest_init(void)
|
||||||
}
|
}
|
||||||
pr_info("verified %u eraseblocks\n", i);
|
pr_info("verified %u eraseblocks\n", i);
|
||||||
|
|
||||||
err = erase_whole_device();
|
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
#include "mtd_test.h"
|
||||||
|
|
||||||
#define RETRIES 3
|
#define RETRIES 3
|
||||||
|
|
||||||
|
@ -92,35 +93,6 @@ static inline void stop_timing(void)
|
||||||
do_gettimeofday(&finish);
|
do_gettimeofday(&finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Erase eraseblock number @ebnum.
|
|
||||||
*/
|
|
||||||
static inline int erase_eraseblock(int ebnum)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct erase_info ei;
|
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
|
||||||
|
|
||||||
memset(&ei, 0, sizeof(struct erase_info));
|
|
||||||
ei.mtd = mtd;
|
|
||||||
ei.addr = addr;
|
|
||||||
ei.len = mtd->erasesize;
|
|
||||||
|
|
||||||
err = mtd_erase(mtd, &ei);
|
|
||||||
if (err) {
|
|
||||||
pr_err("error %d while erasing EB %d\n", err, ebnum);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ei.state == MTD_ERASE_FAILED) {
|
|
||||||
pr_err("some erase error occurred at EB %d\n",
|
|
||||||
ebnum);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that the contents of eraseblock number @enbum is equivalent to the
|
* Check that the contents of eraseblock number @enbum is equivalent to the
|
||||||
* @buf buffer.
|
* @buf buffer.
|
||||||
|
@ -208,7 +180,7 @@ static inline int write_pattern(int ebnum, void *buf)
|
||||||
static int __init tort_init(void)
|
static int __init tort_init(void)
|
||||||
{
|
{
|
||||||
int err = 0, i, infinite = !cycles_count;
|
int err = 0, i, infinite = !cycles_count;
|
||||||
int *bad_ebs;
|
unsigned char *bad_ebs;
|
||||||
|
|
||||||
printk(KERN_INFO "\n");
|
printk(KERN_INFO "\n");
|
||||||
printk(KERN_INFO "=================================================\n");
|
printk(KERN_INFO "=================================================\n");
|
||||||
|
@ -265,7 +237,7 @@ static int __init tort_init(void)
|
||||||
if (!check_buf)
|
if (!check_buf)
|
||||||
goto out_patt_FF;
|
goto out_patt_FF;
|
||||||
|
|
||||||
bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL);
|
bad_ebs = kzalloc(ebcnt, GFP_KERNEL);
|
||||||
if (!bad_ebs)
|
if (!bad_ebs)
|
||||||
goto out_check_buf;
|
goto out_check_buf;
|
||||||
|
|
||||||
|
@ -283,40 +255,16 @@ static int __init tort_init(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
err = mtdtest_scan_for_bad_eraseblocks(mtd, bad_ebs, eb, ebcnt);
|
||||||
* Check if there is a bad eraseblock among those we are going to test.
|
if (err)
|
||||||
*/
|
|
||||||
if (mtd_can_have_bb(mtd)) {
|
|
||||||
for (i = eb; i < eb + ebcnt; i++) {
|
|
||||||
err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize);
|
|
||||||
|
|
||||||
if (err < 0) {
|
|
||||||
pr_info("block_isbad() returned %d "
|
|
||||||
"for EB %d\n", err, i);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
pr_err("EB %d is bad. Skip it.\n", i);
|
|
||||||
bad_ebs[i - eb] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start_timing();
|
start_timing();
|
||||||
while (1) {
|
while (1) {
|
||||||
int i;
|
int i;
|
||||||
void *patt;
|
void *patt;
|
||||||
|
|
||||||
/* Erase all eraseblocks */
|
mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt);
|
||||||
for (i = eb; i < eb + ebcnt; i++) {
|
|
||||||
if (bad_ebs[i - eb])
|
|
||||||
continue;
|
|
||||||
err = erase_eraseblock(i);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the eraseblocks contain only 0xFF bytes */
|
/* Check if the eraseblocks contain only 0xFF bytes */
|
||||||
if (check) {
|
if (check) {
|
|
@ -93,8 +93,6 @@ struct nand_bbt_descr {
|
||||||
#define NAND_BBT_CREATE_EMPTY 0x00000400
|
#define NAND_BBT_CREATE_EMPTY 0x00000400
|
||||||
/* Search good / bad pattern through all pages of a block */
|
/* Search good / bad pattern through all pages of a block */
|
||||||
#define NAND_BBT_SCANALLPAGES 0x00000800
|
#define NAND_BBT_SCANALLPAGES 0x00000800
|
||||||
/* Scan block empty during good / bad block scan */
|
|
||||||
#define NAND_BBT_SCANEMPTY 0x00001000
|
|
||||||
/* Write bbt if neccecary */
|
/* Write bbt if neccecary */
|
||||||
#define NAND_BBT_WRITE 0x00002000
|
#define NAND_BBT_WRITE 0x00002000
|
||||||
/* Read and write back block contents when writing bbt */
|
/* Read and write back block contents when writing bbt */
|
||||||
|
|
|
@ -137,6 +137,7 @@ enum access_mode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fsmc_nand_platform_data - platform specific NAND controller config
|
* fsmc_nand_platform_data - platform specific NAND controller config
|
||||||
|
* @nand_timings: timing setup for the physical NAND interface
|
||||||
* @partitions: partition table for the platform, use a default fallback
|
* @partitions: partition table for the platform, use a default fallback
|
||||||
* if this is NULL
|
* if this is NULL
|
||||||
* @nr_partitions: the number of partitions in the previous entry
|
* @nr_partitions: the number of partitions in the previous entry
|
||||||
|
|
|
@ -173,6 +173,9 @@ struct mtd_info {
|
||||||
/* ECC layout structure pointer - read only! */
|
/* ECC layout structure pointer - read only! */
|
||||||
struct nand_ecclayout *ecclayout;
|
struct nand_ecclayout *ecclayout;
|
||||||
|
|
||||||
|
/* the ecc step size. */
|
||||||
|
unsigned int ecc_step_size;
|
||||||
|
|
||||||
/* max number of correctible bit errors per ecc step */
|
/* max number of correctible bit errors per ecc step */
|
||||||
unsigned int ecc_strength;
|
unsigned int ecc_strength;
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||||
* is supported now. If you add a chip with bigger oobsize/page
|
* is supported now. If you add a chip with bigger oobsize/page
|
||||||
* adjust this accordingly.
|
* adjust this accordingly.
|
||||||
*/
|
*/
|
||||||
#define NAND_MAX_OOBSIZE 640
|
#define NAND_MAX_OOBSIZE 744
|
||||||
#define NAND_MAX_PAGESIZE 8192
|
#define NAND_MAX_PAGESIZE 8192
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -202,6 +202,10 @@ typedef enum {
|
||||||
/* Keep gcc happy */
|
/* Keep gcc happy */
|
||||||
struct nand_chip;
|
struct nand_chip;
|
||||||
|
|
||||||
|
/* ONFI features */
|
||||||
|
#define ONFI_FEATURE_16_BIT_BUS (1 << 0)
|
||||||
|
#define ONFI_FEATURE_EXT_PARAM_PAGE (1 << 7)
|
||||||
|
|
||||||
/* ONFI timing mode, used in both asynchronous and synchronous mode */
|
/* ONFI timing mode, used in both asynchronous and synchronous mode */
|
||||||
#define ONFI_TIMING_MODE_0 (1 << 0)
|
#define ONFI_TIMING_MODE_0 (1 << 0)
|
||||||
#define ONFI_TIMING_MODE_1 (1 << 1)
|
#define ONFI_TIMING_MODE_1 (1 << 1)
|
||||||
|
@ -217,6 +221,9 @@ struct nand_chip;
|
||||||
/* ONFI subfeature parameters length */
|
/* ONFI subfeature parameters length */
|
||||||
#define ONFI_SUBFEATURE_PARAM_LEN 4
|
#define ONFI_SUBFEATURE_PARAM_LEN 4
|
||||||
|
|
||||||
|
/* ONFI optional commands SET/GET FEATURES supported? */
|
||||||
|
#define ONFI_OPT_CMD_SET_GET_FEATURES (1 << 2)
|
||||||
|
|
||||||
struct nand_onfi_params {
|
struct nand_onfi_params {
|
||||||
/* rev info and features block */
|
/* rev info and features block */
|
||||||
/* 'O' 'N' 'F' 'I' */
|
/* 'O' 'N' 'F' 'I' */
|
||||||
|
@ -224,7 +231,10 @@ struct nand_onfi_params {
|
||||||
__le16 revision;
|
__le16 revision;
|
||||||
__le16 features;
|
__le16 features;
|
||||||
__le16 opt_cmd;
|
__le16 opt_cmd;
|
||||||
u8 reserved[22];
|
u8 reserved0[2];
|
||||||
|
__le16 ext_param_page_length; /* since ONFI 2.1 */
|
||||||
|
u8 num_of_param_pages; /* since ONFI 2.1 */
|
||||||
|
u8 reserved1[17];
|
||||||
|
|
||||||
/* manufacturer information block */
|
/* manufacturer information block */
|
||||||
char manufacturer[12];
|
char manufacturer[12];
|
||||||
|
@ -281,6 +291,40 @@ struct nand_onfi_params {
|
||||||
|
|
||||||
#define ONFI_CRC_BASE 0x4F4E
|
#define ONFI_CRC_BASE 0x4F4E
|
||||||
|
|
||||||
|
/* Extended ECC information Block Definition (since ONFI 2.1) */
|
||||||
|
struct onfi_ext_ecc_info {
|
||||||
|
u8 ecc_bits;
|
||||||
|
u8 codeword_size;
|
||||||
|
__le16 bb_per_lun;
|
||||||
|
__le16 block_endurance;
|
||||||
|
u8 reserved[2];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define ONFI_SECTION_TYPE_0 0 /* Unused section. */
|
||||||
|
#define ONFI_SECTION_TYPE_1 1 /* for additional sections. */
|
||||||
|
#define ONFI_SECTION_TYPE_2 2 /* for ECC information. */
|
||||||
|
struct onfi_ext_section {
|
||||||
|
u8 type;
|
||||||
|
u8 length;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define ONFI_EXT_SECTION_MAX 8
|
||||||
|
|
||||||
|
/* Extended Parameter Page Definition (since ONFI 2.1) */
|
||||||
|
struct onfi_ext_param_page {
|
||||||
|
__le16 crc;
|
||||||
|
u8 sig[4]; /* 'E' 'P' 'P' 'S' */
|
||||||
|
u8 reserved0[10];
|
||||||
|
struct onfi_ext_section sections[ONFI_EXT_SECTION_MAX];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The actual size of the Extended Parameter Page is in
|
||||||
|
* @ext_param_page_length of nand_onfi_params{}.
|
||||||
|
* The following are the variable length sections.
|
||||||
|
* So we do not add any fields below. Please see the ONFI spec.
|
||||||
|
*/
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
|
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
|
||||||
* @lock: protection lock
|
* @lock: protection lock
|
||||||
|
@ -390,8 +434,8 @@ struct nand_buffers {
|
||||||
* @write_buf: [REPLACEABLE] write data from the buffer to the chip
|
* @write_buf: [REPLACEABLE] write data from the buffer to the chip
|
||||||
* @read_buf: [REPLACEABLE] read data from the chip into the buffer
|
* @read_buf: [REPLACEABLE] read data from the chip into the buffer
|
||||||
* @select_chip: [REPLACEABLE] select chip nr
|
* @select_chip: [REPLACEABLE] select chip nr
|
||||||
* @block_bad: [REPLACEABLE] check, if the block is bad
|
* @block_bad: [REPLACEABLE] check if a block is bad, using OOB markers
|
||||||
* @block_markbad: [REPLACEABLE] mark the block bad
|
* @block_markbad: [REPLACEABLE] mark a block bad
|
||||||
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
||||||
* ALE/CLE/nCE. Also used to write command and address
|
* ALE/CLE/nCE. Also used to write command and address
|
||||||
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
||||||
|
@ -434,6 +478,12 @@ struct nand_buffers {
|
||||||
* bad block marker position; i.e., BBM == 11110111b is
|
* bad block marker position; i.e., BBM == 11110111b is
|
||||||
* not bad when badblockbits == 7
|
* not bad when badblockbits == 7
|
||||||
* @cellinfo: [INTERN] MLC/multichip data from chip ident
|
* @cellinfo: [INTERN] MLC/multichip data from chip ident
|
||||||
|
* @ecc_strength_ds: [INTERN] ECC correctability from the datasheet.
|
||||||
|
* Minimum amount of bit errors per @ecc_step_ds guaranteed
|
||||||
|
* to be correctable. If unknown, set to zero.
|
||||||
|
* @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds,
|
||||||
|
* also from the datasheet. It is the recommended ECC step
|
||||||
|
* size, if known; if unknown, set to zero.
|
||||||
* @numchips: [INTERN] number of physical chips
|
* @numchips: [INTERN] number of physical chips
|
||||||
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
||||||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||||
|
@ -510,6 +560,8 @@ struct nand_chip {
|
||||||
unsigned int pagebuf_bitflips;
|
unsigned int pagebuf_bitflips;
|
||||||
int subpagesize;
|
int subpagesize;
|
||||||
uint8_t cellinfo;
|
uint8_t cellinfo;
|
||||||
|
uint16_t ecc_strength_ds;
|
||||||
|
uint16_t ecc_step_ds;
|
||||||
int badblockpos;
|
int badblockpos;
|
||||||
int badblockbits;
|
int badblockbits;
|
||||||
|
|
||||||
|
@ -576,6 +628,11 @@ struct nand_chip {
|
||||||
{ .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \
|
{ .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \
|
||||||
.options = (opts) }
|
.options = (opts) }
|
||||||
|
|
||||||
|
#define NAND_ECC_INFO(_strength, _step) \
|
||||||
|
{ .strength_ds = (_strength), .step_ds = (_step) }
|
||||||
|
#define NAND_ECC_STRENGTH(type) ((type)->ecc.strength_ds)
|
||||||
|
#define NAND_ECC_STEP(type) ((type)->ecc.step_ds)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nand_flash_dev - NAND Flash Device ID Structure
|
* struct nand_flash_dev - NAND Flash Device ID Structure
|
||||||
* @name: a human-readable name of the NAND chip
|
* @name: a human-readable name of the NAND chip
|
||||||
|
@ -593,6 +650,12 @@ struct nand_chip {
|
||||||
* @options: stores various chip bit options
|
* @options: stores various chip bit options
|
||||||
* @id_len: The valid length of the @id.
|
* @id_len: The valid length of the @id.
|
||||||
* @oobsize: OOB size
|
* @oobsize: OOB size
|
||||||
|
* @ecc.strength_ds: The ECC correctability from the datasheet, same as the
|
||||||
|
* @ecc_strength_ds in nand_chip{}.
|
||||||
|
* @ecc.step_ds: The ECC step required by the @ecc.strength_ds, same as the
|
||||||
|
* @ecc_step_ds in nand_chip{}, also from the datasheet.
|
||||||
|
* For example, the "4bit ECC for each 512Byte" can be set with
|
||||||
|
* NAND_ECC_INFO(4, 512).
|
||||||
*/
|
*/
|
||||||
struct nand_flash_dev {
|
struct nand_flash_dev {
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -609,6 +672,10 @@ struct nand_flash_dev {
|
||||||
unsigned int options;
|
unsigned int options;
|
||||||
uint16_t id_len;
|
uint16_t id_len;
|
||||||
uint16_t oobsize;
|
uint16_t oobsize;
|
||||||
|
struct {
|
||||||
|
uint16_t strength_ds;
|
||||||
|
uint16_t step_ds;
|
||||||
|
} ecc;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -625,8 +692,8 @@ extern struct nand_flash_dev nand_flash_ids[];
|
||||||
extern struct nand_manufacturers nand_manuf_ids[];
|
extern struct nand_manufacturers nand_manuf_ids[];
|
||||||
|
|
||||||
extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
||||||
extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
|
|
||||||
extern int nand_default_bbt(struct mtd_info *mtd);
|
extern int nand_default_bbt(struct mtd_info *mtd);
|
||||||
|
extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
||||||
extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
|
extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
|
||||||
extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||||
int allowbbt);
|
int allowbbt);
|
||||||
|
@ -708,6 +775,12 @@ struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)
|
||||||
return chip->priv;
|
return chip->priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* return the supported features. */
|
||||||
|
static inline int onfi_feature(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
return chip->onfi_version ? le16_to_cpu(chip->onfi_params.features) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* return the supported asynchronous timing mode. */
|
/* return the supported asynchronous timing mode. */
|
||||||
static inline int onfi_get_async_timing_mode(struct nand_chip *chip)
|
static inline int onfi_get_async_timing_mode(struct nand_chip *chip)
|
||||||
{
|
{
|
||||||
|
|
|
@ -71,6 +71,10 @@ struct atmel_nand_data {
|
||||||
u8 on_flash_bbt; /* bbt on flash */
|
u8 on_flash_bbt; /* bbt on flash */
|
||||||
struct mtd_partition *parts;
|
struct mtd_partition *parts;
|
||||||
unsigned int num_parts;
|
unsigned int num_parts;
|
||||||
|
bool has_dma; /* support dma transfer */
|
||||||
|
|
||||||
|
/* default is false, only for at32ap7000 chip is true */
|
||||||
|
bool need_reset_workaround;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Serial */
|
/* Serial */
|
||||||
|
|
|
@ -16,19 +16,6 @@ struct pxa3xx_nand_timing {
|
||||||
unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
|
unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pxa3xx_nand_cmdset {
|
|
||||||
uint16_t read1;
|
|
||||||
uint16_t read2;
|
|
||||||
uint16_t program;
|
|
||||||
uint16_t read_status;
|
|
||||||
uint16_t read_id;
|
|
||||||
uint16_t erase;
|
|
||||||
uint16_t reset;
|
|
||||||
uint16_t lock;
|
|
||||||
uint16_t unlock;
|
|
||||||
uint16_t lock_status;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pxa3xx_nand_flash {
|
struct pxa3xx_nand_flash {
|
||||||
char *name;
|
char *name;
|
||||||
uint32_t chip_id;
|
uint32_t chip_id;
|
||||||
|
|
Loading…
Reference in a new issue