1
0
Fork 0

- 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
  ...
hifive-unleashed-5.1
Linus Torvalds 2013-09-09 10:33:19 -07:00
commit ef9a61bef9
99 changed files with 2734 additions and 3203 deletions

View File

@ -128,9 +128,8 @@ KernelVersion: 3.4
Contact: linux-mtd@lists.infradead.org
Description:
Maximum number of bit errors that the device is capable of
correcting within each region covering an ecc step. This will
always be a non-negative integer. Note that some devices will
have multiple ecc steps within each writesize region.
correcting within each region covering an ECC step (see
ecc_step_size). This will always be a non-negative integer.
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
capability. It is ignored on devices lacking ECC capability;
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.

View File

@ -1224,8 +1224,6 @@ in this page</entry>
#define NAND_BBT_CREATE 0x00000200
/* Search good / bad pattern through all pages of a block */
#define NAND_BBT_SCANALLPAGES 0x00000400
/* Scan block empty during good / bad block scan */
#define NAND_BBT_SCANEMPTY 0x00000800
/* Write bbt if neccecary */
#define NAND_BBT_WRITE 0x00001000
/* Read and write back block contents when writing bbt */

View File

@ -15,6 +15,7 @@ Required properties:
optional gpio and may be set to 0 if not present.
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.
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
"soft_bch".
@ -29,6 +30,14 @@ Optional properties:
sector size 1024.
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
- 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:
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 */
>;
};
};

View File

@ -1,4 +1,5 @@
* FSMC NAND
ST Microelectronics Flexible Static Memory Controller (FSMC)
NAND Interface
Required properties:
- 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
defaults to 1 byte
- 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:
@ -24,6 +45,8 @@ Example:
bank-width = <1>;
nand-skip-bbtscan;
timings = /bits/ 8 <0 0 0 2 3 0>;
bank = <1>;
partition@0 {
...

View File

@ -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
used for what purposes, but which don't use an on-flash partition table such
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
two valid values for both:

View File

@ -1983,6 +1983,9 @@ at32_add_device_nand(unsigned int id, struct atmel_nand_data *data)
ARRAY_SIZE(smc_cs3_resource)))
goto fail;
/* For at32ap7000, we use the reset workaround for nand driver */
data->need_reset_workaround = true;
if (platform_device_add_data(pdev, data,
sizeof(struct atmel_nand_data)))
goto fail;

View File

@ -35,6 +35,8 @@ struct bcm963xx_nvram {
u32 checksum_high;
};
#define BCM63XX_DEFAULT_PSI_SIZE 64
static struct bcm963xx_nvram nvram;
static int mac_addr_used;
@ -114,3 +116,12 @@ int bcm63xx_nvram_get_mac_address(u8 *mac)
return 0;
}
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);

View File

@ -30,4 +30,6 @@ u8 *bcm63xx_nvram_get_name(void);
*/
int bcm63xx_nvram_get_mac_address(u8 *mac);
int bcm63xx_nvram_get_psi_size(void);
#endif /* BCM63XX_NVRAM_H */

View File

@ -4,7 +4,7 @@
* Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
* Mike Albon <malbon@openwrt.org>
* 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
* it under the terms of the GNU General Public License as published by
@ -27,17 +27,19 @@
#include <linux/crc32.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <asm/mach-bcm63xx/bcm63xx_nvram.h>
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
#include <asm/mach-bcm63xx/board_bcm963xx.h>
#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
@ -90,7 +92,8 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
BCM63XX_CFE_BLOCK_SIZE);
cfelen = cfe_erasesize;
nvramlen = cfe_erasesize;
nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K;
nvramlen = roundup(nvramlen, cfe_erasesize);
/* Allocate memory for buffer */
buf = vmalloc(sizeof(struct bcm_tag));

View File

@ -1571,8 +1571,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
xip_enable(map, chip, adr);
/* FIXME - should have reset delay before continuing */
printk(KERN_WARNING "MTD %s(): software timeout\n",
__func__ );
printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n",
__func__, adr);
ret = -EIO;
op_done:

View File

@ -211,9 +211,7 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map,
probe_function = __symbol_get(probename);
if (!probe_function) {
char modname[sizeof("cfi_cmdset_%4.4X")];
sprintf(modname, "cfi_cmdset_%4.4X", type);
request_module(modname);
request_module("cfi_cmdset_%4.4X", type);
probe_function = __symbol_get(probename);
}

View File

@ -120,7 +120,7 @@
#define PM49FL008 0x006A
/* Sharp */
#define LH28F640BF 0x00b0
#define LH28F640BF 0x00B0
/* ST - www.st.com */
#define M29F800AB 0x0058
@ -1299,13 +1299,14 @@ static const struct amd_flash_info jedec_table[] = {
.mfr_id = CFI_MFR_SHARP,
.dev_id = LH28F640BF,
.name = "LH28F640BF",
.devtypes = CFI_DEVICETYPE_X8,
.devtypes = CFI_DEVICETYPE_X16,
.uaddr = MTD_UADDR_UNNECESSARY,
.dev_size = SIZE_4MiB,
.cmd_set = P_ID_INTEL_STD,
.nr_regions = 1,
.dev_size = SIZE_8MiB,
.cmd_set = P_ID_INTEL_EXT,
.nr_regions = 2,
.regions = {
ERASEINFO(0x40000,16),
ERASEINFO(0x10000, 127),
ERASEINFO(0x02000, 8),
}
}, {
.mfr_id = CFI_MFR_SST,

View File

@ -224,59 +224,4 @@ config BCH_CONST_T
default 4
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

View File

@ -1,6 +1,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/platform_device.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 };
/**************************************************
* 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,
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;
}
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)
{
struct mtd_info *mtd = &b47s->mtd;
@ -35,33 +244,48 @@ static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s)
mtd->priv = b47s;
mtd->name = "bcm47xxsflash";
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->flags = MTD_CAP_ROM;
mtd->writebufsize = mtd->writesize = 1;
mtd->type = MTD_NORFLASH;
mtd->flags = MTD_CAP_NORFLASH;
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
**************************************************/
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)
{
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
struct bcm47xxsflash *b47s;
int err;
b47s = kzalloc(sizeof(*b47s), GFP_KERNEL);
if (!b47s) {
err = -ENOMEM;
goto out;
}
b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL);
if (!b47s)
return -ENOMEM;
sflash->priv = b47s;
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) {
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);
if (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:
kfree(&b47s->mtd);
out:
return err;
return 0;
}
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;
mtd_device_unregister(&b47s->mtd);
kfree(b47s);
return 0;
}
@ -116,22 +337,4 @@ static struct platform_driver bcma_sflash_driver = {
* Init
**************************************************/
static int __init bcm47xxsflash_init(void)
{
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);
module_platform_driver(bcma_sflash_driver);

View File

@ -60,6 +60,8 @@ enum bcm47xxsflash_type {
struct bcm47xxsflash {
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;

View File

@ -6,6 +6,9 @@
*
* Licence: GPL
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
@ -18,10 +21,6 @@
#include <linux/mount.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 */
struct block2mtd_dev {
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);
mutex_unlock(&dev->write_mutex);
if (err) {
ERROR("erase failed err = %d", err);
pr_err("erase failed err = %d\n", err);
instr->state = MTD_ERASE_FAILED;
} else
instr->state = MTD_ERASE_DONE;
@ -239,13 +238,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
#endif
if (IS_ERR(bdev)) {
ERROR("error: cannot open device %s", devname);
pr_err("error: cannot open device %s\n", devname);
goto devinit_err;
}
dev->blkdev = bdev;
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;
}
@ -277,9 +276,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
goto devinit_err;
}
list_add(&dev->list, &blkmtd_device_list);
INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index,
dev->mtd.name + strlen("block2mtd: "),
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
dev->mtd.index,
dev->mtd.name + strlen("block2mtd: "),
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
return dev;
devinit_err:
@ -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
static int block2mtd_init_called = 0;
static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
#endif
static int block2mtd_setup2(const char *val)
{
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;
int i, ret;
if (strnlen(val, sizeof(buf)) >= sizeof(buf))
parse_err("parameter too long");
if (strnlen(val, sizeof(buf)) >= sizeof(buf)) {
pr_err("parameter too long\n");
return 0;
}
strcpy(str, val);
kill_final_newline(str);
@ -368,20 +364,27 @@ static int block2mtd_setup2(const char *val)
for (i = 0; i < 2; i++)
token[i] = strsep(&str, ",");
if (str)
parse_err("too many arguments");
if (str) {
pr_err("too many arguments\n");
return 0;
}
if (!token[0])
parse_err("no argument");
if (!token[0]) {
pr_err("no argument\n");
return 0;
}
name = token[0];
if (strlen(name) + 1 > 80)
parse_err("device name too long");
if (strlen(name) + 1 > 80) {
pr_err("device name too long\n");
return 0;
}
if (token[1]) {
ret = parse_num(&erase_size, token[1]);
if (ret) {
parse_err("illegal erase size");
pr_err("illegal erase size\n");
return 0;
}
}
@ -444,8 +447,9 @@ static void block2mtd_exit(void)
struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
block2mtd_sync(&dev->mtd);
mtd_device_unregister(&dev->mtd);
INFO("mtd%d: [%s] removed", dev->mtd.index,
dev->mtd.name + strlen("block2mtd: "));
pr_info("mtd%d: [%s] removed\n",
dev->mtd.index,
dev->mtd.name + strlen("block2mtd: "));
list_del(&dev->list);
block2mtd_free_device(dev);
}

View File

@ -20,14 +20,21 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/sched.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/elm.h>
#define ELM_SYSCONFIG 0x010
#define ELM_IRQSTATUS 0x018
#define ELM_IRQENABLE 0x01c
#define ELM_LOCATION_CONFIG 0x020
#define ELM_PAGE_CTRL 0x080
#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_LOCATION_STATUS 0x800
#define ELM_ERROR_LOCATION_0 0x880
@ -56,12 +63,27 @@
#define SYNDROME_FRAGMENT_REG_SIZE 0x40
#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 device *dev;
void __iomem *elm_base;
struct completion elm_completion;
struct list_head list;
enum bch_ecc bch_type;
struct elm_registers elm_regs;
};
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);
if (!res) {
dev_err(&pdev->dev, "no memory resource defined\n");
return -ENODEV;
}
info->elm_base = devm_request_and_ioremap(&pdev->dev, res);
if (!info->elm_base)
return -EADDRNOTAVAIL;
info->elm_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(info->elm_base))
return PTR_ERR(info->elm_base);
ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
pdev->name, info);
@ -381,10 +398,103 @@ static int elm_remove(struct platform_device *pdev)
{
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
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
static const struct of_device_id elm_of_match[] = {
{ .compatible = "ti,am3352-elm" },
@ -398,6 +508,7 @@ static struct platform_driver elm_driver = {
.name = "elm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(elm_of_match),
.pm = &elm_pm_ops,
},
.probe = elm_probe,
.remove = elm_remove,

View File

@ -43,17 +43,24 @@
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
#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_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
#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. */
#define OPCODE_BP 0x02 /* Byte program */
#define OPCODE_WRDI 0x04 /* Write disable */
#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_EX4B 0xe9 /* Exit 4-byte mode */
@ -84,6 +91,8 @@ struct m25p {
u16 page_size;
u16 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 program_opcode;
u8 *command;
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)) {
case CFI_MFR_MACRONIX:
case CFI_MFR_ST: /* Micron, actually */
case 0xEF /* winbond */:
flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
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. */
opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ;
opcode = flash->read_opcode;
flash->command[0] = opcode;
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);
/* 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);
page_offset = to & (flash->page_size - 1);
@ -682,6 +692,8 @@ struct flash_info {
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
#define M25P_NO_ERASE 0x02 /* No erase command needed */
#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) \
@ -694,13 +706,13 @@ struct flash_info {
.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) { \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = (_page_size), \
.addr_width = (_addr_width), \
.flags = M25P_NO_ERASE, \
.flags = (_flags), \
})
/* 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) },
/* 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 */
{ "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) },
{ "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
* 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) },
{ "w25x64", INFO(0xef3017, 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) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
/* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
{ "cat25c03", CAT25_INFO( 32, 8, 16, 2) },
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2) },
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2) },
{ "cat25128", CAT25_INFO(2048, 8, 64, 2) },
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ },
};
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
* 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) {
const struct spi_device_id *plat_id;
@ -972,7 +991,7 @@ static int m25p_probe(struct spi_device *spi)
flash->spi = spi;
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
@ -1014,6 +1033,9 @@ static int m25p_probe(struct spi_device *spi)
if (info->flags & SECT_4K) {
flash->erase_opcode = OPCODE_BE_4K;
flash->mtd.erasesize = 4096;
} else if (info->flags & SECT_4K_PMC) {
flash->erase_opcode = OPCODE_BE_4K_PMC;
flash->mtd.erasesize = 4096;
} else {
flash->erase_opcode = OPCODE_SE;
flash->mtd.erasesize = info->sector_size;
@ -1028,24 +1050,41 @@ static int m25p_probe(struct spi_device *spi)
flash->mtd.writebufsize = flash->page_size;
flash->fast_read = false;
#ifdef CONFIG_OF
if (np && of_property_read_bool(np, "m25p,fast-read"))
flash->fast_read = true;
#endif
#ifdef CONFIG_M25PXX_USE_FAST_READ
flash->fast_read = true;
#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)
flash->addr_width = info->addr_width;
else {
else if (flash->mtd.size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
if (flash->mtd.size > 0x1000000) {
flash->addr_width = 4;
set_4byte(flash, info->jedec_id, 1);
flash->addr_width = 4;
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
flash->addr_width = 3;
set_4byte(flash, info->jedec_id, 1);
} else {
flash->addr_width = 3;
}
dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
@ -1080,7 +1119,7 @@ static int m25p_probe(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;
/* Clean up MTD stuff. */

View File

@ -622,7 +622,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
struct dataflash *priv;
struct mtd_info *device;
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 = "";
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",
name, (long long)((device->size + 1023) >> 10),
pagesize, otp_tag);
dev_set_drvdata(&spi->dev, priv);
spi_set_drvdata(spi, priv);
ppdata.of_node = spi->dev.of_node;
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)
return 0;
dev_set_drvdata(&spi->dev, NULL);
spi_set_drvdata(spi, NULL);
kfree(priv);
return err;
}
@ -895,14 +895,14 @@ static int dataflash_probe(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;
pr_debug("%s: remove\n", dev_name(&spi->dev));
status = mtd_device_unregister(&flash->mtd);
if (status == 0) {
dev_set_drvdata(&spi->dev, NULL);
spi_set_drvdata(spi, NULL);
kfree(flash);
}
return status;

View File

@ -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_smi *dev = mtd->priv;
void *src;
void __iomem *src;
u32 ctrlreg1, val;
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);
memcpy_fromio(buf, (u8 *)src, len);
memcpy_fromio(buf, src, len);
/* restore ctrl reg1 */
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,
void *dest, const void *src, size_t len)
void __iomem *dest, const void *src, size_t len)
{
int ret;
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_smi *dev = mtd->priv;
void *dest;
void __iomem *dest;
u32 page_offset, page_size;
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]);
if (ret) {
dev_err(&dev->pdev->dev, "bank setup failed\n");
goto err_bank_setup;
goto err_irq;
}
}
return 0;
err_bank_setup:
platform_set_drvdata(pdev, NULL);
err_irq:
clk_disable_unprepare(dev->clk);
err:
@ -1040,12 +1038,11 @@ static int spear_smi_remove(struct platform_device *pdev)
}
clk_disable_unprepare(dev->clk);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int spear_smi_suspend(struct device *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);
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume);
#endif
#ifdef CONFIG_OF
static const struct of_device_id spear_smi_id_table[] = {
@ -1086,9 +1083,7 @@ static struct platform_driver spear_smi_driver = {
.bus = &platform_bus_type,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(spear_smi_id_table),
#ifdef CONFIG_PM
.pm = &spear_smi_pm_ops,
#endif
},
.probe = spear_smi_probe,
.remove = spear_smi_remove,

View File

@ -370,9 +370,9 @@ static int sst25l_probe(struct spi_device *spi)
flash->spi = spi;
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)
flash->mtd.name = data->name;
else
@ -404,7 +404,7 @@ static int sst25l_probe(struct spi_device *spi)
data ? data->nr_parts : 0);
if (ret) {
kfree(flash);
dev_set_drvdata(&spi->dev, NULL);
spi_set_drvdata(spi, NULL);
return -ENODEV;
}
@ -413,7 +413,7 @@ static int sst25l_probe(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;
ret = mtd_device_unregister(&flash->mtd);

View File

@ -157,24 +157,6 @@ config MTD_PXA2XX
help
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
tristate "Flash device mapped with DOCCS on NatSemi SCx200"
depends on SCx200 && MTD_CFI

View File

@ -16,7 +16,6 @@ obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_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_OF) += physmap_of.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_TS5500) += ts5500_flash.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_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o

View File

@ -128,7 +128,7 @@ static const char * const part_probe_types[] = {
static int bfin_flash_probe(struct platform_device *pdev)
{
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 *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
struct async_state *state;

View File

@ -55,13 +55,13 @@
#define FLASH_PARTITION3_SIZE 0x001C0000
struct map_info flagadm_map = {
static struct map_info flagadm_map = {
.name = "FlagaDM flash device",
.size = FLASH_SIZE,
.bankwidth = 2,
};
struct mtd_partition flagadm_parts[] = {
static struct mtd_partition flagadm_parts[] = {
{
.name = "Bootloader",
.offset = FLASH_PARTITION0_ADDR,
@ -112,7 +112,7 @@ static int __init init_flagadm(void)
return 0;
}
iounmap((void *)flagadm_map.virt);
iounmap((void __iomem *)flagadm_map.virt);
return -ENXIO;
}
@ -123,8 +123,8 @@ static void __exit cleanup_flagadm(void)
map_destroy(mymtd);
}
if (flagadm_map.virt) {
iounmap((void *)flagadm_map.virt);
flagadm_map.virt = 0;
iounmap((void __iomem *)flagadm_map.virt);
flagadm_map.virt = NULL;
}
}

View File

@ -196,7 +196,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
struct resource *gpios;
struct async_state *state;
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

View File

@ -79,7 +79,7 @@ static int __init init_impa7(void)
}
simple_map_init(&impa7_map[i]);
impa7_mtd[i] = 0;
impa7_mtd[i] = NULL;
type = rom_probe_types;
for(; !impa7_mtd[i] && *type; type++) {
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,
partitions,
ARRAY_SIZE(partitions));
} else {
iounmap((void __iomem *)impa7_map[i].virt);
}
else
iounmap((void *)impa7_map[i].virt);
}
return devicesfound == 0 ? -ENXIO : 0;
}
@ -105,8 +105,8 @@ static void __exit cleanup_impa7(void)
if (impa7_mtd[i]) {
mtd_device_unregister(impa7_mtd[i]);
map_destroy(impa7_mtd[i]);
iounmap((void *)impa7_map[i].virt);
impa7_map[i].virt = 0;
iounmap((void __iomem *)impa7_map[i].virt);
impa7_map[i].virt = NULL;
}
}
}

View File

@ -152,11 +152,9 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
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);
platform_set_drvdata(dev, NULL);
if(!info)
return 0;
@ -180,7 +178,7 @@ static int ixp4xx_flash_remove(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 mtd_part_parser_data ppdata = {
.origin = dev->resource->start,

View File

@ -102,9 +102,8 @@ static int latch_addr_flash_remove(struct platform_device *dev)
info = platform_get_drvdata(dev);
if (info == NULL)
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) {
mtd_device_unregister(info->mtd);
@ -135,7 +134,7 @@ static int latch_addr_flash_probe(struct platform_device *dev)
int chipsel;
int err;
latch_addr_data = dev->dev.platform_data;
latch_addr_data = dev_get_platdata(&dev->dev);
if (latch_addr_data == NULL)
return -ENODEV;

View File

@ -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");

View File

@ -40,9 +40,8 @@ static int physmap_flash_remove(struct platform_device *dev)
info = platform_get_drvdata(dev);
if (info == NULL)
return 0;
platform_set_drvdata(dev, NULL);
physmap_data = dev->dev.platform_data;
physmap_data = dev_get_platdata(&dev->dev);
if (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;
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)
return;
@ -103,7 +102,7 @@ static int physmap_flash_probe(struct platform_device *dev)
int i;
int devices_found = 0;
physmap_data = dev->dev.platform_data;
physmap_data = dev_get_platdata(&dev->dev);
if (physmap_data == NULL)
return -ENODEV;

View File

@ -84,8 +84,6 @@ static int platram_remove(struct platform_device *pdev)
{
struct platram_info *info = to_platram_info(pdev);
platform_set_drvdata(pdev, NULL);
dev_dbg(&pdev->dev, "removing device\n");
if (info == NULL)
@ -130,13 +128,13 @@ static int platram_probe(struct platform_device *pdev)
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");
err = -ENOENT;
goto exit_error;
}
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {

View File

@ -49,7 +49,7 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
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 resource *res;
@ -107,8 +107,6 @@ static int pxa2xx_flash_remove(struct platform_device *dev)
{
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);

View File

@ -34,10 +34,9 @@ static int rbtx4939_flash_remove(struct platform_device *dev)
info = platform_get_drvdata(dev);
if (!info)
return 0;
platform_set_drvdata(dev, NULL);
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);
map_destroy(info->mtd);
@ -57,7 +56,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
int err = 0;
unsigned long size;
pdata = dev->dev.platform_data;
pdata = dev_get_platdata(&dev->dev);
if (!pdata)
return -ENODEV;

View File

@ -248,7 +248,7 @@ static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL };
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;
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)
{
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);
return 0;

View File

@ -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");

View File

@ -285,6 +285,16 @@ static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR,
mtd_bitflip_threshold_show,
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[] = {
&dev_attr_type.attr,
&dev_attr_flags.attr,
@ -296,6 +306,7 @@ static struct attribute *mtd_attrs[] = {
&dev_attr_numeraseregions.attr,
&dev_attr_name.attr,
&dev_attr_ecc_strength.attr,
&dev_attr_ecc_step_size.attr,
&dev_attr_bitflip_threshold.attr,
NULL,
};

View File

@ -516,6 +516,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
}
slave->mtd.ecclayout = master->ecclayout;
slave->mtd.ecc_step_size = master->ecc_step_size;
slave->mtd.ecc_strength = master->ecc_strength;
slave->mtd.bitflip_threshold = master->bitflip_threshold;

View File

@ -1425,7 +1425,7 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
return;
while ((this_opt = strsep(&parts, ",")) != NULL) {
if (strict_strtoul(this_opt, 0, &part) < 0)
if (kstrtoul(this_opt, 0, &part) < 0)
return;
if (mtd->index == part)

View File

@ -43,6 +43,7 @@ config MTD_SM_COMMON
config MTD_NAND_DENALI
tristate "Support Denali NAND controller"
depends on HAS_DMA
help
Enable support for the Denali NAND controller. This should be
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
tristate "GPIO NAND Flash driver"
depends on GPIOLIB && ARM
depends on GPIOLIB
help
This enables a GPIO based NAND flash driver.
@ -354,7 +355,7 @@ config MTD_NAND_ATMEL
config MTD_NAND_PXA3xx
tristate "Support for NAND flash devices on PXA3xx"
depends on PXA3xx || ARCH_MMP
depends on PXA3xx || ARCH_MMP || PLAT_ORION
help
This enables the driver for the NAND flash device found on
PXA3xx processors
@ -432,13 +433,6 @@ config MTD_NAND_PLATFORM
devices. You will need to provide platform-specific functions
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
tristate "NAND Flash support for Marvell Orion SoC"
depends on PLAT_ORION

View File

@ -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_TMIO) += tmio_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_ORION) += orion_nand.o
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o

View File

@ -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");

View File

@ -258,7 +258,6 @@ static int ams_delta_init(struct platform_device *pdev)
out_mtd:
gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
out_gpio:
platform_set_drvdata(pdev, NULL);
gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
iounmap(io_base);
out_free:

File diff suppressed because it is too large Load Diff

View 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

View File

@ -411,7 +411,7 @@ static int au1550nd_probe(struct platform_device *pdev)
struct resource *r;
int ret, cs;
pd = pdev->dev.platform_data;
pd = dev_get_platdata(&pdev->dev);
if (!pd) {
dev_err(&pdev->dev, "missing platform data\n");
return -ENODEV;

View File

@ -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)
{
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);
platform_set_drvdata(pdev, NULL);
/* first thing we need to do is release all our mtds
* and their partitions, then go through freeing the
* resources used
@ -832,7 +830,6 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
out_err_nand_scan:
bf5xx_nand_dma_remove(info);
out_err_hw_init:
platform_set_drvdata(pdev, NULL);
kfree(info);
out_err_kzalloc:
peripheral_free_list(bfin_nfc_pin_req);

View File

@ -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 */
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) {
printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n");
err = -ENOMEM;
@ -207,10 +207,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
/* Get pointer to private data */
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 */
new_mtd->priv = this;
new_mtd->owner = THIS_MODULE;

View File

@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, davinci_nand_of_match);
static struct davinci_nand_pdata
*nand_davinci_get_pdata(struct platform_device *pdev)
{
if (!pdev->dev.platform_data && pdev->dev.of_node) {
if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) {
struct davinci_nand_pdata *pdata;
const char *mode;
u32 prop;
@ -575,13 +575,13 @@ static struct davinci_nand_pdata
pdata->bbt_options = NAND_BBT_USE_FLASH;
}
return pdev->dev.platform_data;
return dev_get_platdata(&pdev->dev);
}
#else
static struct davinci_nand_pdata
*nand_davinci_get_pdata(struct platform_device *pdev)
{
return pdev->dev.platform_data;
return dev_get_platdata(&pdev->dev);
}
#endif
@ -623,11 +623,14 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
goto err_nomem;
}
vaddr = devm_request_and_ioremap(&pdev->dev, res1);
base = devm_request_and_ioremap(&pdev->dev, res2);
if (!vaddr || !base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -EADDRNOTAVAIL;
vaddr = devm_ioremap_resource(&pdev->dev, res1);
if (IS_ERR(vaddr)) {
ret = PTR_ERR(vaddr);
goto err_ioremap;
}
base = devm_ioremap_resource(&pdev->dev, res2);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
goto err_ioremap;
}

View File

@ -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
* SLC if possible.
* */
if (denali->nand.cellinfo & 0xc &&
if (denali->nand.cellinfo & NAND_CI_CELLTYPE_MSK &&
(denali->mtd.oobsize > (denali->bbtskipbytes +
ECC_15BITS * (denali->mtd.writesize /
ECC_SECTOR_SIZE)))) {

View File

@ -46,13 +46,13 @@ static unsigned long __initdata doc_locations[] = {
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
#else /* CONFIG_MTD_DOCPROBE_HIGH */
#else
0xc8000, 0xca000, 0xcc000, 0xce000,
0xd0000, 0xd2000, 0xd4000, 0xd6000,
0xd8000, 0xda000, 0xdc000, 0xde000,
0xe0000, 0xe2000, 0xe4000, 0xe6000,
0xe8000, 0xea000, 0xec000, 0xee000,
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
#endif
#endif
0xffffffff };

View File

@ -1093,7 +1093,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
struct nand_chip *nand = mtd->priv;
struct docg4_priv *doc = nand->priv;
struct nand_bbt_descr *bbtd = nand->badblock_pattern;
int block = (int)(ofs >> nand->bbt_erase_shift);
int page = (int)(ofs >> nand->page_shift);
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)
return -ENOMEM;
/* update bbt in memory */
nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2);
/* write bit-wise negation of pattern to oob buffer */
memset(nand->oob_poi, 0xff, mtd->oobsize);
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);
docg4_write_page(mtd, nand, buf, 1);
ret = pageprog(mtd);
if (!ret)
mtd->ecc_stats.badblocks++;
kfree(buf);
@ -1368,7 +1362,6 @@ static int __init probe_docg4(struct platform_device *pdev)
struct nand_chip *nand = mtd->priv;
struct docg4_priv *doc = nand->priv;
nand_release(mtd); /* deletes partitions and mtd devices */
platform_set_drvdata(pdev, NULL);
free_bch(doc->bch);
kfree(mtd);
}
@ -1380,7 +1373,6 @@ static int __exit cleanup_docg4(struct platform_device *pdev)
{
struct docg4_priv *doc = platform_get_drvdata(pdev);
nand_release(doc->mtd);
platform_set_drvdata(pdev, NULL);
free_bch(doc->bch);
kfree(doc->mtd);
iounmap(doc->virtadr);

View File

@ -823,7 +823,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
/* set up nand options */
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) {
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;
dev_set_drvdata(priv->dev, NULL);
kfree(priv);
return 0;
}

View File

@ -889,6 +889,24 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
if (of_get_property(np, "nand-skip-bbtscan", NULL))
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;
}
#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");
if (!res)
return -EINVAL;
host->data_va = devm_ioremap_resource(&pdev->dev, res);
if (IS_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;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
if (!res)
return -EINVAL;
host->addr_va = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->addr_va))
return PTR_ERR(host->addr_va);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
if (!res)
return -EINVAL;
host->cmd_va = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->cmd_va))
return PTR_ERR(host->cmd_va);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
if (!res)
return -EINVAL;
host->regs_va = devm_ioremap_resource(&pdev->dev, res);
if (IS_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);
platform_set_drvdata(pdev, NULL);
if (host) {
nand_release(&host->mtd);
@ -1190,7 +1194,7 @@ static int fsmc_nand_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int fsmc_nand_suspend(struct device *dev)
{
struct fsmc_nand_data *host = dev_get_drvdata(dev);
@ -1210,9 +1214,9 @@ static int fsmc_nand_resume(struct device *dev)
}
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
#endif
#ifdef CONFIG_OF
static const struct of_device_id fsmc_nand_id_table[] = {
@ -1229,9 +1233,7 @@ static struct platform_driver fsmc_nand_driver = {
.owner = THIS_MODULE,
.name = "fsmc-nand",
.of_match_table = of_match_ptr(fsmc_nand_id_table),
#ifdef CONFIG_PM
.pm = &fsmc_nand_pm_ops,
#endif
},
};

View File

@ -17,6 +17,7 @@
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.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);
}
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)
{
struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd);
if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
return gpio_get_value(gpiomtd->plat.gpio_rdy);
return 1;
return gpio_get_value(gpiomtd->plat.gpio_rdy);
}
#ifdef CONFIG_OF
@ -153,6 +106,9 @@ static int gpio_nand_get_config_of(const struct device *dev,
{
u32 val;
if (!dev->of_node)
return -ENODEV;
if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {
if (val == 2) {
plat->options |= NAND_BUSWIDTH_16;
@ -211,8 +167,8 @@ static inline int gpio_nand_get_config(const struct device *dev,
if (!ret)
return ret;
if (dev->platform_data) {
memcpy(plat, dev->platform_data, sizeof(*plat));
if (dev_get_platdata(dev)) {
memcpy(plat, dev_get_platdata(dev), sizeof(*plat));
return 0;
}
@ -230,145 +186,100 @@ gpio_nand_get_io_sync(struct platform_device *pdev)
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 resource *res;
struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);
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))
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
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;
}
static void __iomem *request_and_remap(struct resource *res, size_t size,
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)
static int gpio_nand_probe(struct platform_device *pdev)
{
struct gpiomtd *gpiomtd;
struct nand_chip *this;
struct resource *res0, *res1;
struct nand_chip *chip;
struct resource *res;
struct mtd_part_parser_data ppdata = {};
int ret = 0;
if (!dev->dev.of_node && !dev->dev.platform_data)
if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev))
return -EINVAL;
res0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res0)
return -EINVAL;
gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL);
if (gpiomtd == NULL) {
dev_err(&dev->dev, "failed to create NAND MTD\n");
gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL);
if (!gpiomtd) {
dev_err(&pdev->dev, "failed to create NAND MTD\n");
return -ENOMEM;
}
this = &gpiomtd->nand_chip;
this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret);
if (!this->IO_ADDR_R) {
dev_err(&dev->dev, "unable to map NAND\n");
goto err_map;
chip = &gpiomtd->nand_chip;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
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);
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);
ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat);
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)
goto err_nce;
return ret;
gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
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)
goto err_nwp;
gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
return ret;
}
ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE");
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE");
if (ret)
goto err_ale;
return ret;
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)
goto err_cle;
return ret;
gpio_direction_output(gpiomtd->plat.gpio_cle, 0);
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)
goto err_rdy;
return ret;
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;
this->ecc.mode = NAND_ECC_SOFT;
this->options = gpiomtd->plat.options;
this->chip_delay = gpiomtd->plat.chip_delay;
gpiomtd->mtd_info.priv = chip;
gpiomtd->mtd_info.owner = THIS_MODULE;
/* install our routines */
this->cmd_ctrl = gpio_nand_cmd_ctrl;
this->dev_ready = gpio_nand_devready;
platform_set_drvdata(pdev, gpiomtd);
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;
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
if (nand_scan(&gpiomtd->mtd_info, 1)) {
dev_err(&dev->dev, "no nand chips found?\n");
ret = -ENXIO;
goto err_wp;
}
@ -377,38 +288,17 @@ static int gpio_nand_probe(struct platform_device *dev)
gpiomtd->plat.adjust_parts(&gpiomtd->plat,
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,
gpiomtd->plat.parts,
gpiomtd->plat.num_parts);
if (ret)
goto err_wp;
platform_set_drvdata(dev, gpiomtd);
return 0;
if (!ret)
return 0;
err_wp:
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
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;
}
@ -417,6 +307,7 @@ static struct platform_driver gpio_nand_driver = {
.remove = gpio_nand_remove,
.driver = {
.name = "gpio-nand",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_nand_id_table),
},
};

View File

@ -26,7 +26,6 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mtd/partitions.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
@ -112,7 +111,131 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
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 mtd_info *mtd = &this->mtd;
@ -224,6 +347,11 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
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)
{
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);
if (!r) {
pr_err("Can't get resource for %s\n", res_name);
return -ENXIO;
return -ENODEV;
}
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);
if (!r) {
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);
@ -473,12 +601,14 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
struct resources *r = &this->resources;
char **extra_clks = NULL;
struct clk *clk;
int i;
int err, i;
/* The main clock is stored in the first. */
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;
}
/* Get extra clocks */
if (GPMI_IS_MX6Q(this))
@ -491,8 +621,10 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
break;
clk = clk_get(this->dev, extra_clks[i - 1]);
if (IS_ERR(clk))
if (IS_ERR(clk)) {
err = PTR_ERR(clk);
goto err_clock;
}
r->clock[i] = clk;
}
@ -511,12 +643,11 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
err_clock:
dev_dbg(this->dev, "failed in finding the clocks.\n");
gpmi_put_clks(this);
return -ENOMEM;
return err;
}
static int acquire_resources(struct gpmi_nand_data *this)
{
struct pinctrl *pinctrl;
int ret;
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)
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);
if (ret)
goto exit_clock;
return 0;
exit_clock:
exit_pin:
release_dma_channels(this);
exit_dma_channels:
release_bch_irq(this);
@ -1153,43 +1277,31 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
struct gpmi_nand_data *this = chip->priv;
int block, ret = 0;
int ret = 0;
uint8_t *block_mark;
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);
chipnr = (int)(ofs >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
/* 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);
chip->select_chip(mtd, chipnr);
column = this->swap_block_mark ? mtd->writesize : 0;
column = this->swap_block_mark ? mtd->writesize : 0;
/* Write the block mark. */
block_mark = this->data_buffer_dma;
block_mark[0] = 0; /* bad block marker */
/* Write the block mark. */
block_mark = this->data_buffer_dma;
block_mark[0] = 0; /* bad block marker */
/* Shift to get page */
page = (int)(ofs >> chip->page_shift);
/* Shift to get page */
page = (int)(ofs >> chip->page_shift);
chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
chip->write_buf(mtd, block_mark, 1);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
chip->write_buf(mtd, block_mark, 1);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL)
ret = -EIO;
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL)
ret = -EIO;
chip->select_chip(mtd, -1);
}
if (!ret)
mtd->ecc_stats.badblocks++;
chip->select_chip(mtd, -1);
return ret;
}
@ -1469,19 +1581,22 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this)
if (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(). */
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 gpmi_nand_data *this = chip->priv;
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct bch_geometry *bch_geo = &this->bch_geometry;
int ret;
/* Prepare for the BBT scan. */
@ -1489,6 +1604,16 @@ static int gpmi_scan_bbt(struct mtd_info *mtd)
if (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.
*
@ -1497,14 +1622,7 @@ static int gpmi_scan_bbt(struct mtd_info *mtd)
*/
gpmi_extra_init(this);
/* use the default BBT implementation */
return nand_default_bbt(mtd);
}
static void gpmi_nfc_exit(struct gpmi_nand_data *this)
{
nand_release(&this->mtd);
gpmi_free_dma_buffer(this);
return 0;
}
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_buf = gpmi_read_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->block_markbad = gpmi_block_markbad;
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))
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.auxiliary_size = 128;
ret = gpmi_alloc_dma_buffer(this);
if (ret)
goto err_out;
ret = nand_scan(mtd, 1);
if (ret) {
pr_err("Chip scan failed\n");
ret = nand_scan_ident(mtd, 1, NULL);
if (ret)
goto err_out;
ret = gpmi_init_last(this);
if (ret)
goto err_out;
ret = nand_scan_tail(mtd);
if (ret)
goto err_out;
}
ppdata.of_node = this->pdev->dev.of_node;
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;
} else {
pr_err("Failed to find the right device id.\n");
return -ENOMEM;
return -ENODEV;
}
this = kzalloc(sizeof(*this), GFP_KERNEL);
@ -1633,7 +1751,6 @@ static int gpmi_nand_probe(struct platform_device *pdev)
exit_nfc_init:
release_resources(this);
exit_acquire_resources:
platform_set_drvdata(pdev, NULL);
dev_err(this->dev, "driver registration failed: %d\n", ret);
kfree(this);
@ -1646,7 +1763,6 @@ static int gpmi_nand_remove(struct platform_device *pdev)
gpmi_nfc_exit(this);
release_resources(this);
platform_set_drvdata(pdev, NULL);
kfree(this);
return 0;
}

View File

@ -411,7 +411,7 @@ static int jz_nand_probe(struct platform_device *pdev)
struct jz_nand *nand;
struct nand_chip *chip;
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;
uint8_t nand_maf_id = 0, nand_dev_id = 0;
@ -538,7 +538,6 @@ err_unclaim_banks:
err_gpio_busy:
if (pdata && gpio_is_valid(pdata->busy_gpio))
gpio_free(pdata->busy_gpio);
platform_set_drvdata(pdev, NULL);
err_iounmap_mmio:
jz_nand_iounmap_resource(nand->mem, nand->base);
err_free:
@ -549,7 +548,7 @@ err_free:
static int jz_nand_remove(struct platform_device *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;
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);
platform_set_drvdata(pdev, NULL);
kfree(nand);
return 0;

View File

@ -696,7 +696,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
}
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 */
mtd->priv = nand_chip;
@ -828,7 +828,6 @@ err_exit3:
err_exit2:
clk_disable(host->clk);
clk_put(host->clk);
platform_set_drvdata(pdev, NULL);
err_exit1:
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);
@ -851,7 +850,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
clk_disable(host->clk);
clk_put(host->clk);
platform_set_drvdata(pdev, NULL);
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);

View File

@ -798,7 +798,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
}
lpc32xx_wp_disable(host);
host->pdata = pdev->dev.platform_data;
host->pdata = dev_get_platdata(&pdev->dev);
mtd = &host->mtd;
chip = &host->nand_chip;
@ -936,7 +936,6 @@ err_exit3:
err_exit2:
clk_disable(host->clk);
clk_put(host->clk);
platform_set_drvdata(pdev, NULL);
err_exit1:
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);
@ -963,7 +962,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
clk_disable(host->clk);
clk_put(host->clk);
platform_set_drvdata(pdev, NULL);
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);

View File

@ -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 };
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);
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) {
host->pdata = *pdata;
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) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
if (IS_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);
}
if (!res)
return -ENODEV;
host->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_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);
platform_set_drvdata(pdev, NULL);
nand_release(&host->mtd);
return 0;

View File

@ -108,13 +108,13 @@ static int check_offs_len(struct mtd_info *mtd,
int ret = 0;
/* 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__);
ret = -EINVAL;
}
/* 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__);
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)
{
int i;
struct nand_chip *chip = mtd->priv;
for (i = 0; i < len; i++)
writeb(buf[i], chip->IO_ADDR_W);
iowrite8_rep(chip->IO_ADDR_W, buf, len);
}
/**
@ -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)
{
int i;
struct nand_chip *chip = mtd->priv;
for (i = 0; i < len; i++)
buf[i] = readb(chip->IO_ADDR_R);
ioread8_rep(chip->IO_ADDR_R, buf, len);
}
/**
@ -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)
{
int i;
struct nand_chip *chip = mtd->priv;
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)
{
int i;
struct nand_chip *chip = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i = 0; i < len; i++)
p[i] = readw(chip->IO_ADDR_R);
ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
}
/**
@ -335,80 +324,88 @@ 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
* @ofs: offset from device start
*
* This is the default implementation, which can be overridden by a hardware
* specific driver. We try operations in the following order, according to our
* bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
* (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.
*/
* specific driver. It provides the details for writing a bad block marker to a
* block.
*/
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
struct mtd_oob_ops ops;
uint8_t buf[2] = { 0, 0 };
int block, res, ret = 0, i = 0;
int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
int ret = 0, res, i = 0;
if (write_oob) {
ops.datbuf = NULL;
ops.oobbuf = buf;
ops.ooboffs = chip->badblockpos;
if (chip->options & NAND_BUSWIDTH_16) {
ops.ooboffs &= ~0x01;
ops.len = ops.ooblen = 2;
} else {
ops.len = ops.ooblen = 1;
}
ops.mode = MTD_OPS_PLACE_OOB;
/* Write to first/last page(s) if necessary */
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
ofs += mtd->erasesize - mtd->writesize;
do {
res = nand_do_write_oob(mtd, ofs, &ops);
if (!ret)
ret = res;
i++;
ofs += mtd->writesize;
} 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 = 1 << chip->phys_erase_shift;
einfo.len = 1ULL << 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;
loff_t wr_ofs = ofs;
/* Write bad block marker to OOB */
nand_get_device(mtd, FL_WRITING);
ops.datbuf = NULL;
ops.oobbuf = buf;
ops.ooboffs = chip->badblockpos;
if (chip->options & NAND_BUSWIDTH_16) {
ops.ooboffs &= ~0x01;
ops.len = ops.ooblen = 2;
} else {
ops.len = ops.ooblen = 1;
}
ops.mode = MTD_OPS_PLACE_OOB;
/* Write to first/last page(s) if necessary */
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
wr_ofs += mtd->erasesize - mtd->writesize;
do {
res = nand_do_write_oob(mtd, wr_ofs, &ops);
if (!ret)
ret = res;
i++;
wr_ofs += mtd->writesize;
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
ret = chip->block_markbad(mtd, ofs);
nand_release_device(mtd);
}
/* Update flash-based bad block table */
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
res = nand_update_bbt(mtd, ofs);
/* Mark block bad in BBT */
if (chip->bbt) {
res = nand_markbad_bbt(mtd, ofs);
if (!ret)
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
* @mtd: mtd 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
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
*/
static int nand_write_subpage_hwecc(struct mtd_info *mtd,
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)
{
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);
/* 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 */
if ((step < start_step) || (step > end_step))
memset(ecc_calc, 0xff, ecc_bytes);
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 */
/* if oob_required, preserve OOB metadata of written subpage */
if (!oob_required || (step < start_step) || (step > end_step))
memset(oob_buf, 0xff, oob_bytes);
data_buf += ecc_size;
buf += ecc_size;
ecc_calc += ecc_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 */
len -= (1 << chip->phys_erase_shift);
len -= (1ULL << chip->phys_erase_shift);
page += pages_per_block;
/* 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)
{
struct nand_chip *chip = mtd->priv;
int ret;
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 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;
if (!chip->onfi_version)
if (!chip->onfi_version ||
!(le16_to_cpu(chip->onfi_params.opt_cmd)
& ONFI_OPT_CMD_SET_GET_FEATURES))
return -EINVAL;
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,
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;
/* clear the sub feature parameters */
@ -2793,7 +2794,15 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
if (!chip->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;
if (!chip->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;
if (!chip->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;
if (!chip->read_buf)
if (!chip->read_buf || chip->read_buf == nand_read_buf)
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!chip->scan_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;
}
/* 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.
*/
@ -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);
chip->chipsize = le32_to_cpu(p->blocks_per_lun);
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;
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");
return 1;
@ -3086,6 +3189,22 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
extid >>= 2;
/* Get buswidth information */
*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->chipsize = (uint64_t)type->chipsize << 20;
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;
@ -3446,12 +3567,6 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!chip->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
* 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 */
mtd->ecclayout = chip->ecc.layout;
mtd->ecc_strength = chip->ecc.strength;
mtd->ecc_step_size = chip->ecc.size;
/*
* Initialize bitflip_threshold to its default prior scan_bbt() call.
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be

View File

@ -71,6 +71,30 @@
#include <linux/export.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)
{
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
*
* 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
* all bytes except the pattern area contain 0xff.
* good / bad block identifiers.
*/
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)
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 */
if (memcmp(p, td->pattern, td->len))
if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
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;
}
@ -159,7 +167,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
* @page: the starting page
* @num: the number of bbt descriptors to read
* @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.
*/
@ -209,14 +217,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
/* Analyse data */
for (i = 0; i < len; 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;
if (tmp == msk)
continue;
if (reserved_block_code && (tmp == reserved_block_code)) {
pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
(loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
(loff_t)(offs + act) <<
this->bbt_erase_shift);
bbt_mark_entry(this, offs + act,
BBT_BLOCK_RESERVED);
mtd->ecc_stats.bbtblocks++;
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.
*/
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? */
if (tmp == 0)
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
bbt_mark_entry(this, offs + act,
BBT_BLOCK_FACTORY_BAD);
else
this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
bbt_mark_entry(this, offs + act,
BBT_BLOCK_WORN);
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);
if (res)
return res;
offs += this->chipsize >> (this->bbt_erase_shift + 2);
offs += this->chipsize >> this->bbt_erase_shift;
}
} else {
res = read_bbt(mtd, buf, td->pages[0],
@ -478,22 +491,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
else
numpages = 1;
if (!(bd->options & NAND_BBT_SCANEMPTY)) {
/* We need only read few bytes from the OOB area */
scanlen = 0;
readlen = bd->len;
} else {
/* Full page content should be read */
scanlen = mtd->writesize + mtd->oobsize;
readlen = numpages * mtd->writesize;
}
/* We need only read few bytes from the OOB area */
scanlen = 0;
readlen = bd->len;
if (chip == -1) {
/*
* 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);
numblocks = mtd->size >> this->bbt_erase_shift;
startblock = 0;
from = 0;
} else {
@ -502,16 +505,16 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
chip + 1, this->numchips);
return -EINVAL;
}
numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
numblocks = this->chipsize >> this->bbt_erase_shift;
startblock = chip * numblocks;
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)
from += mtd->erasesize - (mtd->writesize * numpages);
for (i = startblock; i < numblocks;) {
for (i = startblock; i < numblocks; i++) {
int ret;
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;
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",
i >> 1, (unsigned long long)from);
i, (unsigned long long)from);
mtd->ecc_stats.badblocks++;
}
i += 2;
from += (1 << this->bbt_erase_shift);
}
return 0;
@ -655,9 +657,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
{
struct nand_chip *this = mtd->priv;
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 nrchips, bbtoffs, pageoffs, ooboffs;
int nrchips, pageoffs, ooboffs;
uint8_t msk[4];
uint8_t rcode = td->reserved_block_code;
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++) {
int block = startblock + dir * i;
/* Check, if the block is bad */
switch ((this->bbt[block >> 2] >>
(2 * (block & 0x03))) & 0x03) {
case 0x01:
case 0x03:
switch (bbt_get_entry(this, block)) {
case BBT_BLOCK_WORN:
case BBT_BLOCK_FACTORY_BAD:
continue;
}
page = block <<
@ -748,8 +749,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
default: return -EINVAL;
}
bbtoffs = chip * (numblocks >> 2);
to = ((loff_t)page) << this->page_shift;
/* 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];
/* Walk through the memory table */
for (i = 0; i < numblocks;) {
for (i = 0; i < numblocks; i++) {
uint8_t dat;
dat = this->bbt[bbtoffs + (i >> 2)];
for (j = 0; j < 4; j++, i++) {
int sftcnt = (i << (3 - sft)) & sftmsk;
/* Do not store the reserved bbt blocks! */
buf[offs + (i >> sft)] &=
~(msk[dat & 0x03] << sftcnt);
dat >>= 2;
}
int sftcnt = (i << (3 - sft)) & sftmsk;
dat = bbt_get_entry(this, chip * numblocks + i);
/* Do not store the reserved bbt blocks! */
buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt);
}
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;
bd->options &= ~NAND_BBT_SCANEMPTY;
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;
int i, j, chips, block, nrblocks, update;
uint8_t oldval, newval;
uint8_t oldval;
/* Do we have a bbt per chip? */
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)
continue;
block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
block <<= 1;
oldval = this->bbt[(block >> 3)];
newval = oldval | (0x2 << (block & 0x06));
this->bbt[(block >> 3)] = newval;
if ((oldval != newval) && td->reserved_block_code)
nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1));
oldval = bbt_get_entry(this, block);
bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
if ((oldval != BBT_BLOCK_RESERVED) &&
td->reserved_block_code)
nand_update_bbt(mtd, (loff_t)block <<
this->bbt_erase_shift);
continue;
}
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;
else
block = i * nrblocks;
block <<= 1;
for (j = 0; j < td->maxblocks; j++) {
oldval = this->bbt[(block >> 3)];
newval = oldval | (0x2 << (block & 0x06));
this->bbt[(block >> 3)] = newval;
if (oldval != newval)
oldval = bbt_get_entry(this, block);
bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
if (oldval != BBT_BLOCK_RESERVED)
update = 1;
block += 2;
block++;
}
/*
* 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.
*/
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
* @offs: the offset of the newly marked block
*
* 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;
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)
{
struct nand_chip *this = mtd->priv;
int block;
uint8_t res;
int block, res;
/* Get block number * 2 */
block = (int)(offs >> (this->bbt_erase_shift - 1));
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
block = (int)(offs >> this->bbt_erase_shift);
res = bbt_get_entry(this, block);
pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: "
"(block %d) 0x%02x\n",
(unsigned int)offs, block >> 1, res);
(unsigned int)offs, block, res);
switch ((int)res) {
case 0x00:
switch (res) {
case BBT_BLOCK_GOOD:
return 0;
case 0x01:
case BBT_BLOCK_WORN:
return 1;
case 0x02:
case BBT_BLOCK_RESERVED:
return allowbbt ? 0 : 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_default_bbt);
EXPORT_SYMBOL_GPL(nand_update_bbt);

View File

@ -33,16 +33,16 @@ struct nand_flash_dev nand_flash_ids[] = {
*/
{"TC58NVG2S0F 4G 3.3V 8-bit",
{ .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",
{ .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",
{ .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",
{ .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 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),

View File

@ -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 */
#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 */
#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 secshift; /* bits number in sector size */
uint pgshift; /* bits number in page size */
uint oobshift; /* bits number in OOB size */
uint pgaddrbytes; /* bytes per page address */
uint secaddrbytes; /* bytes per sector address */
uint idbytes; /* the number ID bytes that this chip outputs */
@ -363,7 +362,7 @@ struct nandsim {
/* Fields needed when using a cache 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;
struct page *held_pages[NS_MAX_HELD_PAGES];
int held_cnt;
@ -586,7 +585,8 @@ static int alloc_device(struct nandsim *ns)
err = -EINVAL;
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) {
NS_ERR("alloc_device: unable to allocate pages written array\n");
err = -ENOMEM;
@ -653,9 +653,7 @@ static void free_device(struct nandsim *ns)
static char *get_partition_name(int i)
{
char buf[64];
sprintf(buf, "NAND simulator partition %d", i);
return kstrdup(buf, GFP_KERNEL);
return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i);
}
/*
@ -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.secshift = ffs(ns->geom.secsz) - 1;
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.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
ns->options = 0;
@ -761,12 +758,6 @@ static int init_nandsim(struct mtd_info *mtd)
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)
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("bits in sector size: %u\n", ns->geom.secshift);
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",
(unsigned long long)ns->geom.totszoob >> 10);
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;
}
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;
@ -1454,7 +1445,7 @@ int do_read_error(struct nandsim *ns, int num)
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)) {
int flips = 1;
@ -1479,7 +1470,7 @@ static void read_page(struct nandsim *ns, int num)
union ns_mem *mypage;
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);
memset(ns->buf.byte, 0xFF, num);
} else {
@ -1490,7 +1481,7 @@ static void read_page(struct nandsim *ns, int num)
ns->regs.row, ns->regs.column + ns->regs.off);
if (do_read_error(ns, num))
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);
if (tx != num) {
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) {
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->pages_written[ns->regs.row + i] = 0;
}
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);
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;
if (!ns->pages_written[ns->regs.row]) {
off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
if (!test_bit(ns->regs.row, ns->pages_written)) {
all = 1;
memset(ns->file_buf, 0xff, ns->geom.pgszoob);
} 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);
return -1;
}
ns->pages_written[ns->regs.row] = 1;
__set_bit(ns->regs.row, ns->pages_written);
} else {
tx = write_file(ns, ns->cfile, pg_off, num, off);
if (tx != num) {

View File

@ -324,8 +324,6 @@ static int nuc900_nand_remove(struct platform_device *pdev)
kfree(nuc900_nand);
platform_set_drvdata(pdev, NULL);
return 0;
}

View File

@ -154,7 +154,7 @@ static struct nand_ecclayout omap_oobinfo;
*/
static uint8_t scan_ff_pattern[] = { 0xff };
static struct nand_bbt_descr bb_descrip_flashbased = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
.options = NAND_BBT_SCANALLPAGES,
.offs = 0,
.len = 1,
.pattern = scan_ff_pattern,
@ -1831,7 +1831,7 @@ static int omap_nand_probe(struct platform_device *pdev)
struct resource *res;
struct mtd_part_parser_data ppdata = {};
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
@ -2087,7 +2087,6 @@ static int omap_nand_remove(struct platform_device *pdev)
mtd);
omap3_free_bch(&info->mtd);
platform_set_drvdata(pdev, NULL);
if (info->dma)
dma_release_channel(info->dma);

View File

@ -130,8 +130,9 @@ static int __init orion_nand_probe(struct platform_device *pdev)
if (!of_property_read_u32(pdev->dev.of_node,
"chip-delay", &val))
board->chip_delay = (u8)val;
} else
board = pdev->dev.platform_data;
} else {
board = dev_get_platdata(&pdev->dev);
}
mtd->priv = nc;
mtd->owner = THIS_MODULE;
@ -186,7 +187,6 @@ no_dev:
clk_disable_unprepare(clk);
clk_put(clk);
}
platform_set_drvdata(pdev, NULL);
iounmap(io_base);
no_res:
kfree(nc);

View File

@ -30,7 +30,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL };
*/
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 plat_nand_data *data;
struct resource *res;
@ -122,7 +122,6 @@ static int plat_nand_probe(struct platform_device *pdev)
out:
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
platform_set_drvdata(pdev, NULL);
iounmap(data->io_base);
out_release_io:
release_mem_region(res->start, resource_size(res));
@ -137,7 +136,7 @@ out_free:
static int plat_nand_remove(struct platform_device *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;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

View File

@ -25,7 +25,14 @@
#include <linux/of.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>
#endif
#include <linux/platform_data/mtd-nand-pxa3xx.h>
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
@ -80,6 +87,7 @@
#define NDSR_RDDREQ (0x1 << 1)
#define NDSR_WRCMDREQ (0x1)
#define NDCB0_LEN_OVRD (0x1 << 28)
#define NDCB0_ST_ROW_EN (0x1 << 26)
#define NDCB0_AUTO_RS (0x1 << 25)
#define NDCB0_CSEL (0x1 << 24)
@ -123,9 +131,13 @@ enum {
STATE_READY,
};
enum pxa3xx_nand_variant {
PXA3XX_NAND_VARIANT_PXA,
PXA3XX_NAND_VARIANT_ARMADA370,
};
struct pxa3xx_nand_host {
struct nand_chip chip;
struct pxa3xx_nand_cmdset *cmdset;
struct mtd_info *mtd;
void *info_data;
@ -139,10 +151,6 @@ struct pxa3xx_nand_host {
unsigned int row_addr_cycles;
size_t read_id_bytes;
/* cached register value */
uint32_t reg_ndcr;
uint32_t ndtr0cs0;
uint32_t ndtr1cs0;
};
struct pxa3xx_nand_info {
@ -171,9 +179,16 @@ struct pxa3xx_nand_info {
struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
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 use_ecc; /* use HW ECC ? */
int use_dma; /* use DMA ? */
int use_spare; /* use spare ? */
int is_ready;
unsigned int page_size; /* page size of attached chip */
@ -181,33 +196,22 @@ struct pxa3xx_nand_info {
unsigned int oob_size;
int retcode;
/* cached register value */
uint32_t reg_ndcr;
uint32_t ndtr0cs0;
uint32_t ndtr1cs0;
/* generated NDCBx register values */
uint32_t ndcb0;
uint32_t ndcb1;
uint32_t ndcb2;
uint32_t ndcb3;
};
static bool use_dma = 1;
module_param(use_dma, bool, 0444);
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[] = {
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
{ 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 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_tCS(c) (min((c), 7) << 16)
#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_tAR(ns2cycle(t->tAR, nand_clk));
host->ndtr0cs0 = ndtr0;
host->ndtr1cs0 = ndtr1;
info->ndtr0cs0 = ndtr0;
info->ndtr1cs0 = ndtr1;
nand_writel(info, NDTR0CS0, ndtr0);
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)
{
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;
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)
{
struct pxa3xx_nand_host *host = info->host[info->cs];
uint32_t ndcr;
ndcr = host->reg_ndcr;
ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
ndcr = info->reg_ndcr;
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;
/* 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);
}
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;
@ -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)
{
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);
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)
{
@ -467,9 +488,22 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
nand_writel(info, NDSR, NDSR_WRCMDREQ);
status &= ~NDSR_WRCMDREQ;
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->ndcb1);
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 */
@ -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,
uint16_t column, int page_addr)
{
uint16_t cmd;
int addr_cycle, exec_cmd;
struct pxa3xx_nand_host *host;
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->oob_size = 0;
info->use_ecc = 0;
info->use_spare = 1;
info->use_dma = (use_dma) ? 1 : 0;
info->is_ready = 0;
info->retcode = ERR_NONE;
if (info->cs != 0)
@ -520,12 +555,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
case NAND_CMD_READOOB:
pxa3xx_set_datasize(info);
break;
case NAND_CMD_PARAM:
info->use_spare = 0;
break;
case NAND_CMD_SEQIN:
exec_cmd = 0;
break;
default:
info->ndcb1 = 0;
info->ndcb2 = 0;
info->ndcb3 = 0;
break;
}
@ -535,21 +574,17 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
switch (command) {
case NAND_CMD_READOOB:
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))
info->ndcb0 |= NDCB0_CMD_TYPE(0)
| addr_cycle
| (cmd & NDCB0_CMD1_MASK);
else
info->ndcb0 |= NDCB0_CMD_TYPE(0)
| NDCB0_DBC
| addr_cycle
| cmd;
if (command == NAND_CMD_READOOB)
info->buf_start += mtd->writesize;
/* Second command setting for large pages */
if (host->page_size >= PAGE_CHUNK_SIZE)
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
case NAND_CMD_SEQIN:
/* small page addr setting */
@ -580,49 +615,58 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
break;
}
cmd = host->cmdset->program;
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
| NDCB0_AUTO_RS
| NDCB0_ST_ROW_EN
| NDCB0_DBC
| cmd
| (NAND_CMD_PAGEPROG << 8)
| NAND_CMD_SEQIN
| addr_cycle;
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:
cmd = host->cmdset->read_id;
info->buf_count = host->read_id_bytes;
info->ndcb0 |= NDCB0_CMD_TYPE(3)
| NDCB0_ADDR_CYC(1)
| cmd;
| command;
info->ndcb1 = (column & 0xFF);
info->data_size = 8;
break;
case NAND_CMD_STATUS:
cmd = host->cmdset->read_status;
info->buf_count = 1;
info->ndcb0 |= NDCB0_CMD_TYPE(4)
| NDCB0_ADDR_CYC(1)
| cmd;
| command;
info->data_size = 8;
break;
case NAND_CMD_ERASE1:
cmd = host->cmdset->erase;
info->ndcb0 |= NDCB0_CMD_TYPE(2)
| NDCB0_AUTO_RS
| NDCB0_ADDR_CYC(3)
| NDCB0_DBC
| cmd;
| (NAND_CMD_ERASE2 << 8)
| NAND_CMD_ERASE1;
info->ndcb1 = page_addr;
info->ndcb2 = 0;
break;
case NAND_CMD_RESET:
cmd = host->cmdset->reset;
info->ndcb0 |= NDCB0_CMD_TYPE(5)
| cmd;
| command;
break;
@ -652,7 +696,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
* "byte" address into a "word" address appropriate
* for indexing a word-oriented device
*/
if (host->reg_ndcr & NDCR_DWIDTH_M)
if (info->reg_ndcr & NDCR_DWIDTH_M)
column /= 2;
/*
@ -662,8 +706,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
*/
if (info->cs != host->cs) {
info->cs = host->cs;
nand_writel(info, NDTR0CS0, host->ndtr0cs0);
nand_writel(info, NDTR1CS0, host->ndtr1cs0);
nand_writel(info, NDTR0CS0, info->ndtr0cs0);
nand_writel(info, NDTR1CS0, info->ndtr1cs0);
}
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)
{
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];
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 */
host->cmdset = &default_cmdset;
host->page_size = f->page_size;
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_SPARE_EN; /* enable spare by default */
host->reg_ndcr = ndcr;
info->reg_ndcr = ndcr;
pxa3xx_nand_set_timing(host, f->timing);
return 0;
@ -863,12 +906,9 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
host->read_id_bytes = 2;
}
host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
host->cmdset = &default_cmdset;
host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
return 0;
}
@ -878,6 +918,7 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
*/
#define MAX_BUFF_SIZE PAGE_SIZE
#ifdef ARCH_HAS_DMA
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
{
struct platform_device *pdev = info->pdev;
@ -912,6 +953,32 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
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)
{
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_info *info = host->info_data;
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;
const struct pxa3xx_nand_flash *f = NULL;
struct nand_chip *chip = mtd->priv;
@ -1003,7 +1070,7 @@ KEEP_CONFIG:
chip->ecc.size = host->page_size;
chip->ecc.strength = 1;
if (host->reg_ndcr & NDCR_DWIDTH_M)
if (info->reg_ndcr & NDCR_DWIDTH_M)
chip->options |= NAND_BUSWIDTH_16;
if (nand_scan_ident(mtd, 1, def))
@ -1019,8 +1086,6 @@ KEEP_CONFIG:
host->row_addr_cycles = 3;
else
host->row_addr_cycles = 2;
mtd->name = mtd_names[0];
return nand_scan_tail(mtd);
}
@ -1034,13 +1099,11 @@ static int alloc_nand_resource(struct platform_device *pdev)
struct resource *r;
int ret, irq, cs;
pdata = pdev->dev.platform_data;
info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
pdata = dev_get_platdata(&pdev->dev);
info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) +
sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
if (!info)
return -ENOMEM;
}
info->pdev = pdev;
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);
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)) {
dev_err(&pdev->dev, "failed to get nand clock\n");
ret = PTR_ERR(info->clk);
goto fail_free_mtd;
return PTR_ERR(info->clk);
}
clk_enable(info->clk);
ret = clk_prepare_enable(info->clk);
if (ret < 0)
return ret;
/*
* This is a dirty hack to make this driver work from devicetree
* bindings. It can be removed once we have a prober DMA controller
* framework for DT.
*/
if (pdev->dev.of_node && cpu_is_pxa3xx()) {
info->drcmr_dat = 97;
info->drcmr_cmd = 99;
} else {
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (r == NULL) {
dev_err(&pdev->dev, "no resource defined for data DMA\n");
ret = -ENXIO;
goto fail_put_clk;
}
info->drcmr_dat = r->start;
if (use_dma) {
/*
* This is a dirty hack to make this driver work from
* devicetree bindings. It can be removed once we have
* a prober DMA controller framework for DT.
*/
if (pdev->dev.of_node &&
of_machine_is_compatible("marvell,pxa3xx")) {
info->drcmr_dat = 97;
info->drcmr_cmd = 99;
} else {
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (r == NULL) {
dev_err(&pdev->dev,
"no resource defined for data DMA\n");
ret = -ENXIO;
goto fail_disable_clk;
}
info->drcmr_dat = r->start;
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (r == NULL) {
dev_err(&pdev->dev, "no resource defined for command DMA\n");
ret = -ENXIO;
goto fail_put_clk;
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (r == NULL) {
dev_err(&pdev->dev,
"no resource defined for cmd DMA\n");
ret = -ENXIO;
goto fail_disable_clk;
}
info->drcmr_cmd = r->start;
}
info->drcmr_cmd = r->start;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no IRQ resource defined\n");
ret = -ENXIO;
goto fail_put_clk;
goto fail_disable_clk;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL) {
dev_err(&pdev->dev, "no IO memory resource defined\n");
ret = -ENODEV;
goto fail_put_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_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(info->mmio_base)) {
ret = PTR_ERR(info->mmio_base);
goto fail_disable_clk;
}
info->mmio_phys = r->start;
ret = pxa3xx_nand_init_buff(info);
if (ret)
goto fail_free_io;
goto fail_disable_clk;
/* initialize all interrupts to be disabled */
disable_int(info, NDSR_MASK);
@ -1152,21 +1207,9 @@ static int alloc_nand_resource(struct platform_device *pdev)
fail_free_buf:
free_irq(irq, info);
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);
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);
pxa3xx_nand_free_buff(info);
fail_disable_clk:
clk_disable_unprepare(info->clk);
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_platform_data *pdata;
struct resource *r;
int irq, cs;
if (!info)
return 0;
pdata = pdev->dev.platform_data;
platform_set_drvdata(pdev, NULL);
pdata = dev_get_platdata(&pdev->dev);
irq = platform_get_irq(pdev, 0);
if (irq >= 0)
free_irq(irq, info);
if (use_dma) {
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);
pxa3xx_nand_free_buff(info);
iounmap(info->mmio_base);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(r->start, resource_size(r));
clk_disable(info->clk);
clk_put(info->clk);
clk_disable_unprepare(info->clk);
for (cs = 0; cs < pdata->num_cs; cs++)
nand_release(info->host[cs]->mtd);
kfree(info);
return 0;
}
#ifdef CONFIG_OF
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)
{
@ -1251,11 +1298,18 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
struct pxa3xx_nand_info *info;
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);
if (ret)
return ret;
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
return -ENODEV;
@ -1268,10 +1322,14 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
}
info = platform_get_drvdata(pdev);
info->variant = pxa3xx_nand_get_variant(pdev);
probe_success = 0;
for (cs = 0; cs < pdata->num_cs; cs++) {
struct mtd_info *mtd = info->host[cs]->mtd;
mtd->name = pdev->name;
info->cs = cs;
ret = pxa3xx_nand_scan(info->host[cs]->mtd);
ret = pxa3xx_nand_scan(mtd);
if (ret) {
dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
cs);
@ -1279,7 +1337,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
}
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],
pdata->nr_parts[cs]);
if (!ret)
@ -1302,7 +1360,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
struct mtd_info *mtd;
int cs;
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
if (info->state) {
dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
return -EAGAIN;
@ -1323,7 +1381,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
struct mtd_info *mtd;
int cs;
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
/* We don't want to handle interrupt without calling mtd routine */
disable_int(info, NDCR_INT_MASK);

View File

@ -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
*/
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);
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
*/
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);
uint32_t reg;
@ -312,7 +312,7 @@ static uint8_t r852_read_byte(struct mtd_info *mtd)
/*
* 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);
@ -357,7 +357,7 @@ void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
* Wait till card is ready.
* 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;
@ -386,7 +386,7 @@ int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
* 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);
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
*/
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);
@ -429,7 +429,7 @@ void r852_ecc_hwctl(struct mtd_info *mtd, int mode)
* 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)
{
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
*/
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)
{
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
*/
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) {
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
*/
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(dev, R852_CTL, R852_CTL_RESET);
@ -557,7 +557,7 @@ void r852_engine_disable(struct r852_device *dev)
* 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;
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
* 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);
dev->card_unstable = 0;
@ -586,8 +586,8 @@ void r852_update_card_detect(struct r852_device *dev)
r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg);
}
ssize_t r852_media_type_show(struct device *sys_dev,
struct device_attribute *attr, char *buf)
static ssize_t r852_media_type_show(struct device *sys_dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev);
struct r852_device *dev = r852_get_dev(mtd);
@ -597,11 +597,11 @@ ssize_t r852_media_type_show(struct device *sys_dev,
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 */
void r852_update_media_status(struct r852_device *dev)
static void r852_update_media_status(struct r852_device *dev)
{
uint8_t reg;
unsigned long flags;
@ -630,7 +630,7 @@ void r852_update_media_status(struct r852_device *dev)
* Register the nand device
* 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);
@ -668,7 +668,7 @@ error1:
* 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)
return;
@ -682,7 +682,7 @@ void r852_unregister_nand_device(struct r852_device *dev)
}
/* 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 =
container_of(work, struct r852_device, card_detect_work.work);
@ -821,7 +821,7 @@ out:
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;
struct nand_chip *chip;
@ -961,7 +961,7 @@ error1:
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);
@ -992,7 +992,7 @@ void r852_remove(struct pci_dev *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);
@ -1002,7 +1002,7 @@ void r852_shutdown(struct pci_dev *pci_dev)
pci_disable_device(pci_dev);
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int r852_suspend(struct device *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);
return 0;
}
#else
#define r852_suspend NULL
#define r852_resume NULL
#endif
static const struct pci_device_id r852_pci_id_tbl[] = {

View File

@ -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)
{
return dev->dev.platform_data;
return dev_get_platdata(&dev->dev);
}
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);
platform_set_drvdata(pdev, NULL);
if (info == NULL)
return 0;

View File

@ -137,7 +137,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl)
dma_cap_mask_t mask;
struct dma_slave_config cfg;
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;
if (!pdata)
@ -1131,7 +1131,7 @@ static int flctl_probe(struct platform_device *pdev)
if (pdev->dev.of_node)
pdata = flctl_parse_dt(&pdev->dev);
else
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no setup data defined\n");

View File

@ -112,7 +112,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
struct resource *r;
int err = 0;
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) {
dev_err(&pdev->dev, "no platform data!\n");
@ -194,7 +194,6 @@ err_add:
nand_release(&sharpsl->mtd);
err_scan:
platform_set_drvdata(pdev, NULL);
iounmap(sharpsl->io);
err_ioremap:
err_get_res:
@ -212,8 +211,6 @@ static int sharpsl_nand_remove(struct platform_device *pdev)
/* Release resources, unregister device */
nand_release(&sharpsl->mtd);
platform_set_drvdata(pdev, NULL);
iounmap(sharpsl->io);
/* Free the MTD device structure */

View File

@ -42,7 +42,7 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_oob_ops ops;
struct sm_oob oob;
int ret, error = 0;
int ret;
memset(&oob, -1, SM_OOB_SIZE);
oob.block_status = 0x0F;
@ -61,11 +61,10 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
printk(KERN_NOTICE
"sm_common: can't mark sector at %i as bad\n",
(int)ofs);
error = -EIO;
} else
mtd->ecc_stats.badblocks++;
return -EIO;
}
return error;
return 0;
}
static struct nand_flash_dev nand_smartmedia_flash_ids[] = {

View File

@ -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)
{
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,
IORESOURCE_MEM, 0);
struct resource *ccr = platform_get_resource(dev,

View File

@ -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)
{
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);
}
@ -138,7 +138,7 @@ static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
struct nand_chip *chip = mtd->priv;
struct txx9ndfmc_priv *txx9_priv = chip->priv;
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) {
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)
{
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);
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)
{
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
int hold, spw;
int i;
struct txx9ndfmc_drvdata *drvdata;
unsigned long gbusclk = plat->gbus_clock;
struct resource *res;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
drvdata->base = devm_ioremap_resource(&dev->dev, res);
if (IS_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);
int i;
platform_set_drvdata(dev, NULL);
if (!drvdata)
return 0;
for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {

View File

@ -20,6 +20,11 @@
#include <linux/slab.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,
struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
@ -38,10 +43,13 @@ static int parse_ofpart_partitions(struct mtd_info *master,
return 0;
/* First count the subnodes */
pp = NULL;
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++;
}
if (nr_parts == 0)
return 0;
@ -50,13 +58,15 @@ static int parse_ofpart_partitions(struct mtd_info *master,
if (!*pparts)
return -ENOMEM;
pp = NULL;
i = 0;
while ((pp = of_get_next_child(node, pp))) {
for_each_child_of_node(node, pp) {
const __be32 *reg;
int len;
int a_cells, s_cells;
if (node_has_compatible(pp))
continue;
reg = of_get_property(pp, "reg", &len);
if (!reg) {
nr_parts--;

View File

@ -38,7 +38,7 @@ struct onenand_info {
static int generic_onenand_probe(struct platform_device *pdev)
{
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;
unsigned long size = resource_size(res);
int err;
@ -94,8 +94,6 @@ static int generic_onenand_remove(struct platform_device *pdev)
struct resource *res = pdev->resource;
unsigned long size = resource_size(res);
platform_set_drvdata(pdev, NULL);
if (info) {
onenand_release(&info->mtd);
release_mem_region(res->start, size);

View File

@ -639,7 +639,7 @@ static int omap2_onenand_probe(struct platform_device *pdev)
struct resource *res;
struct mtd_part_parser_data ppdata = {};
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
@ -810,7 +810,6 @@ static int omap2_onenand_remove(struct platform_device *pdev)
if (c->dma_channel != -1)
omap_free_dma(c->dma_channel);
omap2_onenand_shutdown(pdev);
platform_set_drvdata(pdev, NULL);
if (c->gpio_irq) {
free_irq(gpio_to_irq(c->gpio_irq), c);
gpio_free(c->gpio_irq);

View File

@ -133,7 +133,6 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc
{
struct onenand_chip *this = mtd->priv;
bd->options &= ~NAND_BBT_SCANEMPTY;
return create_bbt(mtd, this->page_buf, bd, -1);
}

View File

@ -867,7 +867,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
struct resource *r;
int size, err;
pdata = pdev->dev.platform_data;
pdata = dev_get_platdata(&pdev->dev);
/* No need to check pdata. the platform data is optional */
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,
resource_size(onenand->base_res));
platform_set_drvdata(pdev, NULL);
kfree(onenand->oob_buf);
kfree(onenand->page_buf);
kfree(onenand);

View File

@ -22,7 +22,7 @@
struct workqueue_struct *cache_flush_workqueue;
static struct workqueue_struct *cache_flush_workqueue;
static int cache_timeout = 1000;
module_param(cache_timeout, int, S_IRUGO);
@ -41,7 +41,7 @@ struct sm_sysfs_attribute {
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)
{
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 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 **attributes;
@ -107,7 +107,7 @@ error1:
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;
int i;
@ -571,7 +571,7 @@ static const uint8_t cis_signature[] = {
};
/* Find out media parameters.
* 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 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 */
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;
int error;
@ -899,7 +899,7 @@ struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num)
/* ----------------- cache handling ------------------------------------------*/
/* 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_clean = 1;
@ -909,7 +909,7 @@ void sm_cache_init(struct sm_ftl *ftl)
}
/* 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);
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 */
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,
&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 */
int sm_cache_flush(struct sm_ftl *ftl)
static int sm_cache_flush(struct sm_ftl *ftl)
{
struct ftl_zone *zone;
@ -1274,10 +1274,10 @@ static struct mtd_blktrans_ops sm_ftl_ops = {
static __init int sm_module_init(void)
{
int error = 0;
cache_flush_workqueue = create_freezable_workqueue("smflush");
if (IS_ERR(cache_flush_workqueue))
return PTR_ERR(cache_flush_workqueue);
cache_flush_workqueue = create_freezable_workqueue("smflush");
if (!cache_flush_workqueue)
return -ENOMEM;
error = register_mtd_blktrans(&sm_ftl_ops);
if (error)

View File

@ -7,3 +7,12 @@ obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o
obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o
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

View 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;
}

View 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);

View File

@ -49,6 +49,7 @@
#include <linux/err.h>
#include <linux/mtd/nand.h>
#include <linux/slab.h>
#include "mtd_test.h"
static int dev;
module_param(dev, int, S_IRUGO);
@ -98,47 +99,13 @@ static uint8_t hash(unsigned offset)
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 */
static int write_page(int log)
{
int err = 0;
size_t written;
if (log)
pr_info("write_page\n");
err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer);
if (err || written != mtd->writesize) {
pr_err("error: write failed at %#llx\n", (long long)offset);
if (!err)
err = -EIO;
}
return err;
return mtdtest_write(mtd, offset, mtd->writesize, wbuffer);
}
/* Re-writes the data area while leaving the OOB alone. */
@ -415,7 +382,7 @@ static int __init mtd_nandbiterrs_init(void)
goto exit_rbuffer;
}
err = erase_block();
err = mtdtest_erase_eraseblock(mtd, eraseblock);
if (err)
goto exit_error;
@ -428,7 +395,7 @@ static int __init mtd_nandbiterrs_init(void)
goto exit_error;
/* We leave the block un-erased in case of test failure. */
err = erase_block();
err = mtdtest_erase_eraseblock(mtd, eraseblock);
if (err)
goto exit_error;

View File

@ -31,6 +31,8 @@
#include <linux/sched.h>
#include <linux/random.h>
#include "mtd_test.h"
static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@ -49,49 +51,6 @@ static int use_len_max;
static int vary_offset;
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)
{
use_len -= 1;
@ -304,38 +263,6 @@ static int verify_all_eraseblocks(void)
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)
{
int err = 0;
@ -380,17 +307,16 @@ static int __init mtd_oobtest_init(void)
err = -ENOMEM;
readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!readbuf) {
pr_err("error: cannot allocate memory\n");
if (!readbuf)
goto out;
}
writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!writebuf) {
pr_err("error: cannot allocate memory\n");
if (!writebuf)
goto out;
bbt = kzalloc(ebcnt, GFP_KERNEL);
if (!bbt)
goto out;
}
err = scan_for_bad_eraseblocks();
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
@ -402,7 +328,7 @@ static int __init mtd_oobtest_init(void)
/* First test: write all OOB, read it back and verify */
pr_info("test 1 of 5\n");
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
@ -422,7 +348,7 @@ static int __init mtd_oobtest_init(void)
*/
pr_info("test 2 of 5\n");
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
@ -452,7 +378,7 @@ static int __init mtd_oobtest_init(void)
*/
pr_info("test 3 of 5\n");
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
@ -485,7 +411,7 @@ static int __init mtd_oobtest_init(void)
/* Fourth test: try to write off end of device */
pr_info("test 4 of 5\n");
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
@ -577,7 +503,7 @@ static int __init mtd_oobtest_init(void)
errcnt += 1;
}
err = erase_eraseblock(ebcnt - 1);
err = mtdtest_erase_eraseblock(mtd, ebcnt - 1);
if (err)
goto out;
@ -626,7 +552,7 @@ static int __init mtd_oobtest_init(void)
pr_info("test 5 of 5\n");
/* Erase all eraseblocks */
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;

View File

@ -31,6 +31,8 @@
#include <linux/sched.h>
#include <linux/random.h>
#include "mtd_test.h"
static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@ -48,52 +50,18 @@ static int pgcnt;
static int errcnt;
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)
{
int err = 0;
size_t written;
loff_t addr = ebnum * mtd->erasesize;
prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
cond_resched();
err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf);
if (err || written != mtd->erasesize)
pr_err("error: write failed at %#llx\n",
(long long)addr);
return err;
return mtdtest_write(mtd, addr, mtd->erasesize, writebuf);
}
static int verify_eraseblock(int ebnum)
{
uint32_t j;
size_t read;
int err = 0, i;
loff_t addr0, addrn;
loff_t addr = ebnum * mtd->erasesize;
@ -109,31 +77,16 @@ static int verify_eraseblock(int ebnum)
prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
/* Do a read to set the internal dataRAMs to different data */
err = mtd_read(mtd, addr0, bufsize, &read, twopages);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr0);
err = mtdtest_read(mtd, addr0, bufsize, twopages);
if (err)
return err;
}
err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
pr_err("error: read failed at %#llx\n",
(long long)(addrn - bufsize));
err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
if (err)
return err;
}
memset(twopages, 0, bufsize);
err = mtd_read(mtd, addr, bufsize, &read, twopages);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
err = mtdtest_read(mtd, addr, bufsize, twopages);
if (err)
break;
}
if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
pr_err("error: verify failed at %#llx\n",
(long long)addr);
@ -145,31 +98,16 @@ static int verify_eraseblock(int ebnum)
struct rnd_state old_state = rnd_state;
/* Do a read to set the internal dataRAMs to different data */
err = mtd_read(mtd, addr0, bufsize, &read, twopages);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr0);
err = mtdtest_read(mtd, addr0, bufsize, twopages);
if (err)
return err;
}
err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
pr_err("error: read failed at %#llx\n",
(long long)(addrn - bufsize));
err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
if (err)
return err;
}
memset(twopages, 0, bufsize);
err = mtd_read(mtd, addr, bufsize, &read, twopages);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != bufsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
err = mtdtest_read(mtd, addr, bufsize, twopages);
if (err)
return err;
}
memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
if (memcmp(twopages, boundary, bufsize)) {
@ -184,17 +122,14 @@ static int verify_eraseblock(int ebnum)
static int crosstest(void)
{
size_t read;
int err = 0, i;
loff_t addr, addr0, addrn;
unsigned char *pp1, *pp2, *pp3, *pp4;
pr_info("crosstest\n");
pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
if (!pp1) {
pr_err("error: cannot allocate memory\n");
if (!pp1)
return -ENOMEM;
}
pp2 = pp1 + pgsize;
pp3 = pp2 + pgsize;
pp4 = pp3 + pgsize;
@ -210,24 +145,16 @@ static int crosstest(void)
/* Read 2nd-to-last page to pp1 */
addr = addrn - pgsize - pgsize;
err = mtd_read(mtd, addr, pgsize, &read, pp1);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
err = mtdtest_read(mtd, addr, pgsize, pp1);
if (err) {
kfree(pp1);
return err;
}
/* Read 3rd-to-last page to pp1 */
addr = addrn - pgsize - pgsize - pgsize;
err = mtd_read(mtd, addr, pgsize, &read, pp1);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
err = mtdtest_read(mtd, addr, pgsize, pp1);
if (err) {
kfree(pp1);
return err;
}
@ -235,12 +162,8 @@ static int crosstest(void)
/* Read first page to pp2 */
addr = addr0;
pr_info("reading page at %#llx\n", (long long)addr);
err = mtd_read(mtd, addr, pgsize, &read, pp2);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
err = mtdtest_read(mtd, addr, pgsize, pp2);
if (err) {
kfree(pp1);
return err;
}
@ -248,12 +171,8 @@ static int crosstest(void)
/* Read last page to pp3 */
addr = addrn - pgsize;
pr_info("reading page at %#llx\n", (long long)addr);
err = mtd_read(mtd, addr, pgsize, &read, pp3);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
err = mtdtest_read(mtd, addr, pgsize, pp3);
if (err) {
kfree(pp1);
return err;
}
@ -261,12 +180,8 @@ static int crosstest(void)
/* Read first page again to pp4 */
addr = addr0;
pr_info("reading page at %#llx\n", (long long)addr);
err = mtd_read(mtd, addr, pgsize, &read, pp4);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
err = mtdtest_read(mtd, addr, pgsize, pp4);
if (err) {
kfree(pp1);
return err;
}
@ -285,7 +200,6 @@ static int crosstest(void)
static int erasecrosstest(void)
{
size_t read, written;
int err = 0, i, ebnum, ebnum2;
loff_t addr0;
char *readbuf = twopages;
@ -304,30 +218,22 @@ static int erasecrosstest(void)
ebnum2 -= 1;
pr_info("erasing block %d\n", ebnum);
err = erase_eraseblock(ebnum);
err = mtdtest_erase_eraseblock(mtd, ebnum);
if (err)
return err;
pr_info("writing 1st page of block %d\n", ebnum);
prandom_bytes_state(&rnd_state, writebuf, pgsize);
strcpy(writebuf, "There is no data like this!");
err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
if (err || written != pgsize) {
pr_info("error: write failed at %#llx\n",
(long long)addr0);
return err ? err : -1;
}
err = mtdtest_write(mtd, addr0, pgsize, writebuf);
if (err)
return err;
pr_info("reading 1st page of block %d\n", ebnum);
memset(readbuf, 0, pgsize);
err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr0);
return err ? err : -1;
}
err = mtdtest_read(mtd, addr0, pgsize, readbuf);
if (err)
return err;
pr_info("verifying 1st page of block %d\n", ebnum);
if (memcmp(writebuf, readbuf, pgsize)) {
@ -337,35 +243,27 @@ static int erasecrosstest(void)
}
pr_info("erasing block %d\n", ebnum);
err = erase_eraseblock(ebnum);
err = mtdtest_erase_eraseblock(mtd, ebnum);
if (err)
return err;
pr_info("writing 1st page of block %d\n", ebnum);
prandom_bytes_state(&rnd_state, writebuf, pgsize);
strcpy(writebuf, "There is no data like this!");
err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
if (err || written != pgsize) {
pr_err("error: write failed at %#llx\n",
(long long)addr0);
return err ? err : -1;
}
err = mtdtest_write(mtd, addr0, pgsize, writebuf);
if (err)
return err;
pr_info("erasing block %d\n", ebnum2);
err = erase_eraseblock(ebnum2);
err = mtdtest_erase_eraseblock(mtd, ebnum2);
if (err)
return err;
pr_info("reading 1st page of block %d\n", ebnum);
memset(readbuf, 0, pgsize);
err = mtd_read(mtd, addr0, pgsize, &read, readbuf);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr0);
return err ? err : -1;
}
err = mtdtest_read(mtd, addr0, pgsize, readbuf);
if (err)
return err;
pr_info("verifying 1st page of block %d\n", ebnum);
if (memcmp(writebuf, readbuf, pgsize)) {
@ -381,7 +279,6 @@ static int erasecrosstest(void)
static int erasetest(void)
{
size_t read, written;
int err = 0, i, ebnum, ok = 1;
loff_t addr0;
@ -395,33 +292,25 @@ static int erasetest(void)
}
pr_info("erasing block %d\n", ebnum);
err = erase_eraseblock(ebnum);
err = mtdtest_erase_eraseblock(mtd, ebnum);
if (err)
return err;
pr_info("writing 1st page of block %d\n", ebnum);
prandom_bytes_state(&rnd_state, writebuf, pgsize);
err = mtd_write(mtd, addr0, pgsize, &written, writebuf);
if (err || written != pgsize) {
pr_err("error: write failed at %#llx\n",
(long long)addr0);
return err ? err : -1;
}
err = mtdtest_write(mtd, addr0, pgsize, writebuf);
if (err)
return err;
pr_info("erasing block %d\n", ebnum);
err = erase_eraseblock(ebnum);
err = mtdtest_erase_eraseblock(mtd, ebnum);
if (err)
return err;
pr_info("reading 1st page of block %d\n", ebnum);
err = mtd_read(mtd, addr0, pgsize, &read, twopages);
if (mtd_is_bitflip(err))
err = 0;
if (err || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr0);
return err ? err : -1;
}
err = mtdtest_read(mtd, addr0, pgsize, twopages);
if (err)
return err;
pr_info("verifying 1st page of block %d is all 0xff\n",
ebnum);
@ -440,38 +329,6 @@ static int erasetest(void)
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)
{
int err = 0;
@ -516,36 +373,28 @@ static int __init mtd_pagetest_init(void)
err = -ENOMEM;
bufsize = pgsize * 2;
writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!writebuf) {
pr_err("error: cannot allocate memory\n");
if (!writebuf)
goto out;
}
twopages = kmalloc(bufsize, GFP_KERNEL);
if (!twopages) {
pr_err("error: cannot allocate memory\n");
if (!twopages)
goto out;
}
boundary = kmalloc(bufsize, GFP_KERNEL);
if (!boundary) {
pr_err("error: cannot allocate memory\n");
if (!boundary)
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)
goto out;
/* Erase all eraseblocks */
pr_info("erasing whole device\n");
for (i = 0; i < ebcnt; ++i) {
if (bbt[i])
continue;
err = erase_eraseblock(i);
if (err)
goto out;
cond_resched();
}
pr_info("erased %u eraseblocks\n", i);
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
pr_info("erased %u eraseblocks\n", ebcnt);
/* Write all eraseblocks */
prandom_seed_state(&rnd_state, 1);

View File

@ -29,6 +29,8 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include "mtd_test.h"
static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@ -44,7 +46,6 @@ static int pgcnt;
static int read_eraseblock_by_page(int ebnum)
{
size_t read;
int i, ret, err = 0;
loff_t addr = ebnum * mtd->erasesize;
void *buf = iobuf;
@ -52,16 +53,10 @@ static int read_eraseblock_by_page(int ebnum)
for (i = 0; i < pgcnt; i++) {
memset(buf, 0 , pgsize);
ret = mtd_read(mtd, addr, pgsize, &read, buf);
if (ret == -EUCLEAN)
ret = 0;
if (ret || read != pgsize) {
pr_err("error: read failed at %#llx\n",
(long long)addr);
ret = mtdtest_read(mtd, addr, pgsize, buf);
if (ret) {
if (!err)
err = ret;
if (!err)
err = -EINVAL;
}
if (mtd->oobsize) {
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)
{
uint64_t tmp;
@ -204,17 +164,16 @@ static int __init mtd_readtest_init(void)
err = -ENOMEM;
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!iobuf) {
pr_err("error: cannot allocate memory\n");
if (!iobuf)
goto out;
}
iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!iobuf1) {
pr_err("error: cannot allocate memory\n");
if (!iobuf1)
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)
goto out;

View File

@ -30,6 +30,8 @@
#include <linux/sched.h>
#include <linux/random.h>
#include "mtd_test.h"
static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@ -49,33 +51,6 @@ static int pgcnt;
static int goodebcnt;
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)
{
int err;
@ -103,54 +78,23 @@ static int multiblock_erase(int ebnum, int blocks)
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)
{
size_t written;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf);
if (err || written != mtd->erasesize) {
pr_err("error: write failed at %#llx\n", addr);
if (!err)
err = -EINVAL;
}
return err;
return mtdtest_write(mtd, addr, mtd->erasesize, iobuf);
}
static int write_eraseblock_by_page(int ebnum)
{
size_t written;
int i, err = 0;
loff_t addr = ebnum * mtd->erasesize;
void *buf = iobuf;
for (i = 0; i < pgcnt; i++) {
err = mtd_write(mtd, addr, pgsize, &written, buf);
if (err || written != pgsize) {
pr_err("error: write failed at %#llx\n",
addr);
if (!err)
err = -EINVAL;
err = mtdtest_write(mtd, addr, pgsize, buf);
if (err)
break;
}
addr += pgsize;
buf += pgsize;
}
@ -160,74 +104,41 @@ static int write_eraseblock_by_page(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;
loff_t addr = ebnum * mtd->erasesize;
void *buf = iobuf;
for (i = 0; i < n; i++) {
err = mtd_write(mtd, addr, sz, &written, buf);
if (err || written != sz) {
pr_err("error: write failed at %#llx\n",
addr);
if (!err)
err = -EINVAL;
err = mtdtest_write(mtd, addr, sz, buf);
if (err)
return err;
}
addr += sz;
buf += sz;
}
if (pgcnt % 2) {
err = mtd_write(mtd, addr, pgsize, &written, buf);
if (err || written != pgsize) {
pr_err("error: write failed at %#llx\n",
addr);
if (!err)
err = -EINVAL;
}
}
if (pgcnt % 2)
err = mtdtest_write(mtd, addr, pgsize, buf);
return err;
}
static int read_eraseblock(int ebnum)
{
size_t read;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
err = mtd_read(mtd, addr, mtd->erasesize, &read, 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;
return mtdtest_read(mtd, addr, mtd->erasesize, iobuf);
}
static int read_eraseblock_by_page(int ebnum)
{
size_t read;
int i, err = 0;
loff_t addr = ebnum * mtd->erasesize;
void *buf = iobuf;
for (i = 0; i < pgcnt; i++) {
err = mtd_read(mtd, addr, pgsize, &read, 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;
err = mtdtest_read(mtd, addr, pgsize, buf);
if (err)
break;
}
addr += pgsize;
buf += pgsize;
}
@ -237,53 +148,24 @@ static int read_eraseblock_by_page(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;
loff_t addr = ebnum * mtd->erasesize;
void *buf = iobuf;
for (i = 0; i < n; i++) {
err = mtd_read(mtd, addr, sz, &read, buf);
/* Ignore corrected ECC errors */
if (mtd_is_bitflip(err))
err = 0;
if (err || read != sz) {
pr_err("error: read failed at %#llx\n",
addr);
if (!err)
err = -EINVAL;
err = mtdtest_read(mtd, addr, sz, buf);
if (err)
return err;
}
addr += sz;
buf += sz;
}
if (pgcnt % 2) {
err = mtd_read(mtd, addr, pgsize, &read, 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;
}
}
if (pgcnt % 2)
err = mtdtest_read(mtd, addr, pgsize, buf);
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)
{
do_gettimeofday(&start);
@ -308,32 +190,6 @@ static long calc_speed(void)
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)
{
int err, i, blocks, j, k;
@ -384,18 +240,23 @@ static int __init mtd_speedtest_init(void)
err = -ENOMEM;
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!iobuf) {
pr_err("error: cannot allocate memory\n");
if (!iobuf)
goto out;
}
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)
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)
goto out;
@ -429,7 +290,7 @@ static int __init mtd_speedtest_init(void)
speed = calc_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)
goto out;
@ -463,7 +324,7 @@ static int __init mtd_speedtest_init(void)
speed = calc_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)
goto out;
@ -500,14 +361,9 @@ static int __init mtd_speedtest_init(void)
/* Erase all eraseblocks */
pr_info("Testing erase speed\n");
start_timing();
for (i = 0; i < ebcnt; ++i) {
if (bbt[i])
continue;
err = erase_eraseblock(i);
if (err)
goto out;
cond_resched();
}
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
stop_timing();
speed = calc_speed();
pr_info("erase speed is %ld KiB/s\n", speed);

View File

@ -31,6 +31,8 @@
#include <linux/vmalloc.h>
#include <linux/random.h>
#include "mtd_test.h"
static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
@ -81,49 +83,11 @@ static int rand_len(int offs)
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)
{
size_t read;
int eb = rand_eb();
int offs = rand_offs();
int len = rand_len(offs), err;
int len = rand_len(offs);
loff_t addr;
if (bbt[eb + 1]) {
@ -133,28 +97,17 @@ static int do_read(void)
len = mtd->erasesize - offs;
}
addr = eb * mtd->erasesize + offs;
err = mtd_read(mtd, addr, len, &read, 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;
return mtdtest_read(mtd, addr, len, readbuf);
}
static int do_write(void)
{
int eb = rand_eb(), offs, err, len;
size_t written;
loff_t addr;
offs = offsets[eb];
if (offs >= mtd->erasesize) {
err = erase_eraseblock(eb);
err = mtdtest_erase_eraseblock(mtd, eb);
if (err)
return err;
offs = offsets[eb] = 0;
@ -165,21 +118,16 @@ static int do_write(void)
if (bbt[eb + 1])
len = mtd->erasesize - offs;
else {
err = erase_eraseblock(eb + 1);
err = mtdtest_erase_eraseblock(mtd, eb + 1);
if (err)
return err;
offsets[eb + 1] = 0;
}
}
addr = eb * mtd->erasesize + offs;
err = mtd_write(mtd, addr, len, &written, writebuf);
if (unlikely(err || written != len)) {
pr_err("error: write failed at 0x%llx\n",
(long long)addr);
if (!err)
err = -EINVAL;
err = mtdtest_write(mtd, addr, len, writebuf);
if (unlikely(err))
return err;
}
offs += len;
while (offs > mtd->erasesize) {
offsets[eb++] = mtd->erasesize;
@ -197,30 +145,6 @@ static int do_operation(void)
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)
{
int err;
@ -276,15 +200,16 @@ static int __init mtd_stresstest_init(void)
readbuf = vmalloc(bufsize);
writebuf = vmalloc(bufsize);
offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
if (!readbuf || !writebuf || !offsets) {
pr_err("error: cannot allocate memory\n");
if (!readbuf || !writebuf || !offsets)
goto out;
}
for (i = 0; i < ebcnt; i++)
offsets[i] = mtd->erasesize;
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)
goto out;

View File

@ -30,6 +30,8 @@
#include <linux/sched.h>
#include <linux/random.h>
#include "mtd_test.h"
static int dev = -EINVAL;
module_param(dev, int, S_IRUGO);
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);
}
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)
{
size_t written;
@ -317,38 +275,6 @@ static int verify_all_eraseblocks_ff(void)
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)
{
int err = 0;
@ -393,21 +319,20 @@ static int __init mtd_subpagetest_init(void)
err = -ENOMEM;
bufsize = subpgsize * 32;
writebuf = kmalloc(bufsize, GFP_KERNEL);
if (!writebuf) {
pr_info("error: cannot allocate memory\n");
if (!writebuf)
goto out;
}
readbuf = kmalloc(bufsize, GFP_KERNEL);
if (!readbuf) {
pr_info("error: cannot allocate memory\n");
if (!readbuf)
goto out;
bbt = kzalloc(ebcnt, GFP_KERNEL);
if (!bbt)
goto out;
}
err = scan_for_bad_eraseblocks();
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
@ -439,7 +364,7 @@ static int __init mtd_subpagetest_init(void)
}
pr_info("verified %u eraseblocks\n", i);
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;
@ -477,7 +402,7 @@ static int __init mtd_subpagetest_init(void)
}
pr_info("verified %u eraseblocks\n", i);
err = erase_whole_device();
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
if (err)
goto out;

View File

@ -32,6 +32,7 @@
#include <linux/mtd/mtd.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include "mtd_test.h"
#define RETRIES 3
@ -92,35 +93,6 @@ static inline void stop_timing(void)
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
* @buf buffer.
@ -208,7 +180,7 @@ static inline int write_pattern(int ebnum, void *buf)
static int __init tort_init(void)
{
int err = 0, i, infinite = !cycles_count;
int *bad_ebs;
unsigned char *bad_ebs;
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
@ -265,7 +237,7 @@ static int __init tort_init(void)
if (!check_buf)
goto out_patt_FF;
bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL);
bad_ebs = kzalloc(ebcnt, GFP_KERNEL);
if (!bad_ebs)
goto out_check_buf;
@ -283,40 +255,16 @@ static int __init tort_init(void)
}
}
/*
* Check if there is a bad eraseblock among those we are going to test.
*/
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;
}
if (err) {
pr_err("EB %d is bad. Skip it.\n", i);
bad_ebs[i - eb] = 1;
}
}
}
err = mtdtest_scan_for_bad_eraseblocks(mtd, bad_ebs, eb, ebcnt);
if (err)
goto out;
start_timing();
while (1) {
int i;
void *patt;
/* Erase all eraseblocks */
for (i = eb; i < eb + ebcnt; i++) {
if (bad_ebs[i - eb])
continue;
err = erase_eraseblock(i);
if (err)
goto out;
cond_resched();
}
mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt);
/* Check if the eraseblocks contain only 0xFF bytes */
if (check) {

View File

@ -93,8 +93,6 @@ struct nand_bbt_descr {
#define NAND_BBT_CREATE_EMPTY 0x00000400
/* Search good / bad pattern through all pages of a block */
#define NAND_BBT_SCANALLPAGES 0x00000800
/* Scan block empty during good / bad block scan */
#define NAND_BBT_SCANEMPTY 0x00001000
/* Write bbt if neccecary */
#define NAND_BBT_WRITE 0x00002000
/* Read and write back block contents when writing bbt */

View File

@ -137,6 +137,7 @@ enum access_mode {
/**
* 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
* if this is NULL
* @nr_partitions: the number of partitions in the previous entry

View File

@ -173,6 +173,9 @@ struct mtd_info {
/* ECC layout structure pointer - read only! */
struct nand_ecclayout *ecclayout;
/* the ecc step size. */
unsigned int ecc_step_size;
/* max number of correctible bit errors per ecc step */
unsigned int ecc_strength;

View File

@ -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
* adjust this accordingly.
*/
#define NAND_MAX_OOBSIZE 640
#define NAND_MAX_OOBSIZE 744
#define NAND_MAX_PAGESIZE 8192
/*
@ -202,6 +202,10 @@ typedef enum {
/* Keep gcc happy */
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 */
#define ONFI_TIMING_MODE_0 (1 << 0)
#define ONFI_TIMING_MODE_1 (1 << 1)
@ -217,6 +221,9 @@ struct nand_chip;
/* ONFI subfeature parameters length */
#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 {
/* rev info and features block */
/* 'O' 'N' 'F' 'I' */
@ -224,7 +231,10 @@ struct nand_onfi_params {
__le16 revision;
__le16 features;
__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 */
char manufacturer[12];
@ -281,6 +291,40 @@ struct nand_onfi_params {
#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
* @lock: protection lock
@ -390,8 +434,8 @@ struct nand_buffers {
* @write_buf: [REPLACEABLE] write data from the buffer to the chip
* @read_buf: [REPLACEABLE] read data from the chip into the buffer
* @select_chip: [REPLACEABLE] select chip nr
* @block_bad: [REPLACEABLE] check, if the block is bad
* @block_markbad: [REPLACEABLE] mark the block bad
* @block_bad: [REPLACEABLE] check if a block is bad, using OOB markers
* @block_markbad: [REPLACEABLE] mark a block bad
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
* ALE/CLE/nCE. Also used to write command and address
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
@ -434,6 +478,12 @@ struct nand_buffers {
* bad block marker position; i.e., BBM == 11110111b is
* not bad when badblockbits == 7
* @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
* @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
@ -510,6 +560,8 @@ struct nand_chip {
unsigned int pagebuf_bitflips;
int subpagesize;
uint8_t cellinfo;
uint16_t ecc_strength_ds;
uint16_t ecc_step_ds;
int badblockpos;
int badblockbits;
@ -576,6 +628,11 @@ struct nand_chip {
{ .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \
.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
* @name: a human-readable name of the NAND chip
@ -593,6 +650,12 @@ struct nand_chip {
* @options: stores various chip bit options
* @id_len: The valid length of the @id.
* @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 {
char *name;
@ -609,6 +672,10 @@ struct nand_flash_dev {
unsigned int options;
uint16_t id_len;
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 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_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_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt);
@ -708,6 +775,12 @@ struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)
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. */
static inline int onfi_get_async_timing_mode(struct nand_chip *chip)
{

View File

@ -71,6 +71,10 @@ struct atmel_nand_data {
u8 on_flash_bbt; /* bbt on flash */
struct mtd_partition *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 */

View File

@ -16,19 +16,6 @@ struct pxa3xx_nand_timing {
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 {
char *name;
uint32_t chip_id;