1
0
Fork 0

MTD updates for v4.6

NAND:
  * Add sunxi_nand randomizer support
  * begin refactoring NAND ecclayout structs
  * fix pxa3xx_nand dmaengine usage
  * brcmnand: fix support for v7.1 controller
  * add Qualcomm NAND controller driver
 
 SPI NOR:
  * add new ls1021a, ls2080a support to Freescale QuadSPI
  * add new flash ID entries
  * support bottom-block protection for Winbond flash
  * support Status Register Write Protect
  * remove broken QPI support for Micron SPI flash
 
 JFFS2:
  * improve post-mount CRC scan efficiency
 
 General:
  * refactor bcm63xxpart parser, to later extend for NAND
  * add writebuf size parameter to mtdram
 
 Other minor code quality improvements
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJW9CzVAAoJEFySrpd9RFgtQFwQAJdH0wnsZTYfeqToIaD8yMM4
 rtakV/oIMSvMSWuqK+Mx0k6OjGwswgnGZ+tfQLRAYIhb33P8UD0F8Dv5D0x/+zRo
 EgiDlnss/lliXpbh2u4fsANSpFF/JUPXFqU6NanjqQ1rtvR60LUeKOFEz1NRciuV
 Ib6oDLFeXQFxwG0J+EBDo5MrT8aiPODtx4TS8VVo0o0y/WLkEujQPP5592TnCPha
 zX0n9azi26pARo7VLqWjVD8GigY5PadqJAWOZcQr0dGMQv5URtWcCCdThiNsCEzY
 SW9cYSr4CBdy1FIeoJ47yoBg8aFzhyeeuF1efb1U0MoYVL0rdIbznop3Kwilj48L
 Rnh4hvKkrTH16rO6RfKm1lIJaJQYKMErXyEceYMIjV91fEL3qhfbU9W6+Q5HT4hY
 oJmlH+4e/I1Jtf+vW4xFGMYclmYwCO6GJ4HHqnNpby/iH/nZ07hNX3lbxrlqHMwh
 MrSIidqLTsseXcyHBFc+42AsWs8unaYWVB0N3VFkEgl0BFyPObAtvwnHA6zywMvp
 EqJijXFG8VPcztE3eTIMbd0WOkxTjpMT6YHzpZqli/ENxCgu79OWELYrJ0/vC5Uj
 HK0qxgvIzUyJgmikkySDvd/Hc6HWItYonlcAht0VErNfTTfkMwWgRz1W4ZRB6bOJ
 7M83aytLyRYaPGEbwaoR
 =xOlP
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-20160324' of git://git.infradead.org/linux-mtd

Pull MTD updates from Brian Norris:
 "NAND:
   - Add sunxi_nand randomizer support
   - begin refactoring NAND ecclayout structs
   - fix pxa3xx_nand dmaengine usage
   - brcmnand: fix support for v7.1 controller
   - add Qualcomm NAND controller driver

  SPI NOR:
   - add new ls1021a, ls2080a support to Freescale QuadSPI
   - add new flash ID entries
   - support bottom-block protection for Winbond flash
   - support Status Register Write Protect
   - remove broken QPI support for Micron SPI flash

  JFFS2:
   - improve post-mount CRC scan efficiency

  General:
   - refactor bcm63xxpart parser, to later extend for NAND
   - add writebuf size parameter to mtdram

  Other minor code quality improvements"

* tag 'for-linus-20160324' of git://git.infradead.org/linux-mtd: (72 commits)
  mtd: nand: remove kerneldoc for removed function parameter
  mtd: nand: Qualcomm NAND controller driver
  dt/bindings: qcom_nandc: Add DT bindings
  mtd: nand: don't select chip in nand_chip's block_bad op
  mtd: spi-nor: support lock/unlock for a few Winbond chips
  mtd: spi-nor: add TB (Top/Bottom) protect support
  mtd: spi-nor: add SPI_NOR_HAS_LOCK flag
  mtd: spi-nor: use BIT() for flash_info flags
  mtd: spi-nor: disallow further writes to SR if WP# is low
  mtd: spi-nor: make lock/unlock bounds checks more obvious and robust
  mtd: spi-nor: silently drop lock/unlock for already locked/unlocked region
  mtd: spi-nor: wait for SR_WIP to clear on initial unlock
  mtd: nand: simplify nand_bch_init() usage
  mtd: mtdswap: remove useless if (!mtd->ecclayout) test
  mtd: create an mtd_oobavail() helper and make use of it
  mtd: kill the ecclayout->oobavail field
  mtd: nand: check status before reporting timeout
  mtd: bcm63xxpart: give width specifier an 'int', not 'size_t'
  mtd: mtdram: Add parameter for setting writebuf size
  mtd: nand: pxa3xx_nand: kill unused field 'drcmr_cmd'
  ...
This commit is contained in:
Linus Torvalds 2016-03-24 19:57:15 -07:00
commit 8f40842e42
61 changed files with 3522 additions and 590 deletions

View file

@ -1,7 +1,10 @@
Atmel NAND flash Atmel NAND flash
Required properties: Required properties:
- compatible : should be "atmel,at91rm9200-nand" or "atmel,sama5d4-nand". - compatible: The possible values are:
"atmel,at91rm9200-nand"
"atmel,sama5d2-nand"
"atmel,sama5d4-nand"
- reg : should specify localbus address and size used for the chip, - reg : should specify localbus address and size used for the chip,
and hardware ECC controller if available. and hardware ECC controller if available.
If the hardware ECC is PMECC, it should contain address and size for If the hardware ECC is PMECC, it should contain address and size for
@ -21,10 +24,11 @@ Optional properties:
- nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default. - nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default.
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
"soft_bch". "soft_bch".
- atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware. - atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware,
Only supported by at91sam9x5 or later sam9 product. capable of BCH encoding and decoding, on devices where it is present.
- atmel,pmecc-cap : error correct capability for Programmable Multibit ECC - atmel,pmecc-cap : error correct capability for Programmable Multibit ECC
Controller. Supported values are: 2, 4, 8, 12, 24. Controller. Supported values are: 2, 4, 8, 12, 24. If the compatible string
is "atmel,sama5d2-nand", 32 is also valid.
- atmel,pmecc-sector-size : sector size for ECC computation. Supported values - atmel,pmecc-sector-size : sector size for ECC computation. Supported values
are: 512, 1024. are: 512, 1024.
- atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM - atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM
@ -32,15 +36,16 @@ Optional properties:
sector size 1024. If not specified, driver will build the table in runtime. sector size 1024. If not specified, driver will build the table in runtime.
- nand-bus-width : 8 or 16 bus width if not present 8 - nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
- Nand Flash Controller(NFC) is a slave driver under Atmel nand flash
- Required properties: Nand Flash Controller(NFC) is an optional sub-node
- compatible : "atmel,sama5d3-nfc". Required properties:
- reg : should specify the address and size used for NFC command registers, - compatible : "atmel,sama5d3-nfc" or "atmel,sama5d4-nfc".
NFC registers and NFC Sram. NFC Sram address and size can be absent - reg : should specify the address and size used for NFC command registers,
if don't want to use it. NFC registers and NFC SRAM. NFC SRAM address and size can be absent
- clocks: phandle to the peripheral clock if don't want to use it.
- Optional properties: - clocks: phandle to the peripheral clock
- atmel,write-by-sram: boolean to enable NFC write by sram. Optional properties:
- atmel,write-by-sram: boolean to enable NFC write by SRAM.
Examples: Examples:
nand0: nand@40000000,0 { nand0: nand@40000000,0 {

View file

@ -3,7 +3,9 @@
Required properties: Required properties:
- compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-qspi", - compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-qspi",
"fsl,imx7d-qspi", "fsl,imx6ul-qspi", "fsl,imx7d-qspi", "fsl,imx6ul-qspi",
"fsl,ls1021-qspi" "fsl,ls1021a-qspi"
or
"fsl,ls2080a-qspi" followed by "fsl,ls1021a-qspi"
- reg : the first contains the register location and length, - reg : the first contains the register location and length,
the second contains the memory mapping address and length the second contains the memory mapping address and length
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
@ -19,6 +21,7 @@ Optional properties:
But if there are two NOR flashes connected to the But if there are two NOR flashes connected to the
bus, you should enable this property. bus, you should enable this property.
(Please check the board's schematic.) (Please check the board's schematic.)
- big-endian : That means the IP register is big endian
Example: Example:

View file

@ -0,0 +1,86 @@
* Qualcomm NAND controller
Required properties:
- compatible: should be "qcom,ipq806x-nand"
- reg: MMIO address range
- clocks: must contain core clock and always on clock
- clock-names: must contain "core" for the core clock and "aon" for the
always on clock
- dmas: DMA specifier, consisting of a phandle to the ADM DMA
controller node and the channel number to be used for
NAND. Refer to dma.txt and qcom_adm.txt for more details
- dma-names: must be "rxtx"
- qcom,cmd-crci: must contain the ADM command type CRCI block instance
number specified for the NAND controller on the given
platform
- qcom,data-crci: must contain the ADM data type CRCI block instance
number specified for the NAND controller on the given
platform
- #address-cells: <1> - subnodes give the chip-select number
- #size-cells: <0>
* NAND chip-select
Each controller may contain one or more subnodes to represent enabled
chip-selects which (may) contain NAND flash chips. Their properties are as
follows.
Required properties:
- compatible: should contain "qcom,nandcs"
- reg: a single integer representing the chip-select
number (e.g., 0, 1, 2, etc.)
- #address-cells: see partition.txt
- #size-cells: see partition.txt
- nand-ecc-strength: see nand.txt
- nand-ecc-step-size: must be 512. see nand.txt for more details.
Optional properties:
- nand-bus-width: see nand.txt
Each nandcs device node may optionally contain a 'partitions' sub-node, which
further contains sub-nodes describing the flash partition mapping. See
partition.txt for more detail.
Example:
nand@1ac00000 {
compatible = "qcom,ebi2-nandc";
reg = <0x1ac00000 0x800>;
clocks = <&gcc EBI2_CLK>,
<&gcc EBI2_AON_CLK>;
clock-names = "core", "aon";
dmas = <&adm_dma 3>;
dma-names = "rxtx";
qcom,cmd-crci = <15>;
qcom,data-crci = <3>;
#address-cells = <1>;
#size-cells = <0>;
nandcs@0 {
compatible = "qcom,nandcs";
reg = <0>;
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;
nand-bus-width = <8>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "boot-nand";
reg = <0 0x58a0000>;
};
partition@58a0000 {
label = "fs-nand";
reg = <0x58a0000 0x4000000>;
};
};
};
};

View file

@ -727,15 +727,6 @@ static int __init s3c_nand_copy_set(struct s3c2410_nand_set *set)
return -ENOMEM; return -ENOMEM;
} }
if (set->ecc_layout) {
ptr = kmemdup(set->ecc_layout,
sizeof(struct nand_ecclayout), GFP_KERNEL);
set->ecc_layout = ptr;
if (!ptr)
return -ENOMEM;
}
return 0; return 0;
} }

View file

@ -25,8 +25,6 @@ struct jz_nand_platform_data {
int num_partitions; int num_partitions;
struct mtd_partition *partitions; struct mtd_partition *partitions;
struct nand_ecclayout *ecc_layout;
unsigned char banks[JZ_NAND_NUM_BANKS]; unsigned char banks[JZ_NAND_NUM_BANKS];
void (*ident_callback)(struct platform_device *, struct nand_chip *, void (*ident_callback)(struct platform_device *, struct nand_chip *,

View file

@ -260,7 +260,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
/* get the Controller level irq */ /* get the Controller level irq */
fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_ifc_ctrl_dev->irq == NO_IRQ) { if (fsl_ifc_ctrl_dev->irq == 0) {
dev_err(&dev->dev, "failed to get irq resource " dev_err(&dev->dev, "failed to get irq resource "
"for IFC\n"); "for IFC\n");
ret = -ENODEV; ret = -ENODEV;

View file

@ -142,7 +142,7 @@ config MTD_AR7_PARTS
config MTD_BCM63XX_PARTS config MTD_BCM63XX_PARTS
tristate "BCM63XX CFE partitioning support" tristate "BCM63XX CFE partitioning support"
depends on BCM63XX depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
select CRC32 select CRC32
help help
This provides partions parsing for BCM63xx devices with CFE This provides partions parsing for BCM63xx devices with CFE

View file

@ -66,11 +66,13 @@ static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
{ {
uint32_t buf; uint32_t buf;
size_t bytes_read; size_t bytes_read;
int err;
if (mtd_read(master, offset, sizeof(buf), &bytes_read, err = mtd_read(master, offset, sizeof(buf), &bytes_read,
(uint8_t *)&buf) < 0) { (uint8_t *)&buf);
pr_err("mtd_read error while parsing (offset: 0x%X)!\n", if (err && !mtd_is_bitflip(err)) {
offset); pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
offset, err);
goto out_default; goto out_default;
} }
@ -95,6 +97,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
int trx_part = -1; int trx_part = -1;
int last_trx_part = -1; int last_trx_part = -1;
int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
int err;
/* /*
* Some really old flashes (like AT45DB*) had smaller erasesize-s, but * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
@ -118,8 +121,8 @@ static int bcm47xxpart_parse(struct mtd_info *master,
/* Parse block by block looking for magics */ /* Parse block by block looking for magics */
for (offset = 0; offset <= master->size - blocksize; for (offset = 0; offset <= master->size - blocksize;
offset += blocksize) { offset += blocksize) {
/* Nothing more in higher memory */ /* Nothing more in higher memory on BCM47XX (MIPS) */
if (offset >= 0x2000000) if (config_enabled(CONFIG_BCM47XX) && offset >= 0x2000000)
break; break;
if (curr_part >= BCM47XXPART_MAX_PARTS) { if (curr_part >= BCM47XXPART_MAX_PARTS) {
@ -128,10 +131,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
} }
/* Read beginning of the block */ /* Read beginning of the block */
if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
&bytes_read, (uint8_t *)buf) < 0) { &bytes_read, (uint8_t *)buf);
pr_err("mtd_read error while parsing (offset: 0x%X)!\n", if (err && !mtd_is_bitflip(err)) {
offset); pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
offset, err);
continue; continue;
} }
@ -254,10 +258,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
} }
/* Read middle of the block */ /* Read middle of the block */
if (mtd_read(master, offset + 0x8000, 0x4, err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read,
&bytes_read, (uint8_t *)buf) < 0) { (uint8_t *)buf);
pr_err("mtd_read error while parsing (offset: 0x%X)!\n", if (err && !mtd_is_bitflip(err)) {
offset); pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
offset, err);
continue; continue;
} }
@ -277,10 +282,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
} }
offset = master->size - possible_nvram_sizes[i]; offset = master->size - possible_nvram_sizes[i];
if (mtd_read(master, offset, 0x4, &bytes_read, err = mtd_read(master, offset, 0x4, &bytes_read,
(uint8_t *)buf) < 0) { (uint8_t *)buf);
pr_err("mtd_read error while reading at offset 0x%X!\n", if (err && !mtd_is_bitflip(err)) {
offset); pr_err("mtd_read error while reading (offset 0x%X): %d\n",
offset, err);
continue; continue;
} }

View file

@ -24,6 +24,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bcm963xx_nvram.h>
#include <linux/bcm963xx_tag.h> #include <linux/bcm963xx_tag.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/module.h> #include <linux/module.h>
@ -34,12 +35,15 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <asm/mach-bcm63xx/bcm63xx_nvram.h> #define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */
#include <asm/mach-bcm63xx/board_bcm963xx.h>
#define BCM63XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */ #define BCM963XX_CFE_MAGIC_OFFSET 0x4e0
#define BCM963XX_CFE_VERSION_OFFSET 0x570
#define BCM963XX_NVRAM_OFFSET 0x580
#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0 /* Ensure strings read from flash structs are null terminated */
#define STR_NULL_TERMINATE(x) \
do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
static int bcm63xx_detect_cfe(struct mtd_info *master) static int bcm63xx_detect_cfe(struct mtd_info *master)
{ {
@ -58,68 +62,130 @@ static int bcm63xx_detect_cfe(struct mtd_info *master)
return 0; return 0;
/* very old CFE's do not have the cfe-v string, so check for magic */ /* very old CFE's do not have the cfe-v string, so check for magic */
ret = mtd_read(master, BCM63XX_CFE_MAGIC_OFFSET, 8, &retlen, ret = mtd_read(master, BCM963XX_CFE_MAGIC_OFFSET, 8, &retlen,
(void *)buf); (void *)buf);
buf[retlen] = 0; buf[retlen] = 0;
return strncmp("CFE1CFE1", buf, 8); return strncmp("CFE1CFE1", buf, 8);
} }
static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, static int bcm63xx_read_nvram(struct mtd_info *master,
const struct mtd_partition **pparts, struct bcm963xx_nvram *nvram)
struct mtd_part_parser_data *data) {
u32 actual_crc, expected_crc;
size_t retlen;
int ret;
/* extract nvram data */
ret = mtd_read(master, BCM963XX_NVRAM_OFFSET, BCM963XX_NVRAM_V5_SIZE,
&retlen, (void *)nvram);
if (ret)
return ret;
ret = bcm963xx_nvram_checksum(nvram, &expected_crc, &actual_crc);
if (ret)
pr_warn("nvram checksum failed, contents may be invalid (expected %08x, got %08x)\n",
expected_crc, actual_crc);
if (!nvram->psi_size)
nvram->psi_size = BCM963XX_DEFAULT_PSI_SIZE;
return 0;
}
static int bcm63xx_read_image_tag(struct mtd_info *master, const char *name,
loff_t tag_offset, struct bcm_tag *buf)
{
int ret;
size_t retlen;
u32 computed_crc;
ret = mtd_read(master, tag_offset, sizeof(*buf), &retlen, (void *)buf);
if (ret)
return ret;
if (retlen != sizeof(*buf))
return -EIO;
computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
offsetof(struct bcm_tag, header_crc));
if (computed_crc == buf->header_crc) {
STR_NULL_TERMINATE(buf->board_id);
STR_NULL_TERMINATE(buf->tag_version);
pr_info("%s: CFE image tag found at 0x%llx with version %s, board type %s\n",
name, tag_offset, buf->tag_version, buf->board_id);
return 0;
}
pr_warn("%s: CFE image tag at 0x%llx CRC invalid (expected %08x, actual %08x)\n",
name, tag_offset, buf->header_crc, computed_crc);
return 1;
}
static int bcm63xx_parse_cfe_nor_partitions(struct mtd_info *master,
const struct mtd_partition **pparts, struct bcm963xx_nvram *nvram)
{ {
/* CFE, NVRAM and global Linux are always present */ /* CFE, NVRAM and global Linux are always present */
int nrparts = 3, curpart = 0; int nrparts = 3, curpart = 0;
struct bcm_tag *buf; struct bcm_tag *buf = NULL;
struct mtd_partition *parts; struct mtd_partition *parts;
int ret; int ret;
size_t retlen;
unsigned int rootfsaddr, kerneladdr, spareaddr; unsigned int rootfsaddr, kerneladdr, spareaddr;
unsigned int rootfslen, kernellen, sparelen, totallen; unsigned int rootfslen, kernellen, sparelen, totallen;
unsigned int cfelen, nvramlen; unsigned int cfelen, nvramlen;
unsigned int cfe_erasesize; unsigned int cfe_erasesize;
int i; int i;
u32 computed_crc;
bool rootfs_first = false; bool rootfs_first = false;
if (bcm63xx_detect_cfe(master))
return -EINVAL;
cfe_erasesize = max_t(uint32_t, master->erasesize, cfe_erasesize = max_t(uint32_t, master->erasesize,
BCM63XX_CFE_BLOCK_SIZE); BCM963XX_CFE_BLOCK_SIZE);
cfelen = cfe_erasesize; cfelen = cfe_erasesize;
nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K; nvramlen = nvram->psi_size * SZ_1K;
nvramlen = roundup(nvramlen, cfe_erasesize); nvramlen = roundup(nvramlen, cfe_erasesize);
/* Allocate memory for buffer */
buf = vmalloc(sizeof(struct bcm_tag)); buf = vmalloc(sizeof(struct bcm_tag));
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
/* Get the tag */ /* Get the tag */
ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen, ret = bcm63xx_read_image_tag(master, "rootfs", cfelen, buf);
(void *)buf); if (!ret) {
STR_NULL_TERMINATE(buf->flash_image_start);
if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) ||
rootfsaddr < BCM963XX_EXTENDED_SIZE) {
pr_err("invalid rootfs address: %*ph\n",
(int)sizeof(buf->flash_image_start),
buf->flash_image_start);
goto invalid_tag;
}
if (retlen != sizeof(struct bcm_tag)) { STR_NULL_TERMINATE(buf->kernel_address);
vfree(buf); if (kstrtouint(buf->kernel_address, 10, &kerneladdr) ||
return -EIO; kerneladdr < BCM963XX_EXTENDED_SIZE) {
} pr_err("invalid kernel address: %*ph\n",
(int)sizeof(buf->kernel_address),
buf->kernel_address);
goto invalid_tag;
}
computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf, STR_NULL_TERMINATE(buf->kernel_length);
offsetof(struct bcm_tag, header_crc)); if (kstrtouint(buf->kernel_length, 10, &kernellen)) {
if (computed_crc == buf->header_crc) { pr_err("invalid kernel length: %*ph\n",
char *boardid = &(buf->board_id[0]); (int)sizeof(buf->kernel_length),
char *tagversion = &(buf->tag_version[0]); buf->kernel_length);
goto invalid_tag;
}
sscanf(buf->flash_image_start, "%u", &rootfsaddr); STR_NULL_TERMINATE(buf->total_length);
sscanf(buf->kernel_address, "%u", &kerneladdr); if (kstrtouint(buf->total_length, 10, &totallen)) {
sscanf(buf->kernel_length, "%u", &kernellen); pr_err("invalid total length: %*ph\n",
sscanf(buf->total_length, "%u", &totallen); (int)sizeof(buf->total_length),
buf->total_length);
pr_info("CFE boot tag found with version %s and board type %s\n", goto invalid_tag;
tagversion, boardid); }
kerneladdr = kerneladdr - BCM963XX_EXTENDED_SIZE; kerneladdr = kerneladdr - BCM963XX_EXTENDED_SIZE;
rootfsaddr = rootfsaddr - BCM963XX_EXTENDED_SIZE; rootfsaddr = rootfsaddr - BCM963XX_EXTENDED_SIZE;
@ -134,13 +200,14 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
rootfsaddr = kerneladdr + kernellen; rootfsaddr = kerneladdr + kernellen;
rootfslen = spareaddr - rootfsaddr; rootfslen = spareaddr - rootfsaddr;
} }
} else { } else if (ret > 0) {
pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n", invalid_tag:
buf->header_crc, computed_crc);
kernellen = 0; kernellen = 0;
rootfslen = 0; rootfslen = 0;
rootfsaddr = 0; rootfsaddr = 0;
spareaddr = cfelen; spareaddr = cfelen;
} else {
goto out;
} }
sparelen = master->size - spareaddr - nvramlen; sparelen = master->size - spareaddr - nvramlen;
@ -151,11 +218,10 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
if (kernellen > 0) if (kernellen > 0)
nrparts++; nrparts++;
/* Ask kernel for more memory */
parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
if (!parts) { if (!parts) {
vfree(buf); ret = -ENOMEM;
return -ENOMEM; goto out;
} }
/* Start building partition list */ /* Start building partition list */
@ -206,9 +272,43 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
sparelen); sparelen);
*pparts = parts; *pparts = parts;
ret = 0;
out:
vfree(buf); vfree(buf);
if (ret)
return ret;
return nrparts; return nrparts;
}
static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct bcm963xx_nvram *nvram = NULL;
int ret;
if (bcm63xx_detect_cfe(master))
return -EINVAL;
nvram = vzalloc(sizeof(*nvram));
if (!nvram)
return -ENOMEM;
ret = bcm63xx_read_nvram(master, nvram);
if (ret)
goto out;
if (!mtd_type_is_nand(master))
ret = bcm63xx_parse_cfe_nor_partitions(master, pparts, nvram);
else
ret = -EINVAL;
out:
vfree(nvram);
return ret;
}; };
static struct mtd_part_parser bcm63xx_cfe_parser = { static struct mtd_part_parser bcm63xx_cfe_parser = {

View file

@ -72,13 +72,11 @@ MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, "
* @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC) * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC)
* @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC) * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC)
* @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15 * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15
* @oobavail: 8 available bytes remaining after ECC toll
*/ */
static struct nand_ecclayout docg3_oobinfo = { static struct nand_ecclayout docg3_oobinfo = {
.eccbytes = 8, .eccbytes = 8,
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14}, .eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
.oobfree = {{0, 7}, {15, 1} }, .oobfree = {{0, 7}, {15, 1} },
.oobavail = 8,
}; };
static inline u8 doc_readb(struct docg3 *docg3, u16 reg) static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
@ -1438,7 +1436,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
oobdelta = mtd->oobsize; oobdelta = mtd->oobsize;
break; break;
case MTD_OPS_AUTO_OOB: case MTD_OPS_AUTO_OOB:
oobdelta = mtd->ecclayout->oobavail; oobdelta = mtd->oobavail;
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -1860,6 +1858,7 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->_write_oob = doc_write_oob; mtd->_write_oob = doc_write_oob;
mtd->_block_isbad = doc_block_isbad; mtd->_block_isbad = doc_block_isbad;
mtd->ecclayout = &docg3_oobinfo; mtd->ecclayout = &docg3_oobinfo;
mtd->oobavail = 8;
mtd->ecc_strength = DOC_ECC_BCH_T; mtd->ecc_strength = DOC_ECC_BCH_T;
return 0; return 0;

View file

@ -19,6 +19,7 @@
static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE;
static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE;
static unsigned long writebuf_size = 64;
#define MTDRAM_TOTAL_SIZE (total_size * 1024) #define MTDRAM_TOTAL_SIZE (total_size * 1024)
#define MTDRAM_ERASE_SIZE (erase_size * 1024) #define MTDRAM_ERASE_SIZE (erase_size * 1024)
@ -27,6 +28,8 @@ module_param(total_size, ulong, 0);
MODULE_PARM_DESC(total_size, "Total device size in KiB"); MODULE_PARM_DESC(total_size, "Total device size in KiB");
module_param(erase_size, ulong, 0); module_param(erase_size, ulong, 0);
MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
module_param(writebuf_size, ulong, 0);
MODULE_PARM_DESC(writebuf_size, "Device write buf size in Bytes (Default: 64)");
#endif #endif
// We could store these in the mtd structure, but we only support 1 device.. // We could store these in the mtd structure, but we only support 1 device..
@ -123,7 +126,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
mtd->flags = MTD_CAP_RAM; mtd->flags = MTD_CAP_RAM;
mtd->size = size; mtd->size = size;
mtd->writesize = 1; mtd->writesize = 1;
mtd->writebufsize = 64; /* Mimic CFI NOR flashes */ mtd->writebufsize = writebuf_size;
mtd->erasesize = MTDRAM_ERASE_SIZE; mtd->erasesize = MTDRAM_ERASE_SIZE;
mtd->priv = mapped_address; mtd->priv = mapped_address;

View file

@ -126,10 +126,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
if (ops->oobbuf) { if (ops->oobbuf) {
size_t len, pages; size_t len, pages;
if (ops->mode == MTD_OPS_AUTO_OOB) len = mtd_oobavail(mtd, ops);
len = mtd->oobavail;
else
len = mtd->oobsize;
pages = mtd_div_by_ws(mtd->size, mtd); pages = mtd_div_by_ws(mtd->size, mtd);
pages -= mtd_div_by_ws(from, mtd); pages -= mtd_div_by_ws(from, mtd);
if (ops->ooboffs + ops->ooblen > pages * len) if (ops->ooboffs + ops->ooblen > pages * len)

View file

@ -346,7 +346,7 @@ static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
if (mtd_can_have_bb(d->mtd) && mtd_block_isbad(d->mtd, offset)) if (mtd_can_have_bb(d->mtd) && mtd_block_isbad(d->mtd, offset))
return MTDSWAP_SCANNED_BAD; return MTDSWAP_SCANNED_BAD;
ops.ooblen = 2 * d->mtd->ecclayout->oobavail; ops.ooblen = 2 * d->mtd->oobavail;
ops.oobbuf = d->oob_buf; ops.oobbuf = d->oob_buf;
ops.ooboffs = 0; ops.ooboffs = 0;
ops.datbuf = NULL; ops.datbuf = NULL;
@ -359,7 +359,7 @@ static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
data = (struct mtdswap_oobdata *)d->oob_buf; data = (struct mtdswap_oobdata *)d->oob_buf;
data2 = (struct mtdswap_oobdata *) data2 = (struct mtdswap_oobdata *)
(d->oob_buf + d->mtd->ecclayout->oobavail); (d->oob_buf + d->mtd->oobavail);
if (le16_to_cpu(data->magic) == MTDSWAP_MAGIC_CLEAN) { if (le16_to_cpu(data->magic) == MTDSWAP_MAGIC_CLEAN) {
eb->erase_count = le32_to_cpu(data->count); eb->erase_count = le32_to_cpu(data->count);
@ -933,7 +933,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
ops.mode = MTD_OPS_AUTO_OOB; ops.mode = MTD_OPS_AUTO_OOB;
ops.len = mtd->writesize; ops.len = mtd->writesize;
ops.ooblen = mtd->ecclayout->oobavail; ops.ooblen = mtd->oobavail;
ops.ooboffs = 0; ops.ooboffs = 0;
ops.datbuf = d->page_buf; ops.datbuf = d->page_buf;
ops.oobbuf = d->oob_buf; ops.oobbuf = d->oob_buf;
@ -945,7 +945,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
for (i = 0; i < mtd_pages; i++) { for (i = 0; i < mtd_pages; i++) {
patt = mtdswap_test_patt(test + i); patt = mtdswap_test_patt(test + i);
memset(d->page_buf, patt, mtd->writesize); memset(d->page_buf, patt, mtd->writesize);
memset(d->oob_buf, patt, mtd->ecclayout->oobavail); memset(d->oob_buf, patt, mtd->oobavail);
ret = mtd_write_oob(mtd, pos, &ops); ret = mtd_write_oob(mtd, pos, &ops);
if (ret) if (ret)
goto error; goto error;
@ -964,7 +964,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
if (p1[j] != patt) if (p1[j] != patt)
goto error; goto error;
for (j = 0; j < mtd->ecclayout->oobavail; j++) for (j = 0; j < mtd->oobavail; j++)
if (p2[j] != (unsigned char)patt) if (p2[j] != (unsigned char)patt)
goto error; goto error;
@ -1387,7 +1387,7 @@ static int mtdswap_init(struct mtdswap_dev *d, unsigned int eblocks,
if (!d->page_buf) if (!d->page_buf)
goto page_buf_fail; goto page_buf_fail;
d->oob_buf = kmalloc(2 * mtd->ecclayout->oobavail, GFP_KERNEL); d->oob_buf = kmalloc(2 * mtd->oobavail, GFP_KERNEL);
if (!d->oob_buf) if (!d->oob_buf)
goto oob_buf_fail; goto oob_buf_fail;
@ -1417,7 +1417,6 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
unsigned long part; unsigned long part;
unsigned int eblocks, eavailable, bad_blocks, spare_cnt; unsigned int eblocks, eavailable, bad_blocks, spare_cnt;
uint64_t swap_size, use_size, size_limit; uint64_t swap_size, use_size, size_limit;
struct nand_ecclayout *oinfo;
int ret; int ret;
parts = &partitions[0]; parts = &partitions[0];
@ -1447,17 +1446,10 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
return; return;
} }
oinfo = mtd->ecclayout; if (!mtd->oobsize || mtd->oobavail < MTDSWAP_OOBSIZE) {
if (!oinfo) {
printk(KERN_ERR "%s: mtd%d does not have OOB\n",
MTDSWAP_PREFIX, mtd->index);
return;
}
if (!mtd->oobsize || oinfo->oobavail < MTDSWAP_OOBSIZE) {
printk(KERN_ERR "%s: Not enough free bytes in OOB, " printk(KERN_ERR "%s: Not enough free bytes in OOB, "
"%d available, %zu needed.\n", "%d available, %zu needed.\n",
MTDSWAP_PREFIX, oinfo->oobavail, MTDSWAP_OOBSIZE); MTDSWAP_PREFIX, mtd->oobavail, MTDSWAP_OOBSIZE);
return; return;
} }

View file

@ -74,6 +74,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
config MTD_NAND_GPIO config MTD_NAND_GPIO
tristate "GPIO assisted NAND Flash driver" tristate "GPIO assisted NAND Flash driver"
depends on GPIOLIB || COMPILE_TEST depends on GPIOLIB || COMPILE_TEST
depends on HAS_IOMEM
help help
This enables a NAND flash driver where control signals are This enables a NAND flash driver where control signals are
connected to GPIO pins, and commands and data are communicated connected to GPIO pins, and commands and data are communicated
@ -310,6 +311,7 @@ config MTD_NAND_CAFE
config MTD_NAND_CS553X config MTD_NAND_CS553X
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
depends on X86_32 depends on X86_32
depends on !UML && HAS_IOMEM
help help
The CS553x companion chips for the AMD Geode processor The CS553x companion chips for the AMD Geode processor
include NAND flash controllers with built-in hardware ECC include NAND flash controllers with built-in hardware ECC
@ -463,6 +465,7 @@ config MTD_NAND_MPC5121_NFC
config MTD_NAND_VF610_NFC config MTD_NAND_VF610_NFC
tristate "Support for Freescale NFC for VF610/MPC5125" tristate "Support for Freescale NFC for VF610/MPC5125"
depends on (SOC_VF610 || COMPILE_TEST) depends on (SOC_VF610 || COMPILE_TEST)
depends on HAS_IOMEM
help help
Enables support for NAND Flash Controller on some Freescale Enables support for NAND Flash Controller on some Freescale
processors like the VF610, MPC5125, MCF54418 or Kinetis K70. processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
@ -553,4 +556,11 @@ config MTD_NAND_HISI504
help help
Enables support for NAND controller on Hisilicon SoC Hip04. Enables support for NAND controller on Hisilicon SoC Hip04.
config MTD_NAND_QCOM
tristate "Support for NAND on QCOM SoCs"
depends on ARCH_QCOM
help
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
controller. This controller is found on IPQ806x SoC.
endif # MTD_NAND endif # MTD_NAND

View file

@ -56,5 +56,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand-objs := nand_base.o nand_bbt.o nand_timings.o

View file

@ -65,6 +65,11 @@ module_param(on_flash_bbt, int, 0);
struct atmel_nand_caps { struct atmel_nand_caps {
bool pmecc_correct_erase_page; bool pmecc_correct_erase_page;
uint8_t pmecc_max_correction;
};
struct atmel_nand_nfc_caps {
uint32_t rb_mask;
}; };
/* oob layout for large page size /* oob layout for large page size
@ -111,6 +116,7 @@ struct atmel_nfc {
/* Point to the sram bank which include readed data via NFC */ /* Point to the sram bank which include readed data via NFC */
void *data_in_sram; void *data_in_sram;
bool will_write_sram; bool will_write_sram;
const struct atmel_nand_nfc_caps *caps;
}; };
static struct atmel_nfc nand_nfc; static struct atmel_nfc nand_nfc;
@ -140,6 +146,7 @@ struct atmel_nand_host {
int pmecc_cw_len; /* Length of codeword */ int pmecc_cw_len; /* Length of codeword */
void __iomem *pmerrloc_base; void __iomem *pmerrloc_base;
void __iomem *pmerrloc_el_base;
void __iomem *pmecc_rom_base; void __iomem *pmecc_rom_base;
/* lookup table for alpha_to and index_of */ /* lookup table for alpha_to and index_of */
@ -468,6 +475,7 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
* 8-bits 13-bytes 14-bytes * 8-bits 13-bytes 14-bytes
* 12-bits 20-bytes 21-bytes * 12-bits 20-bytes 21-bytes
* 24-bits 39-bytes 42-bytes * 24-bits 39-bytes 42-bytes
* 32-bits 52-bytes 56-bytes
*/ */
static int pmecc_get_ecc_bytes(int cap, int sector_size) static int pmecc_get_ecc_bytes(int cap, int sector_size)
{ {
@ -813,7 +821,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
sector_size = host->pmecc_sector_size; sector_size = host->pmecc_sector_size;
while (err_nbr) { while (err_nbr) {
tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1; tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_el_base, i) - 1;
byte_pos = tmp / 8; byte_pos = tmp / 8;
bit_pos = tmp % 8; bit_pos = tmp % 8;
@ -825,7 +833,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
*(buf + byte_pos) ^= (1 << bit_pos); *(buf + byte_pos) ^= (1 << bit_pos);
pos = sector_num * host->pmecc_sector_size + byte_pos; pos = sector_num * host->pmecc_sector_size + byte_pos;
dev_info(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
pos, bit_pos, err_byte, *(buf + byte_pos)); pos, bit_pos, err_byte, *(buf + byte_pos));
} else { } else {
/* Bit flip in OOB area */ /* Bit flip in OOB area */
@ -835,7 +843,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
ecc[tmp] ^= (1 << bit_pos); ecc[tmp] ^= (1 << bit_pos);
pos = tmp + nand_chip->ecc.layout->eccpos[0]; pos = tmp + nand_chip->ecc.layout->eccpos[0];
dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
pos, bit_pos, err_byte, ecc[tmp]); pos, bit_pos, err_byte, ecc[tmp]);
} }
@ -1017,6 +1025,9 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
case 24: case 24:
val = PMECC_CFG_BCH_ERR24; val = PMECC_CFG_BCH_ERR24;
break; break;
case 32:
val = PMECC_CFG_BCH_ERR32;
break;
} }
if (host->pmecc_sector_size == 512) if (host->pmecc_sector_size == 512)
@ -1078,6 +1089,9 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
/* If device tree doesn't specify, use NAND's minimum ECC parameters */ /* If device tree doesn't specify, use NAND's minimum ECC parameters */
if (host->pmecc_corr_cap == 0) { if (host->pmecc_corr_cap == 0) {
if (*cap > host->caps->pmecc_max_correction)
return -EINVAL;
/* use the most fitable ecc bits (the near bigger one ) */ /* use the most fitable ecc bits (the near bigger one ) */
if (*cap <= 2) if (*cap <= 2)
host->pmecc_corr_cap = 2; host->pmecc_corr_cap = 2;
@ -1089,6 +1103,8 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
host->pmecc_corr_cap = 12; host->pmecc_corr_cap = 12;
else if (*cap <= 24) else if (*cap <= 24)
host->pmecc_corr_cap = 24; host->pmecc_corr_cap = 24;
else if (*cap <= 32)
host->pmecc_corr_cap = 32;
else else
return -EINVAL; return -EINVAL;
} }
@ -1205,6 +1221,8 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
err_no = PTR_ERR(host->pmerrloc_base); err_no = PTR_ERR(host->pmerrloc_base);
goto err; goto err;
} }
host->pmerrloc_el_base = host->pmerrloc_base + ATMEL_PMERRLOC_SIGMAx +
(host->caps->pmecc_max_correction + 1) * 4;
if (!host->has_no_lookup_table) { if (!host->has_no_lookup_table) {
regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
@ -1486,8 +1504,6 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
ecc_writel(host->ecc, CR, ATMEL_ECC_RST); ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
} }
static const struct of_device_id atmel_nand_dt_ids[];
static int atmel_of_init_port(struct atmel_nand_host *host, static int atmel_of_init_port(struct atmel_nand_host *host,
struct device_node *np) struct device_node *np)
{ {
@ -1498,7 +1514,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
enum of_gpio_flags flags = 0; enum of_gpio_flags flags = 0;
host->caps = (struct atmel_nand_caps *) host->caps = (struct atmel_nand_caps *)
of_match_device(atmel_nand_dt_ids, host->dev)->data; of_device_get_match_data(host->dev);
if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
if (val >= 32) { if (val >= 32) {
@ -1547,10 +1563,16 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
* them from NAND ONFI parameters. * them from NAND ONFI parameters.
*/ */
if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) { if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) {
if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && if (val > host->caps->pmecc_max_correction) {
(val != 24)) {
dev_err(host->dev, dev_err(host->dev,
"Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", "Required ECC strength too high: %u max %u\n",
val, host->caps->pmecc_max_correction);
return -EINVAL;
}
if ((val != 2) && (val != 4) && (val != 8) &&
(val != 12) && (val != 24) && (val != 32)) {
dev_err(host->dev,
"Required ECC strength not supported: %u\n",
val); val);
return -EINVAL; return -EINVAL;
} }
@ -1560,7 +1582,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) { if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) {
if ((val != 512) && (val != 1024)) { if ((val != 512) && (val != 1024)) {
dev_err(host->dev, dev_err(host->dev,
"Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", "Required ECC sector size not supported: %u\n",
val); val);
return -EINVAL; return -EINVAL;
} }
@ -1677,9 +1699,9 @@ static irqreturn_t hsmc_interrupt(int irq, void *dev_id)
nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE); nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
} }
if (pending & NFC_SR_RB_EDGE) { if (pending & host->nfc->caps->rb_mask) {
complete(&host->nfc->comp_ready); complete(&host->nfc->comp_ready);
nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE); nfc_writel(host->nfc->hsmc_regs, IDR, host->nfc->caps->rb_mask);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
} }
if (pending & NFC_SR_CMD_DONE) { if (pending & NFC_SR_CMD_DONE) {
@ -1697,7 +1719,7 @@ static void nfc_prepare_interrupt(struct atmel_nand_host *host, u32 flag)
if (flag & NFC_SR_XFR_DONE) if (flag & NFC_SR_XFR_DONE)
init_completion(&host->nfc->comp_xfer_done); init_completion(&host->nfc->comp_xfer_done);
if (flag & NFC_SR_RB_EDGE) if (flag & host->nfc->caps->rb_mask)
init_completion(&host->nfc->comp_ready); init_completion(&host->nfc->comp_ready);
if (flag & NFC_SR_CMD_DONE) if (flag & NFC_SR_CMD_DONE)
@ -1715,7 +1737,7 @@ static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
if (flag & NFC_SR_XFR_DONE) if (flag & NFC_SR_XFR_DONE)
comp[index++] = &host->nfc->comp_xfer_done; comp[index++] = &host->nfc->comp_xfer_done;
if (flag & NFC_SR_RB_EDGE) if (flag & host->nfc->caps->rb_mask)
comp[index++] = &host->nfc->comp_ready; comp[index++] = &host->nfc->comp_ready;
if (flag & NFC_SR_CMD_DONE) if (flag & NFC_SR_CMD_DONE)
@ -1783,7 +1805,7 @@ static int nfc_device_ready(struct mtd_info *mtd)
dev_err(host->dev, "Lost the interrupt flags: 0x%08x\n", dev_err(host->dev, "Lost the interrupt flags: 0x%08x\n",
mask & status); mask & status);
return status & NFC_SR_RB_EDGE; return status & host->nfc->caps->rb_mask;
} }
static void nfc_select_chip(struct mtd_info *mtd, int chip) static void nfc_select_chip(struct mtd_info *mtd, int chip)
@ -1956,8 +1978,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
} }
/* fall through */ /* fall through */
default: default:
nfc_prepare_interrupt(host, NFC_SR_RB_EDGE); nfc_prepare_interrupt(host, host->nfc->caps->rb_mask);
nfc_wait_interrupt(host, NFC_SR_RB_EDGE); nfc_wait_interrupt(host, host->nfc->caps->rb_mask);
} }
} }
@ -2304,17 +2326,34 @@ static int atmel_nand_remove(struct platform_device *pdev)
return 0; return 0;
} }
/*
* AT91RM9200 does not have PMECC or PMECC Errloc peripherals for
* BCH ECC. Combined with the "atmel,has-pmecc", it is used to describe
* devices from the SAM9 family that have those.
*/
static const struct atmel_nand_caps at91rm9200_caps = { static const struct atmel_nand_caps at91rm9200_caps = {
.pmecc_correct_erase_page = false, .pmecc_correct_erase_page = false,
.pmecc_max_correction = 24,
}; };
static const struct atmel_nand_caps sama5d4_caps = { static const struct atmel_nand_caps sama5d4_caps = {
.pmecc_correct_erase_page = true, .pmecc_correct_erase_page = true,
.pmecc_max_correction = 24,
};
/*
* The PMECC Errloc controller starting in SAMA5D2 is not compatible,
* as the increased correction strength requires more registers.
*/
static const struct atmel_nand_caps sama5d2_caps = {
.pmecc_correct_erase_page = true,
.pmecc_max_correction = 32,
}; };
static const struct of_device_id atmel_nand_dt_ids[] = { static const struct of_device_id atmel_nand_dt_ids[] = {
{ .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps }, { .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps },
{ .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps }, { .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps },
{ .compatible = "atmel,sama5d2-nand", .data = &sama5d2_caps },
{ /* sentinel */ } { /* sentinel */ }
}; };
@ -2354,6 +2393,11 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
} }
} }
nfc->caps = (const struct atmel_nand_nfc_caps *)
of_device_get_match_data(&pdev->dev);
if (!nfc->caps)
return -ENODEV;
nfc_writel(nfc->hsmc_regs, IDR, 0xffffffff); nfc_writel(nfc->hsmc_regs, IDR, 0xffffffff);
nfc_readl(nfc->hsmc_regs, SR); /* clear the NFC_SR */ nfc_readl(nfc->hsmc_regs, SR); /* clear the NFC_SR */
@ -2382,8 +2426,17 @@ static int atmel_nand_nfc_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct atmel_nand_nfc_caps sama5d3_nfc_caps = {
.rb_mask = NFC_SR_RB_EDGE0,
};
static const struct atmel_nand_nfc_caps sama5d4_nfc_caps = {
.rb_mask = NFC_SR_RB_EDGE3,
};
static const struct of_device_id atmel_nand_nfc_match[] = { static const struct of_device_id atmel_nand_nfc_match[] = {
{ .compatible = "atmel,sama5d3-nfc" }, { .compatible = "atmel,sama5d3-nfc", .data = &sama5d3_nfc_caps },
{ .compatible = "atmel,sama5d4-nfc", .data = &sama5d4_nfc_caps },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, atmel_nand_nfc_match); MODULE_DEVICE_TABLE(of, atmel_nand_nfc_match);

View file

@ -43,6 +43,7 @@
#define PMECC_CFG_BCH_ERR8 (2 << 0) #define PMECC_CFG_BCH_ERR8 (2 << 0)
#define PMECC_CFG_BCH_ERR12 (3 << 0) #define PMECC_CFG_BCH_ERR12 (3 << 0)
#define PMECC_CFG_BCH_ERR24 (4 << 0) #define PMECC_CFG_BCH_ERR24 (4 << 0)
#define PMECC_CFG_BCH_ERR32 (5 << 0)
#define PMECC_CFG_SECTOR512 (0 << 4) #define PMECC_CFG_SECTOR512 (0 << 4)
#define PMECC_CFG_SECTOR1024 (1 << 4) #define PMECC_CFG_SECTOR1024 (1 << 4)
@ -108,7 +109,11 @@
#define PMERRLOC_ERR_NUM_MASK (0x1f << 8) #define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
#define PMERRLOC_CALC_DONE (1 << 0) #define PMERRLOC_CALC_DONE (1 << 0)
#define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */ #define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */
#define ATMEL_PMERRLOC_ELx 0x08c /* Error location x */
/*
* The ATMEL_PMERRLOC_ELx register location depends from the number of
* bits corrected by the PMECC controller. Do not use it.
*/
/* Register access macros for PMECC */ /* Register access macros for PMECC */
#define pmecc_readl_relaxed(addr, reg) \ #define pmecc_readl_relaxed(addr, reg) \
@ -136,7 +141,7 @@
readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4)) readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
#define pmerrloc_readl_el_relaxed(addr, n) \ #define pmerrloc_readl_el_relaxed(addr, n) \
readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4)) readl_relaxed((addr) + ((n) * 4))
/* Galois field dimension */ /* Galois field dimension */
#define PMECC_GF_DIMENSION_13 13 #define PMECC_GF_DIMENSION_13 13

View file

@ -42,7 +42,8 @@
#define NFC_SR_UNDEF (1 << 21) #define NFC_SR_UNDEF (1 << 21)
#define NFC_SR_AWB (1 << 22) #define NFC_SR_AWB (1 << 22)
#define NFC_SR_ASE (1 << 23) #define NFC_SR_ASE (1 << 23)
#define NFC_SR_RB_EDGE (1 << 24) #define NFC_SR_RB_EDGE0 (1 << 24)
#define NFC_SR_RB_EDGE3 (1 << 27)
#define ATMEL_HSMC_NFC_IER 0x0c #define ATMEL_HSMC_NFC_IER 0x0c
#define ATMEL_HSMC_NFC_IDR 0x10 #define ATMEL_HSMC_NFC_IDR 0x10

View file

@ -311,6 +311,36 @@ static const u16 brcmnand_regs_v60[] = {
[BRCMNAND_FC_BASE] = 0x400, [BRCMNAND_FC_BASE] = 0x400,
}; };
/* BRCMNAND v7.1 */
static const u16 brcmnand_regs_v71[] = {
[BRCMNAND_CMD_START] = 0x04,
[BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
[BRCMNAND_CMD_ADDRESS] = 0x0c,
[BRCMNAND_INTFC_STATUS] = 0x14,
[BRCMNAND_CS_SELECT] = 0x18,
[BRCMNAND_CS_XOR] = 0x1c,
[BRCMNAND_LL_OP] = 0x20,
[BRCMNAND_CS0_BASE] = 0x50,
[BRCMNAND_CS1_BASE] = 0,
[BRCMNAND_CORR_THRESHOLD] = 0xdc,
[BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
[BRCMNAND_UNCORR_COUNT] = 0xfc,
[BRCMNAND_CORR_COUNT] = 0x100,
[BRCMNAND_CORR_EXT_ADDR] = 0x10c,
[BRCMNAND_CORR_ADDR] = 0x110,
[BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
[BRCMNAND_UNCORR_ADDR] = 0x118,
[BRCMNAND_SEMAPHORE] = 0x150,
[BRCMNAND_ID] = 0x194,
[BRCMNAND_ID_EXT] = 0x198,
[BRCMNAND_LL_RDATA] = 0x19c,
[BRCMNAND_OOB_READ_BASE] = 0x200,
[BRCMNAND_OOB_READ_10_BASE] = 0,
[BRCMNAND_OOB_WRITE_BASE] = 0x280,
[BRCMNAND_OOB_WRITE_10_BASE] = 0,
[BRCMNAND_FC_BASE] = 0x400,
};
enum brcmnand_cs_reg { enum brcmnand_cs_reg {
BRCMNAND_CS_CFG_EXT = 0, BRCMNAND_CS_CFG_EXT = 0,
BRCMNAND_CS_CFG, BRCMNAND_CS_CFG,
@ -406,7 +436,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
} }
/* Register offsets */ /* Register offsets */
if (ctrl->nand_version >= 0x0600) if (ctrl->nand_version >= 0x0701)
ctrl->reg_offsets = brcmnand_regs_v71;
else if (ctrl->nand_version >= 0x0600)
ctrl->reg_offsets = brcmnand_regs_v60; ctrl->reg_offsets = brcmnand_regs_v60;
else if (ctrl->nand_version >= 0x0500) else if (ctrl->nand_version >= 0x0500)
ctrl->reg_offsets = brcmnand_regs_v50; ctrl->reg_offsets = brcmnand_regs_v50;
@ -796,7 +828,8 @@ static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
break; break;
} }
goto out;
return layout;
} }
/* /*
@ -847,10 +880,7 @@ static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
break; break;
} }
out:
/* Sum available OOB */
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++)
layout->oobavail += layout->oobfree[i].length;
return layout; return layout;
} }

View file

@ -537,7 +537,7 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
return 0; return 0;
} }
static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
{ {
return 0; return 0;
} }

View file

@ -794,7 +794,7 @@ static int doc200x_dev_ready(struct mtd_info *mtd)
} }
} }
static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs)
{ {
/* This is our last resort if we couldn't find or create a BBT. Just /* This is our last resort if we couldn't find or create a BBT. Just
pretend all blocks are good. */ pretend all blocks are good. */

View file

@ -225,7 +225,6 @@ struct docg4_priv {
static struct nand_ecclayout docg4_oobinfo = { static struct nand_ecclayout docg4_oobinfo = {
.eccbytes = 9, .eccbytes = 9,
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
.oobavail = 5,
.oobfree = { {.offset = 2, .length = 5} } .oobfree = { {.offset = 2, .length = 5} }
}; };
@ -1121,7 +1120,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
return ret; return ret;
} }
static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip) static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs)
{ {
/* only called when module_param ignore_badblocks is set */ /* only called when module_param ignore_badblocks is set */
return 0; return 0;

View file

@ -1,7 +1,7 @@
/* /*
* Freescale GPMI NAND Flash Driver * Freescale GPMI NAND Flash Driver
* *
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc. * Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Embedded Alley Solutions, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -136,7 +136,7 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
* *
* We may have available oob space in this case. * We may have available oob space in this case.
*/ */
static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
{ {
struct bch_geometry *geo = &this->bch_geometry; struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand; struct nand_chip *chip = &this->nand;
@ -145,7 +145,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
unsigned int block_mark_bit_offset; unsigned int block_mark_bit_offset;
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
return false; return -EINVAL;
switch (chip->ecc_step_ds) { switch (chip->ecc_step_ds) {
case SZ_512: case SZ_512:
@ -158,19 +158,19 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
dev_err(this->dev, dev_err(this->dev,
"unsupported nand chip. ecc bits : %d, ecc size : %d\n", "unsupported nand chip. ecc bits : %d, ecc size : %d\n",
chip->ecc_strength_ds, chip->ecc_step_ds); chip->ecc_strength_ds, chip->ecc_step_ds);
return false; return -EINVAL;
} }
geo->ecc_chunk_size = chip->ecc_step_ds; geo->ecc_chunk_size = chip->ecc_step_ds;
geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
if (!gpmi_check_ecc(this)) if (!gpmi_check_ecc(this))
return false; return -EINVAL;
/* Keep the C >= O */ /* Keep the C >= O */
if (geo->ecc_chunk_size < mtd->oobsize) { if (geo->ecc_chunk_size < mtd->oobsize) {
dev_err(this->dev, dev_err(this->dev,
"unsupported nand chip. ecc size: %d, oob size : %d\n", "unsupported nand chip. ecc size: %d, oob size : %d\n",
chip->ecc_step_ds, mtd->oobsize); chip->ecc_step_ds, mtd->oobsize);
return false; return -EINVAL;
} }
/* The default value, see comment in the legacy_set_geometry(). */ /* The default value, see comment in the legacy_set_geometry(). */
@ -242,7 +242,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
+ ALIGN(geo->ecc_chunk_count, 4); + ALIGN(geo->ecc_chunk_count, 4);
if (!this->swap_block_mark) if (!this->swap_block_mark)
return true; return 0;
/* For bit swap. */ /* For bit swap. */
block_mark_bit_offset = mtd->writesize * 8 - block_mark_bit_offset = mtd->writesize * 8 -
@ -251,7 +251,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
geo->block_mark_byte_offset = block_mark_bit_offset / 8; geo->block_mark_byte_offset = block_mark_bit_offset / 8;
geo->block_mark_bit_offset = block_mark_bit_offset % 8; geo->block_mark_bit_offset = block_mark_bit_offset % 8;
return true; return 0;
} }
static int legacy_set_geometry(struct gpmi_nand_data *this) static int legacy_set_geometry(struct gpmi_nand_data *this)
@ -285,7 +285,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
geo->ecc_strength = get_ecc_strength(this); geo->ecc_strength = get_ecc_strength(this);
if (!gpmi_check_ecc(this)) { if (!gpmi_check_ecc(this)) {
dev_err(this->dev, dev_err(this->dev,
"required ecc strength of the NAND chip: %d is not supported by the GPMI controller (%d)\n", "ecc strength: %d cannot be supported by the controller (%d)\n"
"try to use minimum ecc strength that NAND chip required\n",
geo->ecc_strength, geo->ecc_strength,
this->devdata->bch_max_ecc_strength); this->devdata->bch_max_ecc_strength);
return -EINVAL; return -EINVAL;
@ -366,10 +367,11 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
int common_nfc_set_geometry(struct gpmi_nand_data *this) int common_nfc_set_geometry(struct gpmi_nand_data *this)
{ {
if (of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc") if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
&& set_geometry_by_ecc_info(this)) || legacy_set_geometry(this))
return 0; return set_geometry_by_ecc_info(this);
return legacy_set_geometry(this);
return 0;
} }
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
@ -2033,9 +2035,54 @@ static int gpmi_nand_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int gpmi_pm_suspend(struct device *dev)
{
struct gpmi_nand_data *this = dev_get_drvdata(dev);
release_dma_channels(this);
return 0;
}
static int gpmi_pm_resume(struct device *dev)
{
struct gpmi_nand_data *this = dev_get_drvdata(dev);
int ret;
ret = acquire_dma_channels(this);
if (ret < 0)
return ret;
/* re-init the GPMI registers */
this->flags &= ~GPMI_TIMING_INIT_OK;
ret = gpmi_init(this);
if (ret) {
dev_err(this->dev, "Error setting GPMI : %d\n", ret);
return ret;
}
/* re-init the BCH registers */
ret = bch_set_geometry(this);
if (ret) {
dev_err(this->dev, "Error setting BCH : %d\n", ret);
return ret;
}
/* re-init others */
gpmi_extra_init(this);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops gpmi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume)
};
static struct platform_driver gpmi_nand_driver = { static struct platform_driver gpmi_nand_driver = {
.driver = { .driver = {
.name = "gpmi-nand", .name = "gpmi-nand",
.pm = &gpmi_pm_ops,
.of_match_table = gpmi_nand_id_table, .of_match_table = gpmi_nand_id_table,
}, },
.probe = gpmi_nand_probe, .probe = gpmi_nand_probe,

View file

@ -632,7 +632,6 @@ static void hisi_nfc_host_init(struct hinfc_host *host)
} }
static struct nand_ecclayout nand_ecc_2K_16bits = { static struct nand_ecclayout nand_ecc_2K_16bits = {
.oobavail = 6,
.oobfree = { {2, 6} }, .oobfree = { {2, 6} },
}; };

View file

@ -427,9 +427,6 @@ static int jz_nand_probe(struct platform_device *pdev)
chip->ecc.strength = 4; chip->ecc.strength = 4;
chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
if (pdata)
chip->ecc.layout = pdata->ecc_layout;
chip->chip_delay = 50; chip->chip_delay = 50;
chip->cmd_ctrl = jz_nand_cmd_ctrl; chip->cmd_ctrl = jz_nand_cmd_ctrl;
chip->select_chip = jz_nand_select_chip; chip->select_chip = jz_nand_select_chip;

View file

@ -750,7 +750,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
} }
nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = mtd->writesize; nand_chip->ecc.size = 512;
nand_chip->ecc.layout = &lpc32xx_nand_oob; nand_chip->ecc.layout = &lpc32xx_nand_oob;
host->mlcsubpages = mtd->writesize / 512; host->mlcsubpages = mtd->writesize / 512;

View file

@ -626,7 +626,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
static int mpc5121_nfc_probe(struct platform_device *op) static int mpc5121_nfc_probe(struct platform_device *op)
{ {
struct device_node *rootnode, *dn = op->dev.of_node; struct device_node *dn = op->dev.of_node;
struct clk *clk; struct clk *clk;
struct device *dev = &op->dev; struct device *dev = &op->dev;
struct mpc5121_nfc_prv *prv; struct mpc5121_nfc_prv *prv;
@ -712,18 +712,15 @@ static int mpc5121_nfc_probe(struct platform_device *op)
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
/* Support external chip-select logic on ADS5121 board */ /* Support external chip-select logic on ADS5121 board */
rootnode = of_find_node_by_path("/"); if (of_machine_is_compatible("fsl,mpc5121ads")) {
if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) {
retval = ads5121_chipselect_init(mtd); retval = ads5121_chipselect_init(mtd);
if (retval) { if (retval) {
dev_err(dev, "Chipselect init error!\n"); dev_err(dev, "Chipselect init error!\n");
of_node_put(rootnode);
return retval; return retval;
} }
chip->select_chip = ads5121_select_chip; chip->select_chip = ads5121_select_chip;
} }
of_node_put(rootnode);
/* Enable NFC clock */ /* Enable NFC clock */
clk = devm_clk_get(dev, "ipg"); clk = devm_clk_get(dev, "ipg");

View file

@ -313,13 +313,12 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
* nand_block_bad - [DEFAULT] Read bad block marker from the chip * nand_block_bad - [DEFAULT] Read bad block marker from the chip
* @mtd: MTD device structure * @mtd: MTD device structure
* @ofs: offset from device start * @ofs: offset from device start
* @getchip: 0, if the chip is already selected
* *
* Check, if the block is bad. * Check, if the block is bad.
*/ */
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
{ {
int page, chipnr, res = 0, i = 0; int page, res = 0, i = 0;
struct nand_chip *chip = mtd_to_nand(mtd); struct nand_chip *chip = mtd_to_nand(mtd);
u16 bad; u16 bad;
@ -328,15 +327,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
page = (int)(ofs >> chip->page_shift) & chip->pagemask; page = (int)(ofs >> chip->page_shift) & chip->pagemask;
if (getchip) {
chipnr = (int)(ofs >> chip->chip_shift);
nand_get_device(mtd, FL_READING);
/* Select the NAND device */
chip->select_chip(mtd, chipnr);
}
do { do {
if (chip->options & NAND_BUSWIDTH_16) { if (chip->options & NAND_BUSWIDTH_16) {
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->cmdfunc(mtd, NAND_CMD_READOOB,
@ -361,11 +351,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
i++; i++;
} while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
if (getchip) {
chip->select_chip(mtd, -1);
nand_release_device(mtd);
}
return res; return res;
} }
@ -503,19 +488,17 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
* nand_block_checkbad - [GENERIC] Check if a block is marked bad * nand_block_checkbad - [GENERIC] Check if a block is marked bad
* @mtd: MTD device structure * @mtd: MTD device structure
* @ofs: offset from device start * @ofs: offset from device start
* @getchip: 0, if the chip is already selected
* @allowbbt: 1, if its allowed to access the bbt area * @allowbbt: 1, if its allowed to access the bbt area
* *
* Check, if the block is bad. Either by reading the bad block table or * Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function. * calling of the scan function.
*/ */
static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
int allowbbt)
{ {
struct nand_chip *chip = mtd_to_nand(mtd); struct nand_chip *chip = mtd_to_nand(mtd);
if (!chip->bbt) if (!chip->bbt)
return chip->block_bad(mtd, ofs, getchip); return chip->block_bad(mtd, ofs);
/* Return info from the table */ /* Return info from the table */
return nand_isbad_bbt(mtd, ofs, allowbbt); return nand_isbad_bbt(mtd, ofs, allowbbt);
@ -566,8 +549,8 @@ void nand_wait_ready(struct mtd_info *mtd)
cond_resched(); cond_resched();
} while (time_before(jiffies, timeo)); } while (time_before(jiffies, timeo));
pr_warn_ratelimited( if (!chip->dev_ready(mtd))
"timeout while waiting for chip to become ready\n"); pr_warn_ratelimited("timeout while waiting for chip to become ready\n");
out: out:
led_trigger_event(nand_led_trigger, LED_OFF); led_trigger_event(nand_led_trigger, LED_OFF);
} }
@ -1723,8 +1706,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
int ret = 0; int ret = 0;
uint32_t readlen = ops->len; uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen; uint32_t oobreadlen = ops->ooblen;
uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ? uint32_t max_oobsize = mtd_oobavail(mtd, ops);
mtd->oobavail : mtd->oobsize;
uint8_t *bufpoi, *oob, *buf; uint8_t *bufpoi, *oob, *buf;
int use_bufpoi; int use_bufpoi;
@ -2075,10 +2057,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
stats = mtd->ecc_stats; stats = mtd->ecc_stats;
if (ops->mode == MTD_OPS_AUTO_OOB) len = mtd_oobavail(mtd, ops);
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
if (unlikely(ops->ooboffs >= len)) { if (unlikely(ops->ooboffs >= len)) {
pr_debug("%s: attempt to start read outside oob\n", pr_debug("%s: attempt to start read outside oob\n",
@ -2575,8 +2554,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
uint32_t writelen = ops->len; uint32_t writelen = ops->len;
uint32_t oobwritelen = ops->ooblen; uint32_t oobwritelen = ops->ooblen;
uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ? uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
mtd->oobavail : mtd->oobsize;
uint8_t *oob = ops->oobbuf; uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf; uint8_t *buf = ops->datbuf;
@ -2766,10 +2744,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
pr_debug("%s: to = 0x%08x, len = %i\n", pr_debug("%s: to = 0x%08x, len = %i\n",
__func__, (unsigned int)to, (int)ops->ooblen); __func__, (unsigned int)to, (int)ops->ooblen);
if (ops->mode == MTD_OPS_AUTO_OOB) len = mtd_oobavail(mtd, ops);
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
/* Do not allow write past end of page */ /* Do not allow write past end of page */
if ((ops->ooboffs + ops->ooblen) > len) { if ((ops->ooboffs + ops->ooblen) > len) {
@ -2957,7 +2932,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
while (len) { while (len) {
/* Check if we have a bad block, we do not erase bad blocks! */ /* Check if we have a bad block, we do not erase bad blocks! */
if (nand_block_checkbad(mtd, ((loff_t) page) << if (nand_block_checkbad(mtd, ((loff_t) page) <<
chip->page_shift, 0, allowbbt)) { chip->page_shift, allowbbt)) {
pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
__func__, page); __func__, page);
instr->state = MTD_ERASE_FAILED; instr->state = MTD_ERASE_FAILED;
@ -3044,7 +3019,20 @@ static void nand_sync(struct mtd_info *mtd)
*/ */
static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{ {
return nand_block_checkbad(mtd, offs, 1, 0); struct nand_chip *chip = mtd_to_nand(mtd);
int chipnr = (int)(offs >> chip->chip_shift);
int ret;
/* Select the NAND device */
nand_get_device(mtd, FL_READING);
chip->select_chip(mtd, chipnr);
ret = nand_block_checkbad(mtd, offs, 0);
chip->select_chip(mtd, -1);
nand_release_device(mtd);
return ret;
} }
/** /**
@ -4287,10 +4275,8 @@ int nand_scan_tail(struct mtd_info *mtd)
} }
/* See nand_bch_init() for details. */ /* See nand_bch_init() for details. */
ecc->bytes = DIV_ROUND_UP( ecc->bytes = 0;
ecc->strength * fls(8 * ecc->size), 8); ecc->priv = nand_bch_init(mtd);
ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
&ecc->layout);
if (!ecc->priv) { if (!ecc->priv) {
pr_warn("BCH ECC initialization failed!\n"); pr_warn("BCH ECC initialization failed!\n");
BUG(); BUG();
@ -4325,11 +4311,11 @@ int nand_scan_tail(struct mtd_info *mtd)
* The number of bytes available for a client to place data into * The number of bytes available for a client to place data into
* the out of band area. * the out of band area.
*/ */
ecc->layout->oobavail = 0; mtd->oobavail = 0;
for (i = 0; ecc->layout->oobfree[i].length if (ecc->layout) {
&& i < ARRAY_SIZE(ecc->layout->oobfree); i++) for (i = 0; ecc->layout->oobfree[i].length; i++)
ecc->layout->oobavail += ecc->layout->oobfree[i].length; mtd->oobavail += ecc->layout->oobfree[i].length;
mtd->oobavail = ecc->layout->oobavail; }
/* ECC sanity check: warn if it's too weak */ /* ECC sanity check: warn if it's too weak */
if (!nand_ecc_strength_good(mtd)) if (!nand_ecc_strength_good(mtd))

View file

@ -1373,5 +1373,3 @@ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
return ret; return ret;
} }
EXPORT_SYMBOL(nand_scan_bbt);

View file

@ -107,9 +107,6 @@ EXPORT_SYMBOL(nand_bch_correct_data);
/** /**
* nand_bch_init - [NAND Interface] Initialize NAND BCH error correction * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
* @mtd: MTD block structure * @mtd: MTD block structure
* @eccsize: ecc block size in bytes
* @eccbytes: ecc length in bytes
* @ecclayout: output default layout
* *
* Returns: * Returns:
* a pointer to a new NAND BCH control structure, or NULL upon failure * a pointer to a new NAND BCH control structure, or NULL upon failure
@ -123,14 +120,21 @@ EXPORT_SYMBOL(nand_bch_correct_data);
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
*/ */
struct nand_bch_control * struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
struct nand_ecclayout **ecclayout)
{ {
struct nand_chip *nand = mtd_to_nand(mtd);
unsigned int m, t, eccsteps, i; unsigned int m, t, eccsteps, i;
struct nand_ecclayout *layout; struct nand_ecclayout *layout = nand->ecc.layout;
struct nand_bch_control *nbc = NULL; struct nand_bch_control *nbc = NULL;
unsigned char *erased_page; unsigned char *erased_page;
unsigned int eccsize = nand->ecc.size;
unsigned int eccbytes = nand->ecc.bytes;
unsigned int eccstrength = nand->ecc.strength;
if (!eccbytes && eccstrength) {
eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
nand->ecc.bytes = eccbytes;
}
if (!eccsize || !eccbytes) { if (!eccsize || !eccbytes) {
printk(KERN_WARNING "ecc parameters not supplied\n"); printk(KERN_WARNING "ecc parameters not supplied\n");
@ -158,7 +162,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
eccsteps = mtd->writesize/eccsize; eccsteps = mtd->writesize/eccsize;
/* if no ecc placement scheme was provided, build one */ /* if no ecc placement scheme was provided, build one */
if (!*ecclayout) { if (!layout) {
/* handle large page devices only */ /* handle large page devices only */
if (mtd->oobsize < 64) { if (mtd->oobsize < 64) {
@ -184,7 +188,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
layout->oobfree[0].offset = 2; layout->oobfree[0].offset = 2;
layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
*ecclayout = layout; nand->ecc.layout = layout;
} }
/* sanity checks */ /* sanity checks */
@ -192,7 +196,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
printk(KERN_WARNING "eccsize %u is too large\n", eccsize); printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
goto fail; goto fail;
} }
if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { if (layout->eccbytes != (eccsteps*eccbytes)) {
printk(KERN_WARNING "invalid ecc layout\n"); printk(KERN_WARNING "invalid ecc layout\n");
goto fail; goto fail;
} }
@ -216,6 +220,9 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
for (i = 0; i < eccbytes; i++) for (i = 0; i < eccbytes; i++)
nbc->eccmask[i] ^= 0xff; nbc->eccmask[i] ^= 0xff;
if (!eccstrength)
nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
return nbc; return nbc;
fail: fail:
nand_bch_free(nbc); nand_bch_free(nbc);

View file

@ -50,8 +50,8 @@ struct nand_flash_dev nand_flash_ids[] = {
SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
{"H27UCG8T2ATR-BC 64G 3.3V 8-bit", {"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K), SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
4 }, NAND_ECC_INFO(40, SZ_1K), 4 },
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),

View file

@ -113,7 +113,7 @@ static int nuc900_check_rb(struct nuc900_nand *nand)
{ {
unsigned int val; unsigned int val;
spin_lock(&nand->lock); spin_lock(&nand->lock);
val = __raw_readl(REG_SMISR); val = __raw_readl(nand->reg + REG_SMISR);
val &= READYBUSY; val &= READYBUSY;
spin_unlock(&nand->lock); spin_unlock(&nand->lock);

View file

@ -1807,13 +1807,19 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error; goto return_error;
} }
/*
* Bail out earlier to let NAND_ECC_SOFT code create its own
* ecclayout instead of using ours.
*/
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
nand_chip->ecc.mode = NAND_ECC_SOFT;
goto scan_tail;
}
/* populate MTD interface based on ECC scheme */ /* populate MTD interface based on ECC scheme */
ecclayout = &info->oobinfo; ecclayout = &info->oobinfo;
nand_chip->ecc.layout = ecclayout;
switch (info->ecc_opt) { switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_SW:
nand_chip->ecc.mode = NAND_ECC_SOFT;
break;
case OMAP_ECC_HAM1_CODE_HW: case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.mode = NAND_ECC_HW;
@ -1861,10 +1867,7 @@ static int omap_nand_probe(struct platform_device *pdev)
ecclayout->oobfree->offset = 1 + ecclayout->oobfree->offset = 1 +
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
/* software bch library is used for locating errors */ /* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd, nand_chip->ecc.priv = nand_bch_init(mtd);
nand_chip->ecc.size,
nand_chip->ecc.bytes,
&ecclayout);
if (!nand_chip->ecc.priv) { if (!nand_chip->ecc.priv) {
dev_err(&info->pdev->dev, "unable to use BCH library\n"); dev_err(&info->pdev->dev, "unable to use BCH library\n");
err = -EINVAL; err = -EINVAL;
@ -1925,10 +1928,7 @@ static int omap_nand_probe(struct platform_device *pdev)
ecclayout->oobfree->offset = 1 + ecclayout->oobfree->offset = 1 +
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
/* software bch library is used for locating errors */ /* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd, nand_chip->ecc.priv = nand_bch_init(mtd);
nand_chip->ecc.size,
nand_chip->ecc.bytes,
&ecclayout);
if (!nand_chip->ecc.priv) { if (!nand_chip->ecc.priv) {
dev_err(&info->pdev->dev, "unable to use BCH library\n"); dev_err(&info->pdev->dev, "unable to use BCH library\n");
err = -EINVAL; err = -EINVAL;
@ -2002,9 +2002,6 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error; goto return_error;
} }
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW)
goto scan_tail;
/* all OOB bytes from oobfree->offset till end off OOB are free */ /* all OOB bytes from oobfree->offset till end off OOB are free */
ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset; ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
/* check if NAND device's OOB is enough to store ECC signatures */ /* check if NAND device's OOB is enough to store ECC signatures */
@ -2015,7 +2012,6 @@ static int omap_nand_probe(struct platform_device *pdev)
err = -EINVAL; err = -EINVAL;
goto return_error; goto return_error;
} }
nand_chip->ecc.layout = ecclayout;
scan_tail: scan_tail:
/* second phase scan */ /* second phase scan */

View file

@ -73,7 +73,6 @@ static int plat_nand_probe(struct platform_device *pdev)
data->chip.bbt_options |= pdata->chip.bbt_options; data->chip.bbt_options |= pdata->chip.bbt_options;
data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
data->chip.ecc.layout = pdata->chip.ecclayout;
data->chip.ecc.mode = NAND_ECC_SOFT; data->chip.ecc.mode = NAND_ECC_SOFT;
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);

View file

@ -131,11 +131,23 @@
#define READ_ID_BYTES 7 #define READ_ID_BYTES 7
/* macros for registers read/write */ /* macros for registers read/write */
#define nand_writel(info, off, val) \ #define nand_writel(info, off, val) \
writel_relaxed((val), (info)->mmio_base + (off)) do { \
dev_vdbg(&info->pdev->dev, \
"%s():%d nand_writel(0x%x, 0x%04x)\n", \
__func__, __LINE__, (val), (off)); \
writel_relaxed((val), (info)->mmio_base + (off)); \
} while (0)
#define nand_readl(info, off) \ #define nand_readl(info, off) \
readl_relaxed((info)->mmio_base + (off)) ({ \
unsigned int _v; \
_v = readl_relaxed((info)->mmio_base + (off)); \
dev_vdbg(&info->pdev->dev, \
"%s():%d nand_readl(0x%04x) = 0x%x\n", \
__func__, __LINE__, (off), _v); \
_v; \
})
/* error code and state */ /* error code and state */
enum { enum {
@ -199,7 +211,6 @@ struct pxa3xx_nand_info {
struct dma_chan *dma_chan; struct dma_chan *dma_chan;
dma_cookie_t dma_cookie; dma_cookie_t dma_cookie;
int drcmr_dat; int drcmr_dat;
int drcmr_cmd;
unsigned char *data_buff; unsigned char *data_buff;
unsigned char *oob_buff; unsigned char *oob_buff;
@ -222,15 +233,44 @@ struct pxa3xx_nand_info {
int use_spare; /* use spare ? */ int use_spare; /* use spare ? */
int need_wait; int need_wait;
unsigned int data_size; /* data to be read from FIFO */ /* Amount of real data per full chunk */
unsigned int chunk_size; /* split commands chunk size */ unsigned int chunk_size;
unsigned int oob_size;
/* Amount of spare data per full chunk */
unsigned int spare_size; unsigned int spare_size;
/* Number of full chunks (i.e chunk_size + spare_size) */
unsigned int nfullchunks;
/*
* Total number of chunks. If equal to nfullchunks, then there
* are only full chunks. Otherwise, there is one last chunk of
* size (last_chunk_size + last_spare_size)
*/
unsigned int ntotalchunks;
/* Amount of real data in the last chunk */
unsigned int last_chunk_size;
/* Amount of spare data in the last chunk */
unsigned int last_spare_size;
unsigned int ecc_size; unsigned int ecc_size;
unsigned int ecc_err_cnt; unsigned int ecc_err_cnt;
unsigned int max_bitflips; unsigned int max_bitflips;
int retcode; int retcode;
/*
* Variables only valid during command
* execution. step_chunk_size and step_spare_size is the
* amount of real data and spare data in the current
* chunk. cur_chunk is the current chunk being
* read/programmed.
*/
unsigned int step_chunk_size;
unsigned int step_spare_size;
unsigned int cur_chunk;
/* cached register value */ /* cached register value */
uint32_t reg_ndcr; uint32_t reg_ndcr;
uint32_t ndtr0cs0; uint32_t ndtr0cs0;
@ -526,25 +566,6 @@ static int pxa3xx_nand_init(struct pxa3xx_nand_host *host)
return 0; return 0;
} }
/*
* Set the data and OOB size, depending on the selected
* spare and ECC configuration.
* Only applicable to READ0, READOOB and PAGEPROG commands.
*/
static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info,
struct mtd_info *mtd)
{
int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
info->data_size = mtd->writesize;
if (!oob_enable)
return;
info->oob_size = info->spare_size;
if (!info->use_ecc)
info->oob_size += info->ecc_size;
}
/** /**
* NOTE: it is a must to set ND_RUN firstly, then write * NOTE: it is a must to set ND_RUN firstly, then write
* command buffer, otherwise, it does not work. * command buffer, otherwise, it does not work.
@ -660,28 +681,28 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
static void handle_data_pio(struct pxa3xx_nand_info *info) static void handle_data_pio(struct pxa3xx_nand_info *info)
{ {
unsigned int do_bytes = min(info->data_size, info->chunk_size);
switch (info->state) { switch (info->state) {
case STATE_PIO_WRITING: case STATE_PIO_WRITING:
writesl(info->mmio_base + NDDB, if (info->step_chunk_size)
info->data_buff + info->data_buff_pos, writesl(info->mmio_base + NDDB,
DIV_ROUND_UP(do_bytes, 4)); info->data_buff + info->data_buff_pos,
DIV_ROUND_UP(info->step_chunk_size, 4));
if (info->oob_size > 0) if (info->step_spare_size)
writesl(info->mmio_base + NDDB, writesl(info->mmio_base + NDDB,
info->oob_buff + info->oob_buff_pos, info->oob_buff + info->oob_buff_pos,
DIV_ROUND_UP(info->oob_size, 4)); DIV_ROUND_UP(info->step_spare_size, 4));
break; break;
case STATE_PIO_READING: case STATE_PIO_READING:
drain_fifo(info, if (info->step_chunk_size)
info->data_buff + info->data_buff_pos, drain_fifo(info,
DIV_ROUND_UP(do_bytes, 4)); info->data_buff + info->data_buff_pos,
DIV_ROUND_UP(info->step_chunk_size, 4));
if (info->oob_size > 0) if (info->step_spare_size)
drain_fifo(info, drain_fifo(info,
info->oob_buff + info->oob_buff_pos, info->oob_buff + info->oob_buff_pos,
DIV_ROUND_UP(info->oob_size, 4)); DIV_ROUND_UP(info->step_spare_size, 4));
break; break;
default: default:
dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
@ -690,9 +711,8 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
} }
/* Update buffer pointers for multi-page read/write */ /* Update buffer pointers for multi-page read/write */
info->data_buff_pos += do_bytes; info->data_buff_pos += info->step_chunk_size;
info->oob_buff_pos += info->oob_size; info->oob_buff_pos += info->step_spare_size;
info->data_size -= do_bytes;
} }
static void pxa3xx_nand_data_dma_irq(void *data) static void pxa3xx_nand_data_dma_irq(void *data)
@ -733,8 +753,9 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
info->state); info->state);
BUG(); BUG();
} }
info->sg.length = info->data_size + info->sg.length = info->chunk_size;
(info->oob_size ? info->spare_size + info->ecc_size : 0); if (info->use_spare)
info->sg.length += info->spare_size + info->ecc_size;
dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction, tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction,
@ -895,9 +916,11 @@ static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
/* reset data and oob column point to handle data */ /* reset data and oob column point to handle data */
info->buf_start = 0; info->buf_start = 0;
info->buf_count = 0; info->buf_count = 0;
info->oob_size = 0;
info->data_buff_pos = 0; info->data_buff_pos = 0;
info->oob_buff_pos = 0; info->oob_buff_pos = 0;
info->step_chunk_size = 0;
info->step_spare_size = 0;
info->cur_chunk = 0;
info->use_ecc = 0; info->use_ecc = 0;
info->use_spare = 1; info->use_spare = 1;
info->retcode = ERR_NONE; info->retcode = ERR_NONE;
@ -909,8 +932,6 @@ static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
case NAND_CMD_READ0: case NAND_CMD_READ0:
case NAND_CMD_PAGEPROG: case NAND_CMD_PAGEPROG:
info->use_ecc = 1; info->use_ecc = 1;
case NAND_CMD_READOOB:
pxa3xx_set_datasize(info, mtd);
break; break;
case NAND_CMD_PARAM: case NAND_CMD_PARAM:
info->use_spare = 0; info->use_spare = 0;
@ -969,6 +990,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
if (command == NAND_CMD_READOOB) if (command == NAND_CMD_READOOB)
info->buf_start += mtd->writesize; info->buf_start += mtd->writesize;
if (info->cur_chunk < info->nfullchunks) {
info->step_chunk_size = info->chunk_size;
info->step_spare_size = info->spare_size;
} else {
info->step_chunk_size = info->last_chunk_size;
info->step_spare_size = info->last_spare_size;
}
/* /*
* Multiple page read needs an 'extended command type' field, * Multiple page read needs an 'extended command type' field,
* which is either naked-read or last-read according to the * which is either naked-read or last-read according to the
@ -980,8 +1009,8 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
| NDCB0_LEN_OVRD | NDCB0_LEN_OVRD
| NDCB0_EXT_CMD_TYPE(ext_cmd_type); | NDCB0_EXT_CMD_TYPE(ext_cmd_type);
info->ndcb3 = info->chunk_size + info->ndcb3 = info->step_chunk_size +
info->oob_size; info->step_spare_size;
} }
set_command_address(info, mtd->writesize, column, page_addr); set_command_address(info, mtd->writesize, column, page_addr);
@ -1001,8 +1030,6 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
| NDCB0_EXT_CMD_TYPE(ext_cmd_type) | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
| addr_cycle | addr_cycle
| command; | command;
/* No data transfer in this case */
info->data_size = 0;
exec_cmd = 1; exec_cmd = 1;
} }
break; break;
@ -1014,6 +1041,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
break; break;
} }
if (info->cur_chunk < info->nfullchunks) {
info->step_chunk_size = info->chunk_size;
info->step_spare_size = info->spare_size;
} else {
info->step_chunk_size = info->last_chunk_size;
info->step_spare_size = info->last_spare_size;
}
/* Second command setting for large pages */ /* Second command setting for large pages */
if (mtd->writesize > PAGE_CHUNK_SIZE) { if (mtd->writesize > PAGE_CHUNK_SIZE) {
/* /*
@ -1024,14 +1059,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
info->ndcb0 |= NDCB0_CMD_TYPE(0x1) info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
| NDCB0_LEN_OVRD | NDCB0_LEN_OVRD
| NDCB0_EXT_CMD_TYPE(ext_cmd_type); | NDCB0_EXT_CMD_TYPE(ext_cmd_type);
info->ndcb3 = info->chunk_size + info->ndcb3 = info->step_chunk_size +
info->oob_size; info->step_spare_size;
/* /*
* This is the command dispatch that completes a chunked * This is the command dispatch that completes a chunked
* page program operation. * page program operation.
*/ */
if (info->data_size == 0) { if (info->cur_chunk == info->ntotalchunks) {
info->ndcb0 = NDCB0_CMD_TYPE(0x1) info->ndcb0 = NDCB0_CMD_TYPE(0x1)
| NDCB0_EXT_CMD_TYPE(ext_cmd_type) | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
| command; | command;
@ -1058,7 +1093,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
| command; | command;
info->ndcb1 = (column & 0xFF); info->ndcb1 = (column & 0xFF);
info->ndcb3 = INIT_BUFFER_SIZE; info->ndcb3 = INIT_BUFFER_SIZE;
info->data_size = INIT_BUFFER_SIZE; info->step_chunk_size = INIT_BUFFER_SIZE;
break; break;
case NAND_CMD_READID: case NAND_CMD_READID:
@ -1068,7 +1103,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
| command; | command;
info->ndcb1 = (column & 0xFF); info->ndcb1 = (column & 0xFF);
info->data_size = 8; info->step_chunk_size = 8;
break; break;
case NAND_CMD_STATUS: case NAND_CMD_STATUS:
info->buf_count = 1; info->buf_count = 1;
@ -1076,7 +1111,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
| NDCB0_ADDR_CYC(1) | NDCB0_ADDR_CYC(1)
| command; | command;
info->data_size = 8; info->step_chunk_size = 8;
break; break;
case NAND_CMD_ERASE1: case NAND_CMD_ERASE1:
@ -1217,6 +1252,7 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
init_completion(&info->dev_ready); init_completion(&info->dev_ready);
do { do {
info->state = STATE_PREPARED; info->state = STATE_PREPARED;
exec_cmd = prepare_set_command(info, command, ext_cmd_type, exec_cmd = prepare_set_command(info, command, ext_cmd_type,
column, page_addr); column, page_addr);
if (!exec_cmd) { if (!exec_cmd) {
@ -1236,22 +1272,30 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
break; break;
} }
/* Only a few commands need several steps */
if (command != NAND_CMD_PAGEPROG &&
command != NAND_CMD_READ0 &&
command != NAND_CMD_READOOB)
break;
info->cur_chunk++;
/* Check if the sequence is complete */ /* Check if the sequence is complete */
if (info->data_size == 0 && command != NAND_CMD_PAGEPROG) if (info->cur_chunk == info->ntotalchunks && command != NAND_CMD_PAGEPROG)
break; break;
/* /*
* After a splitted program command sequence has issued * After a splitted program command sequence has issued
* the command dispatch, the command sequence is complete. * the command dispatch, the command sequence is complete.
*/ */
if (info->data_size == 0 && if (info->cur_chunk == (info->ntotalchunks + 1) &&
command == NAND_CMD_PAGEPROG && command == NAND_CMD_PAGEPROG &&
ext_cmd_type == EXT_CMD_TYPE_DISPATCH) ext_cmd_type == EXT_CMD_TYPE_DISPATCH)
break; break;
if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) {
/* Last read: issue a 'last naked read' */ /* Last read: issue a 'last naked read' */
if (info->data_size == info->chunk_size) if (info->cur_chunk == info->ntotalchunks - 1)
ext_cmd_type = EXT_CMD_TYPE_LAST_RW; ext_cmd_type = EXT_CMD_TYPE_LAST_RW;
else else
ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; ext_cmd_type = EXT_CMD_TYPE_NAKED_RW;
@ -1261,7 +1305,7 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
* the command dispatch must be issued to complete. * the command dispatch must be issued to complete.
*/ */
} else if (command == NAND_CMD_PAGEPROG && } else if (command == NAND_CMD_PAGEPROG &&
info->data_size == 0) { info->cur_chunk == info->ntotalchunks) {
ext_cmd_type = EXT_CMD_TYPE_DISPATCH; ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
} }
} while (1); } while (1);
@ -1506,6 +1550,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
int strength, int ecc_stepsize, int page_size) int strength, int ecc_stepsize, int page_size)
{ {
if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
info->nfullchunks = 1;
info->ntotalchunks = 1;
info->chunk_size = 2048; info->chunk_size = 2048;
info->spare_size = 40; info->spare_size = 40;
info->ecc_size = 24; info->ecc_size = 24;
@ -1514,6 +1560,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
ecc->strength = 1; ecc->strength = 1;
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
info->nfullchunks = 1;
info->ntotalchunks = 1;
info->chunk_size = 512; info->chunk_size = 512;
info->spare_size = 8; info->spare_size = 8;
info->ecc_size = 8; info->ecc_size = 8;
@ -1527,6 +1575,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
*/ */
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) {
info->ecc_bch = 1; info->ecc_bch = 1;
info->nfullchunks = 1;
info->ntotalchunks = 1;
info->chunk_size = 2048; info->chunk_size = 2048;
info->spare_size = 32; info->spare_size = 32;
info->ecc_size = 32; info->ecc_size = 32;
@ -1537,6 +1587,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
info->ecc_bch = 1; info->ecc_bch = 1;
info->nfullchunks = 2;
info->ntotalchunks = 2;
info->chunk_size = 2048; info->chunk_size = 2048;
info->spare_size = 32; info->spare_size = 32;
info->ecc_size = 32; info->ecc_size = 32;
@ -1551,8 +1603,12 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
*/ */
} else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) {
info->ecc_bch = 1; info->ecc_bch = 1;
info->nfullchunks = 4;
info->ntotalchunks = 5;
info->chunk_size = 1024; info->chunk_size = 1024;
info->spare_size = 0; info->spare_size = 0;
info->last_chunk_size = 0;
info->last_spare_size = 64;
info->ecc_size = 32; info->ecc_size = 32;
ecc->mode = NAND_ECC_HW; ecc->mode = NAND_ECC_HW;
ecc->size = info->chunk_size; ecc->size = info->chunk_size;
@ -1738,7 +1794,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (use_dma) { if (!np && use_dma) {
r = platform_get_resource(pdev, IORESOURCE_DMA, 0); r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (r == NULL) { if (r == NULL) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
@ -1747,15 +1803,6 @@ static int alloc_nand_resource(struct platform_device *pdev)
goto fail_disable_clk; goto fail_disable_clk;
} }
info->drcmr_dat = r->start; info->drcmr_dat = r->start;
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;
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);

File diff suppressed because it is too large Load diff

View file

@ -861,9 +861,6 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
#endif #endif
if (set->ecc_layout != NULL)
chip->ecc.layout = set->ecc_layout;
if (set->disable_ecc) if (set->disable_ecc)
chip->ecc.mode = NAND_ECC_NONE; chip->ecc.mode = NAND_ECC_NONE;

View file

@ -60,6 +60,7 @@
#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) #define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) #define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
#define NFC_REG_SPARE_AREA 0x00A0 #define NFC_REG_SPARE_AREA 0x00A0
#define NFC_REG_PAT_ID 0x00A4
#define NFC_RAM0_BASE 0x0400 #define NFC_RAM0_BASE 0x0400
#define NFC_RAM1_BASE 0x0800 #define NFC_RAM1_BASE 0x0800
@ -538,6 +539,174 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
} }
/* These seed values have been extracted from Allwinner's BSP */
static const u16 sunxi_nfc_randomizer_page_seeds[] = {
0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
};
/*
* sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
* have been generated using
* sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
* the randomizer engine does internally before de/scrambling OOB data.
*
* Those tables are statically defined to avoid calculating randomizer state
* at runtime.
*/
static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
};
static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
};
static u16 sunxi_nfc_randomizer_step(u16 state, int count)
{
state &= 0x7fff;
/*
* This loop is just a simple implementation of a Fibonacci LFSR using
* the x16 + x15 + 1 polynomial.
*/
while (count--)
state = ((state >> 1) |
(((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
return state;
}
static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc)
{
const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
int mod = mtd_div_by_ws(mtd->erasesize, mtd);
if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
if (ecc) {
if (mtd->ecc_step_size == 512)
seeds = sunxi_nfc_randomizer_ecc512_seeds;
else
seeds = sunxi_nfc_randomizer_ecc1024_seeds;
}
return seeds[page % mod];
}
static void sunxi_nfc_randomizer_config(struct mtd_info *mtd,
int page, bool ecc)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
u16 state;
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
state = sunxi_nfc_randomizer_state(mtd, page, ecc);
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
}
static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
nfc->regs + NFC_REG_ECC_CTL);
}
static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
nfc->regs + NFC_REG_ECC_CTL);
}
static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm)
{
u16 state = sunxi_nfc_randomizer_state(mtd, page, true);
bbm[0] ^= state;
bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
}
static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len,
bool ecc, int page)
{
sunxi_nfc_randomizer_config(mtd, page, ecc);
sunxi_nfc_randomizer_enable(mtd);
sunxi_nfc_write_buf(mtd, buf, len);
sunxi_nfc_randomizer_disable(mtd);
}
static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf,
int len, bool ecc, int page)
{
sunxi_nfc_randomizer_config(mtd, page, ecc);
sunxi_nfc_randomizer_enable(mtd);
sunxi_nfc_read_buf(mtd, buf, len);
sunxi_nfc_randomizer_disable(mtd);
}
static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
{ {
struct nand_chip *nand = mtd_to_nand(mtd); struct nand_chip *nand = mtd_to_nand(mtd);
@ -574,18 +743,20 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
u8 *data, int data_off, u8 *data, int data_off,
u8 *oob, int oob_off, u8 *oob, int oob_off,
int *cur_off, int *cur_off,
unsigned int *max_bitflips) unsigned int *max_bitflips,
bool bbm, int page)
{ {
struct nand_chip *nand = mtd_to_nand(mtd); struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc; struct nand_ecc_ctrl *ecc = &nand->ecc;
int raw_mode = 0;
u32 status; u32 status;
int ret; int ret;
if (*cur_off != data_off) if (*cur_off != data_off)
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
sunxi_nfc_read_buf(mtd, NULL, ecc->size); sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page);
if (data_off + ecc->size != oob_off) if (data_off + ecc->size != oob_off)
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
@ -594,25 +765,54 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
if (ret) if (ret)
return ret; return ret;
sunxi_nfc_randomizer_enable(mtd);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD); nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret) if (ret)
return ret; return ret;
*cur_off = oob_off + ecc->bytes + 4;
status = readl(nfc->regs + NFC_REG_ECC_ST); status = readl(nfc->regs + NFC_REG_ECC_ST);
if (status & NFC_ECC_PAT_FOUND(0)) {
u8 pattern = 0xff;
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
pattern = 0x0;
memset(data, pattern, ecc->size);
memset(oob, pattern, ecc->bytes + 4);
return 1;
}
ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
sunxi_nfc_read_buf(mtd, oob, ecc->bytes + 4); sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
if (status & NFC_ECC_ERR(0)) { if (status & NFC_ECC_ERR(0)) {
/*
* Re-read the data with the randomizer disabled to identify
* bitflips in erased pages.
*/
if (nand->options & NAND_NEED_SCRAMBLING) {
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
nand->read_buf(mtd, data, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
nand->read_buf(mtd, oob, ecc->bytes + 4);
}
ret = nand_check_erased_ecc_chunk(data, ecc->size, ret = nand_check_erased_ecc_chunk(data, ecc->size,
oob, ecc->bytes + 4, oob, ecc->bytes + 4,
NULL, 0, ecc->strength); NULL, 0, ecc->strength);
if (ret >= 0)
raw_mode = 1;
} else { } else {
/* /*
* The engine protects 4 bytes of OOB data per chunk. * The engine protects 4 bytes of OOB data per chunk.
@ -620,6 +820,10 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
*/ */
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)), sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)),
oob); oob);
/* De-randomize the Bad Block Marker. */
if (bbm && nand->options & NAND_NEED_SCRAMBLING)
sunxi_nfc_randomize_bbm(mtd, page, oob);
} }
if (ret < 0) { if (ret < 0) {
@ -629,13 +833,12 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
*max_bitflips = max_t(unsigned int, *max_bitflips, ret); *max_bitflips = max_t(unsigned int, *max_bitflips, ret);
} }
*cur_off = oob_off + ecc->bytes + 4; return raw_mode;
return 0;
} }
static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
u8 *oob, int *cur_off) u8 *oob, int *cur_off,
bool randomize, int page)
{ {
struct nand_chip *nand = mtd_to_nand(mtd); struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc; struct nand_ecc_ctrl *ecc = &nand->ecc;
@ -649,7 +852,11 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
offset + mtd->writesize, -1); offset + mtd->writesize, -1);
sunxi_nfc_read_buf(mtd, oob + offset, len); if (!randomize)
sunxi_nfc_read_buf(mtd, oob + offset, len);
else
sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
false, page);
*cur_off = mtd->oobsize + mtd->writesize; *cur_off = mtd->oobsize + mtd->writesize;
} }
@ -662,7 +869,8 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
const u8 *data, int data_off, const u8 *data, int data_off,
const u8 *oob, int oob_off, const u8 *oob, int oob_off,
int *cur_off) int *cur_off, bool bbm,
int page)
{ {
struct nand_chip *nand = mtd_to_nand(mtd); struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
@ -672,11 +880,20 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
if (data_off != *cur_off) if (data_off != *cur_off)
nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1); nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1);
sunxi_nfc_write_buf(mtd, data, ecc->size); sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
/* Fill OOB data in */ /* Fill OOB data in */
writel(sunxi_nfc_buf_to_user_data(oob), if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
nfc->regs + NFC_REG_USER_DATA(0)); u8 user_data[4];
memcpy(user_data, oob, 4);
sunxi_nfc_randomize_bbm(mtd, page, user_data);
writel(sunxi_nfc_buf_to_user_data(user_data),
nfc->regs + NFC_REG_USER_DATA(0));
} else {
writel(sunxi_nfc_buf_to_user_data(oob),
nfc->regs + NFC_REG_USER_DATA(0));
}
if (data_off + ecc->size != oob_off) if (data_off + ecc->size != oob_off)
nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
@ -685,11 +902,13 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
if (ret) if (ret)
return ret; return ret;
sunxi_nfc_randomizer_enable(mtd);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
NFC_ACCESS_DIR | NFC_ECC_OP, NFC_ACCESS_DIR | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD); nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret) if (ret)
return ret; return ret;
@ -699,7 +918,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
} }
static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
u8 *oob, int *cur_off) u8 *oob, int *cur_off,
int page)
{ {
struct nand_chip *nand = mtd_to_nand(mtd); struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc; struct nand_ecc_ctrl *ecc = &nand->ecc;
@ -713,7 +933,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
nand->cmdfunc(mtd, NAND_CMD_RNDIN, nand->cmdfunc(mtd, NAND_CMD_RNDIN,
offset + mtd->writesize, -1); offset + mtd->writesize, -1);
sunxi_nfc_write_buf(mtd, oob + offset, len); sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
*cur_off = mtd->oobsize + mtd->writesize; *cur_off = mtd->oobsize + mtd->writesize;
} }
@ -725,6 +945,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_ecc_ctrl *ecc = &chip->ecc;
unsigned int max_bitflips = 0; unsigned int max_bitflips = 0;
int ret, i, cur_off = 0; int ret, i, cur_off = 0;
bool raw_mode = false;
sunxi_nfc_hw_ecc_enable(mtd); sunxi_nfc_hw_ecc_enable(mtd);
@ -736,13 +957,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize, oob_off + mtd->writesize,
&cur_off, &max_bitflips); &cur_off, &max_bitflips,
if (ret) !i, page);
if (ret < 0)
return ret; return ret;
else if (ret)
raw_mode = true;
} }
if (oob_required) if (oob_required)
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
!raw_mode, page);
sunxi_nfc_hw_ecc_disable(mtd); sunxi_nfc_hw_ecc_disable(mtd);
@ -767,13 +992,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize, oob_off + mtd->writesize,
&cur_off); &cur_off, !i, page);
if (ret) if (ret)
return ret; return ret;
} }
if (oob_required) if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
&cur_off, page);
sunxi_nfc_hw_ecc_disable(mtd); sunxi_nfc_hw_ecc_disable(mtd);
@ -788,6 +1014,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_ecc_ctrl *ecc = &chip->ecc;
unsigned int max_bitflips = 0; unsigned int max_bitflips = 0;
int ret, i, cur_off = 0; int ret, i, cur_off = 0;
bool raw_mode = false;
sunxi_nfc_hw_ecc_enable(mtd); sunxi_nfc_hw_ecc_enable(mtd);
@ -799,13 +1026,16 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off, &cur_off, oob_off, &cur_off,
&max_bitflips); &max_bitflips, !i, page);
if (ret) if (ret < 0)
return ret; return ret;
else if (ret)
raw_mode = true;
} }
if (oob_required) if (oob_required)
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
!raw_mode, page);
sunxi_nfc_hw_ecc_disable(mtd); sunxi_nfc_hw_ecc_disable(mtd);
@ -829,13 +1059,15 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
oob, oob_off, &cur_off); oob, oob_off, &cur_off,
false, page);
if (ret) if (ret)
return ret; return ret;
} }
if (oob_required) if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
&cur_off, page);
sunxi_nfc_hw_ecc_disable(mtd); sunxi_nfc_hw_ecc_disable(mtd);
@ -1345,6 +1577,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (nand->bbt_options & NAND_BBT_USE_FLASH) if (nand->bbt_options & NAND_BBT_USE_FLASH)
nand->bbt_options |= NAND_BBT_NO_OOB; nand->bbt_options |= NAND_BBT_NO_OOB;
if (nand->options & NAND_NEED_SCRAMBLING)
nand->options |= NAND_NO_SUBPAGE_WRITE;
ret = sunxi_nand_chip_init_timings(chip, np); ret = sunxi_nand_chip_init_timings(chip, np);
if (ret) { if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret); dev_err(dev, "could not configure chip timings: %d\n", ret);

View file

@ -795,8 +795,6 @@ static int vf610_nfc_probe(struct platform_device *pdev)
goto error; goto error;
} }
/* propagate ecc.layout to mtd_info */
mtd->ecclayout = chip->ecc.layout;
chip->ecc.read_page = vf610_nfc_read_page; chip->ecc.read_page = vf610_nfc_read_page;
chip->ecc.write_page = vf610_nfc_write_page; chip->ecc.write_page = vf610_nfc_write_page;

View file

@ -1124,11 +1124,7 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
(int)len); (int)len);
if (ops->mode == MTD_OPS_AUTO_OOB) oobsize = mtd_oobavail(mtd, ops);
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
oobcolumn = from & (mtd->oobsize - 1); oobcolumn = from & (mtd->oobsize - 1);
/* Do not allow reads past end of device */ /* Do not allow reads past end of device */
@ -1229,11 +1225,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
(int)len); (int)len);
if (ops->mode == MTD_OPS_AUTO_OOB) oobsize = mtd_oobavail(mtd, ops);
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
oobcolumn = from & (mtd->oobsize - 1); oobcolumn = from & (mtd->oobsize - 1);
/* Do not allow reads past end of device */ /* Do not allow reads past end of device */
@ -1365,7 +1357,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
ops->oobretlen = 0; ops->oobretlen = 0;
if (mode == MTD_OPS_AUTO_OOB) if (mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail; oobsize = mtd->oobavail;
else else
oobsize = mtd->oobsize; oobsize = mtd->oobsize;
@ -1885,12 +1877,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
/* Check zero length */ /* Check zero length */
if (!len) if (!len)
return 0; return 0;
oobsize = mtd_oobavail(mtd, ops);
if (ops->mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
oobcolumn = to & (mtd->oobsize - 1); oobcolumn = to & (mtd->oobsize - 1);
column = to & (mtd->writesize - 1); column = to & (mtd->writesize - 1);
@ -2063,7 +2050,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
ops->oobretlen = 0; ops->oobretlen = 0;
if (mode == MTD_OPS_AUTO_OOB) if (mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail; oobsize = mtd->oobavail;
else else
oobsize = mtd->oobsize; oobsize = mtd->oobsize;
@ -2599,6 +2586,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
*/ */
static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{ {
struct onenand_chip *this = mtd->priv;
int ret; int ret;
ret = onenand_block_isbad(mtd, ofs); ret = onenand_block_isbad(mtd, ofs);
@ -2610,7 +2598,7 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
} }
onenand_get_device(mtd, FL_WRITING); onenand_get_device(mtd, FL_WRITING);
ret = mtd_block_markbad(mtd, ofs); ret = this->block_markbad(mtd, ofs);
onenand_release_device(mtd); onenand_release_device(mtd);
return ret; return ret;
} }
@ -4049,12 +4037,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
* The number of bytes available for a client to place data into * The number of bytes available for a client to place data into
* the out of band area * the out of band area
*/ */
this->ecclayout->oobavail = 0; mtd->oobavail = 0;
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
this->ecclayout->oobfree[i].length; i++) this->ecclayout->oobfree[i].length; i++)
this->ecclayout->oobavail += mtd->oobavail += this->ecclayout->oobfree[i].length;
this->ecclayout->oobfree[i].length;
mtd->oobavail = this->ecclayout->oobavail;
mtd->ecclayout = this->ecclayout; mtd->ecclayout = this->ecclayout;
mtd->ecc_strength = 1; mtd->ecc_strength = 1;

View file

@ -179,7 +179,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
* by the onenand_release function. * by the onenand_release function.
* *
*/ */
int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
struct bbm_info *bbm = this->bbm; struct bbm_info *bbm = this->bbm;
@ -247,6 +247,3 @@ int onenand_default_bbt(struct mtd_info *mtd)
return onenand_scan_bbt(mtd, bbm->badblock_pattern); return onenand_scan_bbt(mtd, bbm->badblock_pattern);
} }
EXPORT_SYMBOL(onenand_scan_bbt);
EXPORT_SYMBOL(onenand_default_bbt);

View file

@ -9,6 +9,7 @@ if MTD_SPI_NOR
config MTD_MT81xx_NOR config MTD_MT81xx_NOR
tristate "Mediatek MT81xx SPI NOR flash controller" tristate "Mediatek MT81xx SPI NOR flash controller"
depends on HAS_IOMEM
help help
This enables access to SPI NOR flash, using MT81xx SPI NOR flash This enables access to SPI NOR flash, using MT81xx SPI NOR flash
controller. This controller does not support generic SPI BUS, it only controller. This controller does not support generic SPI BUS, it only
@ -30,7 +31,7 @@ config MTD_SPI_NOR_USE_4K_SECTORS
config SPI_FSL_QUADSPI config SPI_FSL_QUADSPI
tristate "Freescale Quad SPI controller" tristate "Freescale Quad SPI controller"
depends on ARCH_MXC || COMPILE_TEST depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
depends on HAS_IOMEM depends on HAS_IOMEM
help help
This enables support for the Quad SPI controller in master mode. This enables support for the Quad SPI controller in master mode.

View file

@ -213,6 +213,7 @@ enum fsl_qspi_devtype {
FSL_QUADSPI_IMX6SX, FSL_QUADSPI_IMX6SX,
FSL_QUADSPI_IMX7D, FSL_QUADSPI_IMX7D,
FSL_QUADSPI_IMX6UL, FSL_QUADSPI_IMX6UL,
FSL_QUADSPI_LS1021A,
}; };
struct fsl_qspi_devtype_data { struct fsl_qspi_devtype_data {
@ -258,6 +259,14 @@ static struct fsl_qspi_devtype_data imx6ul_data = {
| QUADSPI_QUIRK_4X_INT_CLK, | QUADSPI_QUIRK_4X_INT_CLK,
}; };
static struct fsl_qspi_devtype_data ls1021a_data = {
.devtype = FSL_QUADSPI_LS1021A,
.rxfifo = 128,
.txfifo = 64,
.ahb_buf_size = 1024,
.driver_data = 0,
};
#define FSL_QSPI_MAX_CHIP 4 #define FSL_QSPI_MAX_CHIP 4
struct fsl_qspi { struct fsl_qspi {
struct spi_nor nor[FSL_QSPI_MAX_CHIP]; struct spi_nor nor[FSL_QSPI_MAX_CHIP];
@ -275,6 +284,7 @@ struct fsl_qspi {
u32 clk_rate; u32 clk_rate;
unsigned int chip_base_addr; /* We may support two chips. */ unsigned int chip_base_addr; /* We may support two chips. */
bool has_second_chip; bool has_second_chip;
bool big_endian;
struct mutex lock; struct mutex lock;
struct pm_qos_request pm_qos_req; struct pm_qos_request pm_qos_req;
}; };
@ -299,6 +309,28 @@ static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618; return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618;
} }
/*
* R/W functions for big- or little-endian registers:
* The qSPI controller's endian is independent of the CPU core's endian.
* So far, although the CPU core is little-endian but the qSPI have two
* versions for big-endian and little-endian.
*/
static void qspi_writel(struct fsl_qspi *q, u32 val, void __iomem *addr)
{
if (q->big_endian)
iowrite32be(val, addr);
else
iowrite32(val, addr);
}
static u32 qspi_readl(struct fsl_qspi *q, void __iomem *addr)
{
if (q->big_endian)
return ioread32be(addr);
else
return ioread32(addr);
}
/* /*
* An IC bug makes us to re-arrange the 32-bit data. * An IC bug makes us to re-arrange the 32-bit data.
* The following chips, such as IMX6SLX, have fixed this bug. * The following chips, such as IMX6SLX, have fixed this bug.
@ -310,14 +342,14 @@ static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q)
{ {
writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR); qspi_writel(q, QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
} }
static inline void fsl_qspi_lock_lut(struct fsl_qspi *q) static inline void fsl_qspi_lock_lut(struct fsl_qspi *q)
{ {
writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR); qspi_writel(q, QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
} }
static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id)
@ -326,8 +358,8 @@ static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id)
u32 reg; u32 reg;
/* clear interrupt */ /* clear interrupt */
reg = readl(q->iobase + QUADSPI_FR); reg = qspi_readl(q, q->iobase + QUADSPI_FR);
writel(reg, q->iobase + QUADSPI_FR); qspi_writel(q, reg, q->iobase + QUADSPI_FR);
if (reg & QUADSPI_FR_TFF_MASK) if (reg & QUADSPI_FR_TFF_MASK)
complete(&q->c); complete(&q->c);
@ -348,7 +380,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Clear all the LUT table */ /* Clear all the LUT table */
for (i = 0; i < QUADSPI_LUT_NUM; i++) for (i = 0; i < QUADSPI_LUT_NUM; i++)
writel(0, base + QUADSPI_LUT_BASE + i * 4); qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
/* Quad Read */ /* Quad Read */
lut_base = SEQID_QUAD_READ * 4; lut_base = SEQID_QUAD_READ * 4;
@ -364,14 +396,15 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
dummy = 8; dummy = 8;
} }
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
writel(LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo), qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
base + QUADSPI_LUT(lut_base + 1)); base + QUADSPI_LUT(lut_base + 1));
/* Write enable */ /* Write enable */
lut_base = SEQID_WREN * 4; lut_base = SEQID_WREN * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base)); qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WREN),
base + QUADSPI_LUT(lut_base));
/* Page Program */ /* Page Program */
lut_base = SEQID_PP * 4; lut_base = SEQID_PP * 4;
@ -385,13 +418,15 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
addrlen = ADDR32BIT; addrlen = ADDR32BIT;
} }
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
writel(LUT0(FSL_WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
base + QUADSPI_LUT(lut_base + 1));
/* Read Status */ /* Read Status */
lut_base = SEQID_RDSR * 4; lut_base = SEQID_RDSR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(FSL_READ, PAD1, 0x1), qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDSR) |
LUT1(FSL_READ, PAD1, 0x1),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
/* Erase a sector */ /* Erase a sector */
@ -400,40 +435,46 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
cmd = q->nor[0].erase_opcode; cmd = q->nor[0].erase_opcode;
addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT; addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
/* Erase the whole chip */ /* Erase the whole chip */
lut_base = SEQID_CHIP_ERASE * 4; lut_base = SEQID_CHIP_ERASE * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
/* READ ID */ /* READ ID */
lut_base = SEQID_RDID * 4; lut_base = SEQID_RDID * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(FSL_READ, PAD1, 0x8), qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDID) |
LUT1(FSL_READ, PAD1, 0x8),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
/* Write Register */ /* Write Register */
lut_base = SEQID_WRSR * 4; lut_base = SEQID_WRSR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(FSL_WRITE, PAD1, 0x2), qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRSR) |
LUT1(FSL_WRITE, PAD1, 0x2),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
/* Read Configuration Register */ /* Read Configuration Register */
lut_base = SEQID_RDCR * 4; lut_base = SEQID_RDCR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(FSL_READ, PAD1, 0x1), qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDCR) |
LUT1(FSL_READ, PAD1, 0x1),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
/* Write disable */ /* Write disable */
lut_base = SEQID_WRDI * 4; lut_base = SEQID_WRDI * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_WRDI), base + QUADSPI_LUT(lut_base)); qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRDI),
base + QUADSPI_LUT(lut_base));
/* Enter 4 Byte Mode (Micron) */ /* Enter 4 Byte Mode (Micron) */
lut_base = SEQID_EN4B * 4; lut_base = SEQID_EN4B * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_EN4B), base + QUADSPI_LUT(lut_base)); qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_EN4B),
base + QUADSPI_LUT(lut_base));
/* Enter 4 Byte Mode (Spansion) */ /* Enter 4 Byte Mode (Spansion) */
lut_base = SEQID_BRWR * 4; lut_base = SEQID_BRWR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_BRWR), base + QUADSPI_LUT(lut_base)); qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR),
base + QUADSPI_LUT(lut_base));
fsl_qspi_lock_lut(q); fsl_qspi_lock_lut(q);
} }
@ -488,15 +529,16 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
q->chip_base_addr, addr, len, cmd); q->chip_base_addr, addr, len, cmd);
/* save the reg */ /* save the reg */
reg = readl(base + QUADSPI_MCR); reg = qspi_readl(q, base + QUADSPI_MCR);
writel(q->memmap_phy + q->chip_base_addr + addr, base + QUADSPI_SFAR); qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr,
writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, base + QUADSPI_SFAR);
qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
base + QUADSPI_RBCT); base + QUADSPI_RBCT);
writel(reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); qspi_writel(q, reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR);
do { do {
reg2 = readl(base + QUADSPI_SR); reg2 = qspi_readl(q, base + QUADSPI_SR);
if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) { if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) {
udelay(1); udelay(1);
dev_dbg(q->dev, "The controller is busy, 0x%x\n", reg2); dev_dbg(q->dev, "The controller is busy, 0x%x\n", reg2);
@ -507,21 +549,22 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
/* trigger the LUT now */ /* trigger the LUT now */
seqid = fsl_qspi_get_seqid(q, cmd); seqid = fsl_qspi_get_seqid(q, cmd);
writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR); qspi_writel(q, (seqid << QUADSPI_IPCR_SEQID_SHIFT) | len,
base + QUADSPI_IPCR);
/* Wait for the interrupt. */ /* Wait for the interrupt. */
if (!wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000))) { if (!wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000))) {
dev_err(q->dev, dev_err(q->dev,
"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n", "cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
cmd, addr, readl(base + QUADSPI_FR), cmd, addr, qspi_readl(q, base + QUADSPI_FR),
readl(base + QUADSPI_SR)); qspi_readl(q, base + QUADSPI_SR));
err = -ETIMEDOUT; err = -ETIMEDOUT;
} else { } else {
err = 0; err = 0;
} }
/* restore the MCR */ /* restore the MCR */
writel(reg, base + QUADSPI_MCR); qspi_writel(q, reg, base + QUADSPI_MCR);
return err; return err;
} }
@ -533,7 +576,7 @@ static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf)
int i = 0; int i = 0;
while (len > 0) { while (len > 0) {
tmp = readl(q->iobase + QUADSPI_RBDR + i * 4); tmp = qspi_readl(q, q->iobase + QUADSPI_RBDR + i * 4);
tmp = fsl_qspi_endian_xchg(q, tmp); tmp = fsl_qspi_endian_xchg(q, tmp);
dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n", dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n",
q->chip_base_addr, tmp); q->chip_base_addr, tmp);
@ -561,9 +604,9 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q)
{ {
u32 reg; u32 reg;
reg = readl(q->iobase + QUADSPI_MCR); reg = qspi_readl(q, q->iobase + QUADSPI_MCR);
reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK; reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
writel(reg, q->iobase + QUADSPI_MCR); qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
/* /*
* The minimum delay : 1 AHB + 2 SFCK clocks. * The minimum delay : 1 AHB + 2 SFCK clocks.
@ -572,7 +615,7 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q)
udelay(1); udelay(1);
reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK); reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
writel(reg, q->iobase + QUADSPI_MCR); qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
} }
static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
@ -586,20 +629,20 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
q->chip_base_addr, to, count); q->chip_base_addr, to, count);
/* clear the TX FIFO. */ /* clear the TX FIFO. */
tmp = readl(q->iobase + QUADSPI_MCR); tmp = qspi_readl(q, q->iobase + QUADSPI_MCR);
writel(tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR); qspi_writel(q, tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR);
/* fill the TX data to the FIFO */ /* fill the TX data to the FIFO */
for (j = 0, i = ((count + 3) / 4); j < i; j++) { for (j = 0, i = ((count + 3) / 4); j < i; j++) {
tmp = fsl_qspi_endian_xchg(q, *txbuf); tmp = fsl_qspi_endian_xchg(q, *txbuf);
writel(tmp, q->iobase + QUADSPI_TBDR); qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR);
txbuf++; txbuf++;
} }
/* fill the TXFIFO upto 16 bytes for i.MX7d */ /* fill the TXFIFO upto 16 bytes for i.MX7d */
if (needs_fill_txfifo(q)) if (needs_fill_txfifo(q))
for (; i < 4; i++) for (; i < 4; i++)
writel(tmp, q->iobase + QUADSPI_TBDR); qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR);
/* Trigger it */ /* Trigger it */
ret = fsl_qspi_runcmd(q, opcode, to, count); ret = fsl_qspi_runcmd(q, opcode, to, count);
@ -615,10 +658,10 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q)
int nor_size = q->nor_size; int nor_size = q->nor_size;
void __iomem *base = q->iobase; void __iomem *base = q->iobase;
writel(nor_size + q->memmap_phy, base + QUADSPI_SFA1AD); qspi_writel(q, nor_size + q->memmap_phy, base + QUADSPI_SFA1AD);
writel(nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD); qspi_writel(q, nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD);
writel(nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD); qspi_writel(q, nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD);
writel(nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD); qspi_writel(q, nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD);
} }
/* /*
@ -640,24 +683,26 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
int seqid; int seqid;
/* AHB configuration for access buffer 0/1/2 .*/ /* AHB configuration for access buffer 0/1/2 .*/
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
/* /*
* Set ADATSZ with the maximum AHB buffer size to improve the * Set ADATSZ with the maximum AHB buffer size to improve the
* read performance. * read performance.
*/ */
writel(QUADSPI_BUF3CR_ALLMST_MASK | ((q->devtype_data->ahb_buf_size / 8) qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK |
<< QUADSPI_BUF3CR_ADATSZ_SHIFT), base + QUADSPI_BUF3CR); ((q->devtype_data->ahb_buf_size / 8)
<< QUADSPI_BUF3CR_ADATSZ_SHIFT),
base + QUADSPI_BUF3CR);
/* We only use the buffer3 */ /* We only use the buffer3 */
writel(0, base + QUADSPI_BUF0IND); qspi_writel(q, 0, base + QUADSPI_BUF0IND);
writel(0, base + QUADSPI_BUF1IND); qspi_writel(q, 0, base + QUADSPI_BUF1IND);
writel(0, base + QUADSPI_BUF2IND); qspi_writel(q, 0, base + QUADSPI_BUF2IND);
/* Set the default lut sequence for AHB Read. */ /* Set the default lut sequence for AHB Read. */
seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode); seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode);
writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT, qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
q->iobase + QUADSPI_BFGENCR); q->iobase + QUADSPI_BFGENCR);
} }
@ -713,7 +758,7 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
return ret; return ret;
/* Reset the module */ /* Reset the module */
writel(QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK, qspi_writel(q, QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
base + QUADSPI_MCR); base + QUADSPI_MCR);
udelay(1); udelay(1);
@ -721,24 +766,24 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
fsl_qspi_init_lut(q); fsl_qspi_init_lut(q);
/* Disable the module */ /* Disable the module */
writel(QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, qspi_writel(q, QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK,
base + QUADSPI_MCR); base + QUADSPI_MCR);
reg = readl(base + QUADSPI_SMPR); reg = qspi_readl(q, base + QUADSPI_SMPR);
writel(reg & ~(QUADSPI_SMPR_FSDLY_MASK qspi_writel(q, reg & ~(QUADSPI_SMPR_FSDLY_MASK
| QUADSPI_SMPR_FSPHS_MASK | QUADSPI_SMPR_FSPHS_MASK
| QUADSPI_SMPR_HSENA_MASK | QUADSPI_SMPR_HSENA_MASK
| QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR); | QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR);
/* Enable the module */ /* Enable the module */
writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, qspi_writel(q, QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
base + QUADSPI_MCR); base + QUADSPI_MCR);
/* clear all interrupt status */ /* clear all interrupt status */
writel(0xffffffff, q->iobase + QUADSPI_FR); qspi_writel(q, 0xffffffff, q->iobase + QUADSPI_FR);
/* enable the interrupt */ /* enable the interrupt */
writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); qspi_writel(q, QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
return 0; return 0;
} }
@ -776,6 +821,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
{ .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, }, { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, },
{ .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, }, { .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
{ .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
@ -954,6 +1000,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (IS_ERR(q->iobase)) if (IS_ERR(q->iobase))
return PTR_ERR(q->iobase); return PTR_ERR(q->iobase);
q->big_endian = of_property_read_bool(np, "big-endian");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"QuadSPI-memory"); "QuadSPI-memory");
if (!devm_request_mem_region(dev, res->start, resource_size(res), if (!devm_request_mem_region(dev, res->start, resource_size(res),
@ -1101,8 +1148,8 @@ static int fsl_qspi_remove(struct platform_device *pdev)
} }
/* disable the hardware */ /* disable the hardware */
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
writel(0x0, q->iobase + QUADSPI_RSER); qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
mutex_destroy(&q->lock); mutex_destroy(&q->lock);

View file

@ -371,8 +371,8 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
return ret; return ret;
} }
static int __init mtk_nor_init(struct mt8173_nor *mt8173_nor, static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
struct device_node *flash_node) struct device_node *flash_node)
{ {
int ret; int ret;
struct spi_nor *nor; struct spi_nor *nor;

View file

@ -61,14 +61,20 @@ struct flash_info {
u16 addr_width; u16 addr_width;
u16 flags; u16 flags;
#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */ #define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ #define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
#define SST_WRITE 0x04 /* use SST byte programming */ #define SST_WRITE BIT(2) /* use SST byte programming */
#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ #define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */
#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ #define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ #define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
#define USE_FSR 0x80 /* use flag status register */ #define USE_FSR BIT(7) /* use flag status register */
#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */
#define SPI_NOR_HAS_TB BIT(9) /*
* Flash SR has Top/Bottom (TB) protect
* bit. Must be used with
* SPI_NOR_HAS_LOCK.
*/
}; };
#define JEDEC_MFR(info) ((info)->id[0]) #define JEDEC_MFR(info) ((info)->id[0])
@ -434,32 +440,58 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
} else { } else {
pow = ((sr & mask) ^ mask) >> shift; pow = ((sr & mask) ^ mask) >> shift;
*len = mtd->size >> pow; *len = mtd->size >> pow;
*ofs = mtd->size - *len; if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)
*ofs = 0;
else
*ofs = mtd->size - *len;
} }
} }
/* /*
* Return 1 if the entire region is locked, 0 otherwise * Return 1 if the entire region is locked (if @locked is true) or unlocked (if
* @locked is false); 0 otherwise
*/ */
static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
u8 sr) u8 sr, bool locked)
{ {
loff_t lock_offs; loff_t lock_offs;
uint64_t lock_len; uint64_t lock_len;
if (!len)
return 1;
stm_get_locked_range(nor, sr, &lock_offs, &lock_len); stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); if (locked)
/* Requested range is a sub-range of locked range */
return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
else
/* Requested range does not overlap with locked range */
return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
}
static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
u8 sr)
{
return stm_check_lock_status_sr(nor, ofs, len, sr, true);
}
static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
u8 sr)
{
return stm_check_lock_status_sr(nor, ofs, len, sr, false);
} }
/* /*
* Lock a region of the flash. Compatible with ST Micro and similar flash. * Lock a region of the flash. Compatible with ST Micro and similar flash.
* Supports only the block protection bits BP{0,1,2} in the status register * Supports the block protection bits BP{0,1,2} in the status register
* (SR). Does not support these features found in newer SR bitfields: * (SR). Does not support these features found in newer SR bitfields:
* - TB: top/bottom protect - only handle TB=0 (top protect)
* - SEC: sector/block protect - only handle SEC=0 (block protect) * - SEC: sector/block protect - only handle SEC=0 (block protect)
* - CMP: complement protect - only support CMP=0 (range is not complemented) * - CMP: complement protect - only support CMP=0 (range is not complemented)
* *
* Support for the following is provided conditionally for some flash:
* - TB: top/bottom protect
*
* Sample table portion for 8MB flash (Winbond w25q64fw): * Sample table portion for 8MB flash (Winbond w25q64fw):
* *
* SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion
@ -472,6 +504,13 @@ static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
* 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4
* 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2
* X | X | 1 | 1 | 1 | 8 MB | ALL * X | X | 1 | 1 | 1 | 8 MB | ALL
* ------|-------|-------|-------|-------|---------------|-------------------
* 0 | 1 | 0 | 0 | 1 | 128 KB | Lower 1/64
* 0 | 1 | 0 | 1 | 0 | 256 KB | Lower 1/32
* 0 | 1 | 0 | 1 | 1 | 512 KB | Lower 1/16
* 0 | 1 | 1 | 0 | 0 | 1 MB | Lower 1/8
* 0 | 1 | 1 | 0 | 1 | 2 MB | Lower 1/4
* 0 | 1 | 1 | 1 | 0 | 4 MB | Lower 1/2
* *
* Returns negative on errors, 0 on success. * Returns negative on errors, 0 on success.
*/ */
@ -481,20 +520,39 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
int status_old, status_new; int status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 shift = ffs(mask) - 1, pow, val; u8 shift = ffs(mask) - 1, pow, val;
loff_t lock_len;
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
bool use_top;
int ret; int ret;
status_old = read_sr(nor); status_old = read_sr(nor);
if (status_old < 0) if (status_old < 0)
return status_old; return status_old;
/* SPI NOR always locks to the end */ /* If nothing in our range is unlocked, we don't need to do anything */
if (ofs + len != mtd->size) { if (stm_is_locked_sr(nor, ofs, len, status_old))
/* Does combined region extend to end? */ return 0;
if (!stm_is_locked_sr(nor, ofs + len, mtd->size - ofs - len,
status_old)) /* If anything below us is unlocked, we can't use 'bottom' protection */
return -EINVAL; if (!stm_is_locked_sr(nor, 0, ofs, status_old))
len = mtd->size - ofs; can_be_bottom = false;
}
/* If anything above us is unlocked, we can't use 'top' protection */
if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),
status_old))
can_be_top = false;
if (!can_be_bottom && !can_be_top)
return -EINVAL;
/* Prefer top, if both are valid */
use_top = can_be_top;
/* lock_len: length of region that should end up locked */
if (use_top)
lock_len = mtd->size - ofs;
else
lock_len = ofs + len;
/* /*
* Need smallest pow such that: * Need smallest pow such that:
@ -505,7 +563,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
* *
* pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
*/ */
pow = ilog2(mtd->size) - ilog2(len); pow = ilog2(mtd->size) - ilog2(lock_len);
val = mask - (pow << shift); val = mask - (pow << shift);
if (val & ~mask) if (val & ~mask)
return -EINVAL; return -EINVAL;
@ -513,10 +571,20 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
if (!(val & mask)) if (!(val & mask))
return -EINVAL; return -EINVAL;
status_new = (status_old & ~mask) | val; status_new = (status_old & ~mask & ~SR_TB) | val;
/* Disallow further writes if WP pin is asserted */
status_new |= SR_SRWD;
if (!use_top)
status_new |= SR_TB;
/* Don't bother if they're the same */
if (status_new == status_old)
return 0;
/* Only modify protection if it will not unlock other areas */ /* Only modify protection if it will not unlock other areas */
if ((status_new & mask) <= (status_old & mask)) if ((status_new & mask) < (status_old & mask))
return -EINVAL; return -EINVAL;
write_enable(nor); write_enable(nor);
@ -537,17 +605,40 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
int status_old, status_new; int status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 shift = ffs(mask) - 1, pow, val; u8 shift = ffs(mask) - 1, pow, val;
loff_t lock_len;
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
bool use_top;
int ret; int ret;
status_old = read_sr(nor); status_old = read_sr(nor);
if (status_old < 0) if (status_old < 0)
return status_old; return status_old;
/* Cannot unlock; would unlock larger region than requested */ /* If nothing in our range is locked, we don't need to do anything */
if (stm_is_locked_sr(nor, ofs - mtd->erasesize, mtd->erasesize, if (stm_is_unlocked_sr(nor, ofs, len, status_old))
status_old)) return 0;
/* If anything below us is locked, we can't use 'top' protection */
if (!stm_is_unlocked_sr(nor, 0, ofs, status_old))
can_be_top = false;
/* If anything above us is locked, we can't use 'bottom' protection */
if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),
status_old))
can_be_bottom = false;
if (!can_be_bottom && !can_be_top)
return -EINVAL; return -EINVAL;
/* Prefer top, if both are valid */
use_top = can_be_top;
/* lock_len: length of region that should remain locked */
if (use_top)
lock_len = mtd->size - (ofs + len);
else
lock_len = ofs;
/* /*
* Need largest pow such that: * Need largest pow such that:
* *
@ -557,8 +648,8 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
* *
* pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
*/ */
pow = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len)); pow = ilog2(mtd->size) - order_base_2(lock_len);
if (ofs + len == mtd->size) { if (lock_len == 0) {
val = 0; /* fully unlocked */ val = 0; /* fully unlocked */
} else { } else {
val = mask - (pow << shift); val = mask - (pow << shift);
@ -567,10 +658,21 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return -EINVAL; return -EINVAL;
} }
status_new = (status_old & ~mask) | val; status_new = (status_old & ~mask & ~SR_TB) | val;
/* Don't protect status register if we're fully unlocked */
if (lock_len == mtd->size)
status_new &= ~SR_SRWD;
if (!use_top)
status_new |= SR_TB;
/* Don't bother if they're the same */
if (status_new == status_old)
return 0;
/* Only modify protection if it will not lock other areas */ /* Only modify protection if it will not lock other areas */
if ((status_new & mask) >= (status_old & mask)) if ((status_new & mask) > (status_old & mask))
return -EINVAL; return -EINVAL;
write_enable(nor); write_enable(nor);
@ -762,8 +864,8 @@ static const struct flash_info spi_nor_ids[] = {
{ "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
@ -797,6 +899,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) }, { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
{ "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) }, { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) },
@ -860,11 +963,23 @@ static const struct flash_info spi_nor_ids[] = {
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, {
"w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, {
{ "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
"w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
@ -1100,45 +1215,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0; return 0;
} }
static int micron_quad_enable(struct spi_nor *nor)
{
int ret;
u8 val;
ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
if (ret < 0) {
dev_err(nor->dev, "error %d reading EVCR\n", ret);
return ret;
}
write_enable(nor);
/* set EVCR, enable quad I/O */
nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1);
if (ret < 0) {
dev_err(nor->dev, "error while writing EVCR register\n");
return ret;
}
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
/* read EVCR and check it */
ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
if (ret < 0) {
dev_err(nor->dev, "error %d reading EVCR\n", ret);
return ret;
}
if (val & EVCR_QUAD_EN_MICRON) {
dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
return -EINVAL;
}
return 0;
}
static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
{ {
int status; int status;
@ -1152,12 +1228,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
} }
return status; return status;
case SNOR_MFR_MICRON: case SNOR_MFR_MICRON:
status = micron_quad_enable(nor); return 0;
if (status) {
dev_err(nor->dev, "Micron quad-read not enabled\n");
return -EINVAL;
}
return status;
default: default:
status = spansion_quad_enable(nor); status = spansion_quad_enable(nor);
if (status) { if (status) {
@ -1233,9 +1304,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
JEDEC_MFR(info) == SNOR_MFR_INTEL || JEDEC_MFR(info) == SNOR_MFR_INTEL ||
JEDEC_MFR(info) == SNOR_MFR_SST) { JEDEC_MFR(info) == SNOR_MFR_SST ||
info->flags & SPI_NOR_HAS_LOCK) {
write_enable(nor); write_enable(nor);
write_sr(nor, 0); write_sr(nor, 0);
spi_nor_wait_till_ready(nor);
} }
if (!mtd->name) if (!mtd->name)
@ -1249,7 +1322,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
mtd->_read = spi_nor_read; mtd->_read = spi_nor_read;
/* NOR protection support for STmicro/Micron chips and similar */ /* NOR protection support for STmicro/Micron chips and similar */
if (JEDEC_MFR(info) == SNOR_MFR_MICRON) { if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
info->flags & SPI_NOR_HAS_LOCK) {
nor->flash_lock = stm_lock; nor->flash_lock = stm_lock;
nor->flash_unlock = stm_unlock; nor->flash_unlock = stm_unlock;
nor->flash_is_locked = stm_is_locked; nor->flash_is_locked = stm_is_locked;
@ -1269,6 +1343,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (info->flags & USE_FSR) if (info->flags & USE_FSR)
nor->flags |= SNOR_F_USE_FSR; nor->flags |= SNOR_F_USE_FSR;
if (info->flags & SPI_NOR_HAS_TB)
nor->flags |= SNOR_F_HAS_SR_TB;
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
/* prefer "small sector" erase if possible */ /* prefer "small sector" erase if possible */

View file

@ -215,19 +215,19 @@ static int verify_eraseblock(int ebnum)
pr_info("ignoring error as within bitflip_limit\n"); pr_info("ignoring error as within bitflip_limit\n");
} }
if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) { if (use_offset != 0 || use_len < mtd->oobavail) {
int k; int k;
ops.mode = MTD_OPS_AUTO_OOB; ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0; ops.len = 0;
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail; ops.ooblen = mtd->oobavail;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = 0; ops.ooboffs = 0;
ops.datbuf = NULL; ops.datbuf = NULL;
ops.oobbuf = readbuf; ops.oobbuf = readbuf;
err = mtd_read_oob(mtd, addr, &ops); err = mtd_read_oob(mtd, addr, &ops);
if (err || ops.oobretlen != mtd->ecclayout->oobavail) { if (err || ops.oobretlen != mtd->oobavail) {
pr_err("error: readoob failed at %#llx\n", pr_err("error: readoob failed at %#llx\n",
(long long)addr); (long long)addr);
errcnt += 1; errcnt += 1;
@ -244,7 +244,7 @@ static int verify_eraseblock(int ebnum)
/* verify post-(use_offset + use_len) area for 0xff */ /* verify post-(use_offset + use_len) area for 0xff */
k = use_offset + use_len; k = use_offset + use_len;
bitflips += memffshow(addr, k, readbuf + k, bitflips += memffshow(addr, k, readbuf + k,
mtd->ecclayout->oobavail - k); mtd->oobavail - k);
if (bitflips > bitflip_limit) { if (bitflips > bitflip_limit) {
pr_err("error: verify failed at %#llx\n", pr_err("error: verify failed at %#llx\n",
@ -269,8 +269,8 @@ static int verify_eraseblock_in_one_go(int ebnum)
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
int err = 0; int err = 0;
loff_t addr = (loff_t)ebnum * mtd->erasesize; loff_t addr = (loff_t)ebnum * mtd->erasesize;
size_t len = mtd->ecclayout->oobavail * pgcnt; size_t len = mtd->oobavail * pgcnt;
size_t oobavail = mtd->ecclayout->oobavail; size_t oobavail = mtd->oobavail;
size_t bitflips; size_t bitflips;
int i; int i;
@ -394,8 +394,8 @@ static int __init mtd_oobtest_init(void)
goto out; goto out;
use_offset = 0; use_offset = 0;
use_len = mtd->ecclayout->oobavail; use_len = mtd->oobavail;
use_len_max = mtd->ecclayout->oobavail; use_len_max = mtd->oobavail;
vary_offset = 0; vary_offset = 0;
/* First test: write all OOB, read it back and verify */ /* First test: write all OOB, read it back and verify */
@ -460,8 +460,8 @@ static int __init mtd_oobtest_init(void)
/* Write all eraseblocks */ /* Write all eraseblocks */
use_offset = 0; use_offset = 0;
use_len = mtd->ecclayout->oobavail; use_len = mtd->oobavail;
use_len_max = mtd->ecclayout->oobavail; use_len_max = mtd->oobavail;
vary_offset = 1; vary_offset = 1;
prandom_seed_state(&rnd_state, 5); prandom_seed_state(&rnd_state, 5);
@ -471,8 +471,8 @@ static int __init mtd_oobtest_init(void)
/* Check all eraseblocks */ /* Check all eraseblocks */
use_offset = 0; use_offset = 0;
use_len = mtd->ecclayout->oobavail; use_len = mtd->oobavail;
use_len_max = mtd->ecclayout->oobavail; use_len_max = mtd->oobavail;
vary_offset = 1; vary_offset = 1;
prandom_seed_state(&rnd_state, 5); prandom_seed_state(&rnd_state, 5);
err = verify_all_eraseblocks(); err = verify_all_eraseblocks();
@ -480,8 +480,8 @@ static int __init mtd_oobtest_init(void)
goto out; goto out;
use_offset = 0; use_offset = 0;
use_len = mtd->ecclayout->oobavail; use_len = mtd->oobavail;
use_len_max = mtd->ecclayout->oobavail; use_len_max = mtd->oobavail;
vary_offset = 0; vary_offset = 0;
/* Fourth test: try to write off end of device */ /* Fourth test: try to write off end of device */
@ -501,7 +501,7 @@ static int __init mtd_oobtest_init(void)
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = 1; ops.ooblen = 1;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = mtd->ecclayout->oobavail; ops.ooboffs = mtd->oobavail;
ops.datbuf = NULL; ops.datbuf = NULL;
ops.oobbuf = writebuf; ops.oobbuf = writebuf;
pr_info("attempting to start write past end of OOB\n"); pr_info("attempting to start write past end of OOB\n");
@ -521,7 +521,7 @@ static int __init mtd_oobtest_init(void)
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = 1; ops.ooblen = 1;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = mtd->ecclayout->oobavail; ops.ooboffs = mtd->oobavail;
ops.datbuf = NULL; ops.datbuf = NULL;
ops.oobbuf = readbuf; ops.oobbuf = readbuf;
pr_info("attempting to start read past end of OOB\n"); pr_info("attempting to start read past end of OOB\n");
@ -543,7 +543,7 @@ static int __init mtd_oobtest_init(void)
ops.mode = MTD_OPS_AUTO_OOB; ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0; ops.len = 0;
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1; ops.ooblen = mtd->oobavail + 1;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = 0; ops.ooboffs = 0;
ops.datbuf = NULL; ops.datbuf = NULL;
@ -563,7 +563,7 @@ static int __init mtd_oobtest_init(void)
ops.mode = MTD_OPS_AUTO_OOB; ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0; ops.len = 0;
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1; ops.ooblen = mtd->oobavail + 1;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = 0; ops.ooboffs = 0;
ops.datbuf = NULL; ops.datbuf = NULL;
@ -587,7 +587,7 @@ static int __init mtd_oobtest_init(void)
ops.mode = MTD_OPS_AUTO_OOB; ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0; ops.len = 0;
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail; ops.ooblen = mtd->oobavail;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = 1; ops.ooboffs = 1;
ops.datbuf = NULL; ops.datbuf = NULL;
@ -607,7 +607,7 @@ static int __init mtd_oobtest_init(void)
ops.mode = MTD_OPS_AUTO_OOB; ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0; ops.len = 0;
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail; ops.ooblen = mtd->oobavail;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = 1; ops.ooboffs = 1;
ops.datbuf = NULL; ops.datbuf = NULL;
@ -638,7 +638,7 @@ static int __init mtd_oobtest_init(void)
for (i = 0; i < ebcnt - 1; ++i) { for (i = 0; i < ebcnt - 1; ++i) {
int cnt = 2; int cnt = 2;
int pg; int pg;
size_t sz = mtd->ecclayout->oobavail; size_t sz = mtd->oobavail;
if (bbt[i] || bbt[i + 1]) if (bbt[i] || bbt[i + 1])
continue; continue;
addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize; addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
@ -673,13 +673,12 @@ static int __init mtd_oobtest_init(void)
for (i = 0; i < ebcnt - 1; ++i) { for (i = 0; i < ebcnt - 1; ++i) {
if (bbt[i] || bbt[i + 1]) if (bbt[i] || bbt[i + 1])
continue; continue;
prandom_bytes_state(&rnd_state, writebuf, prandom_bytes_state(&rnd_state, writebuf, mtd->oobavail * 2);
mtd->ecclayout->oobavail * 2);
addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize; addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
ops.mode = MTD_OPS_AUTO_OOB; ops.mode = MTD_OPS_AUTO_OOB;
ops.len = 0; ops.len = 0;
ops.retlen = 0; ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2; ops.ooblen = mtd->oobavail * 2;
ops.oobretlen = 0; ops.oobretlen = 0;
ops.ooboffs = 0; ops.ooboffs = 0;
ops.datbuf = NULL; ops.datbuf = NULL;
@ -688,7 +687,7 @@ static int __init mtd_oobtest_init(void)
if (err) if (err)
goto out; goto out;
if (memcmpshow(addr, readbuf, writebuf, if (memcmpshow(addr, readbuf, writebuf,
mtd->ecclayout->oobavail * 2)) { mtd->oobavail * 2)) {
pr_err("error: verify failed at %#llx\n", pr_err("error: verify failed at %#llx\n",
(long long)addr); (long long)addr);
errcnt += 1; errcnt += 1;

View file

@ -49,7 +49,6 @@ static struct nand_ecclayout spinand_oob_64 = {
17, 18, 19, 20, 21, 22, 17, 18, 19, 20, 21, 22,
33, 34, 35, 36, 37, 38, 33, 34, 35, 36, 37, 38,
49, 50, 51, 52, 53, 54, }, 49, 50, 51, 52, 53, 54, },
.oobavail = 32,
.oobfree = { .oobfree = {
{.offset = 8, {.offset = 8,
.length = 8}, .length = 8},

View file

@ -78,7 +78,6 @@
#define BL_ALL_UNLOCKED 0 #define BL_ALL_UNLOCKED 0
struct spinand_info { struct spinand_info {
struct nand_ecclayout *ecclayout;
struct spi_device *spi; struct spi_device *spi;
void *priv; void *priv;
}; };

View file

@ -134,38 +134,60 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
if (mutex_lock_interruptible(&c->alloc_sem)) if (mutex_lock_interruptible(&c->alloc_sem))
return -EINTR; return -EINTR;
for (;;) { for (;;) {
/* We can't start doing GC until we've finished checking
the node CRCs etc. */
int bucket, want_ino;
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
if (!c->unchecked_size) if (!c->unchecked_size)
break; break;
/* We can't start doing GC yet. We haven't finished checking
the node CRCs etc. Do it now. */
/* checked_ino is protected by the alloc_sem */
if (c->checked_ino > c->highest_ino && xattr) {
pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n",
c->unchecked_size);
jffs2_dbg_dump_block_lists_nolock(c);
spin_unlock(&c->erase_completion_lock);
mutex_unlock(&c->alloc_sem);
return -ENOSPC;
}
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
if (!xattr) if (!xattr)
xattr = jffs2_verify_xattr(c); xattr = jffs2_verify_xattr(c);
spin_lock(&c->inocache_lock); spin_lock(&c->inocache_lock);
/* Instead of doing the inodes in numeric order, doing a lookup
* in the hash for each possible number, just walk the hash
* buckets of *existing* inodes. This means that we process
* them out-of-order, but it can be a lot faster if there's
* a sparse inode# space. Which there often is. */
want_ino = c->check_ino;
for (bucket = c->check_ino % c->inocache_hashsize ; bucket < c->inocache_hashsize; bucket++) {
for (ic = c->inocache_list[bucket]; ic; ic = ic->next) {
if (ic->ino < want_ino)
continue;
ic = jffs2_get_ino_cache(c, c->checked_ino++); if (ic->state != INO_STATE_CHECKEDABSENT &&
ic->state != INO_STATE_PRESENT)
goto got_next; /* with inocache_lock held */
if (!ic) { jffs2_dbg(1, "Skipping ino #%u already checked\n",
spin_unlock(&c->inocache_lock); ic->ino);
continue; }
want_ino = 0;
} }
/* Point c->check_ino past the end of the last bucket. */
c->check_ino = ((c->highest_ino + c->inocache_hashsize + 1) &
~c->inocache_hashsize) - 1;
spin_unlock(&c->inocache_lock);
pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n",
c->unchecked_size);
jffs2_dbg_dump_block_lists_nolock(c);
mutex_unlock(&c->alloc_sem);
return -ENOSPC;
got_next:
/* For next time round the loop, we want c->checked_ino to indicate
* the *next* one we want to check. And since we're walking the
* buckets rather than doing it sequentially, it's: */
c->check_ino = ic->ino + c->inocache_hashsize;
if (!ic->pino_nlink) { if (!ic->pino_nlink) {
jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n", jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n",
ic->ino); ic->ino);
@ -176,8 +198,6 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
switch(ic->state) { switch(ic->state) {
case INO_STATE_CHECKEDABSENT: case INO_STATE_CHECKEDABSENT:
case INO_STATE_PRESENT: case INO_STATE_PRESENT:
jffs2_dbg(1, "Skipping ino #%u already checked\n",
ic->ino);
spin_unlock(&c->inocache_lock); spin_unlock(&c->inocache_lock);
continue; continue;
@ -196,7 +216,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
ic->ino); ic->ino);
/* We need to come back again for the _same_ inode. We've /* We need to come back again for the _same_ inode. We've
made no progress in this case, but that should be OK */ made no progress in this case, but that should be OK */
c->checked_ino--; c->check_ino = ic->ino;
mutex_unlock(&c->alloc_sem); mutex_unlock(&c->alloc_sem);
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);

View file

@ -49,7 +49,7 @@ struct jffs2_sb_info {
struct mtd_info *mtd; struct mtd_info *mtd;
uint32_t highest_ino; uint32_t highest_ino;
uint32_t checked_ino; uint32_t check_ino; /* *NEXT* inode to be checked */
unsigned int flags; unsigned int flags;

View file

@ -846,8 +846,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
return 1; return 1;
if (c->unchecked_size) { if (c->unchecked_size) {
jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, check_ino #%d\n",
c->unchecked_size, c->checked_ino); c->unchecked_size, c->check_ino);
return 1; return 1;
} }

View file

@ -1183,22 +1183,20 @@ void jffs2_dirty_trigger(struct jffs2_sb_info *c)
int jffs2_nand_flash_setup(struct jffs2_sb_info *c) int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
{ {
struct nand_ecclayout *oinfo = c->mtd->ecclayout;
if (!c->mtd->oobsize) if (!c->mtd->oobsize)
return 0; return 0;
/* Cleanmarker is out-of-band, so inline size zero */ /* Cleanmarker is out-of-band, so inline size zero */
c->cleanmarker_size = 0; c->cleanmarker_size = 0;
if (!oinfo || oinfo->oobavail == 0) { if (c->mtd->oobavail == 0) {
pr_err("inconsistent device description\n"); pr_err("inconsistent device description\n");
return -EINVAL; return -EINVAL;
} }
jffs2_dbg(1, "using OOB on NAND\n"); jffs2_dbg(1, "using OOB on NAND\n");
c->oobavail = oinfo->oobavail; c->oobavail = c->mtd->oobavail;
/* Initialise write buffer */ /* Initialise write buffer */
init_rwsem(&c->wbuf_sem); init_rwsem(&c->wbuf_sem);

View file

@ -166,7 +166,6 @@ struct bbm_info {
}; };
/* OneNAND BBT interface */ /* OneNAND BBT interface */
extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
extern int onenand_default_bbt(struct mtd_info *mtd); extern int onenand_default_bbt(struct mtd_info *mtd);
#endif /* __LINUX_MTD_BBM_H */ #endif /* __LINUX_MTD_BBM_H */

View file

@ -44,7 +44,6 @@ struct INFTLrecord {
unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr; struct erase_info instr;
struct nand_ecclayout oobinfo;
}; };
int INFTL_mount(struct INFTLrecord *s); int INFTL_mount(struct INFTLrecord *s);

View file

@ -240,8 +240,11 @@ struct map_info {
If there is no cache to care about this can be set to NULL. */ If there is no cache to care about this can be set to NULL. */
void (*inval_cache)(struct map_info *, unsigned long, ssize_t); void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
/* set_vpp() must handle being reentered -- enable, enable, disable /* This will be called with 1 as parameter when the first map user
must leave it enabled. */ * needs VPP, and called with 0 when the last user exits. The map
* core maintains a reference counter, and assumes that VPP is a
* global resource applying to all mapped flash chips on the system.
*/
void (*set_vpp)(struct map_info *, int); void (*set_vpp)(struct map_info *, int);
unsigned long pfow_base; unsigned long pfow_base;

View file

@ -105,7 +105,6 @@ struct mtd_oob_ops {
struct nand_ecclayout { struct nand_ecclayout {
__u32 eccbytes; __u32 eccbytes;
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE]; __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
__u32 oobavail;
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
}; };
@ -265,6 +264,11 @@ static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
return mtd->dev.of_node; return mtd->dev.of_node;
} }
static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
{
return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
}
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr); int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
void **virt, resource_size_t *phys); void **virt, resource_size_t *phys);

View file

@ -168,6 +168,12 @@ typedef enum {
/* Device supports subpage reads */ /* Device supports subpage reads */
#define NAND_SUBPAGE_READ 0x00001000 #define NAND_SUBPAGE_READ 0x00001000
/*
* Some MLC NANDs need data scrambling to limit bitflips caused by repeated
* patterns.
*/
#define NAND_NEED_SCRAMBLING 0x00002000
/* Options valid for Samsung large page devices */ /* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG #define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG
@ -666,7 +672,7 @@ struct nand_chip {
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip); void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
int (*dev_ready)(struct mtd_info *mtd); int (*dev_ready)(struct mtd_info *mtd);
@ -896,7 +902,6 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
* @chip_delay: R/B delay value in us * @chip_delay: R/B delay value in us
* @options: Option flags, e.g. 16bit buswidth * @options: Option flags, e.g. 16bit buswidth
* @bbt_options: BBT option flags, e.g. NAND_BBT_USE_FLASH * @bbt_options: BBT option flags, e.g. NAND_BBT_USE_FLASH
* @ecclayout: ECC layout info structure
* @part_probe_types: NULL-terminated array of probe types * @part_probe_types: NULL-terminated array of probe types
*/ */
struct platform_nand_chip { struct platform_nand_chip {
@ -904,7 +909,6 @@ struct platform_nand_chip {
int chip_offset; int chip_offset;
int nr_partitions; int nr_partitions;
struct mtd_partition *partitions; struct mtd_partition *partitions;
struct nand_ecclayout *ecclayout;
int chip_delay; int chip_delay;
unsigned int options; unsigned int options;
unsigned int bbt_options; unsigned int bbt_options;

View file

@ -32,9 +32,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc,
/* /*
* Initialize BCH encoder/decoder * Initialize BCH encoder/decoder
*/ */
struct nand_bch_control * struct nand_bch_control *nand_bch_init(struct mtd_info *mtd);
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
unsigned int eccbytes, struct nand_ecclayout **ecclayout);
/* /*
* Release BCH encoder/decoder resources * Release BCH encoder/decoder resources
*/ */
@ -58,9 +56,7 @@ nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
return -ENOTSUPP; return -ENOTSUPP;
} }
static inline struct nand_bch_control * static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
unsigned int eccbytes, struct nand_ecclayout **ecclayout)
{ {
return NULL; return NULL;
} }

View file

@ -50,7 +50,6 @@ struct NFTLrecord {
unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr; struct erase_info instr;
struct nand_ecclayout oobinfo;
}; };
int NFTL_mount(struct NFTLrecord *s); int NFTL_mount(struct NFTLrecord *s);

View file

@ -85,6 +85,7 @@
#define SR_BP0 BIT(2) /* Block protect 0 */ #define SR_BP0 BIT(2) /* Block protect 0 */
#define SR_BP1 BIT(3) /* Block protect 1 */ #define SR_BP1 BIT(3) /* Block protect 1 */
#define SR_BP2 BIT(4) /* Block protect 2 */ #define SR_BP2 BIT(4) /* Block protect 2 */
#define SR_TB BIT(5) /* Top/Bottom protect */
#define SR_SRWD BIT(7) /* SR write protect */ #define SR_SRWD BIT(7) /* SR write protect */
#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ #define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
@ -116,6 +117,7 @@ enum spi_nor_ops {
enum spi_nor_option_flags { enum spi_nor_option_flags {
SNOR_F_USE_FSR = BIT(0), SNOR_F_USE_FSR = BIT(0),
SNOR_F_HAS_SR_TB = BIT(1),
}; };
/** /**

View file

@ -40,7 +40,6 @@ struct s3c2410_nand_set {
char *name; char *name;
int *nr_map; int *nr_map;
struct mtd_partition *partitions; struct mtd_partition *partitions;
struct nand_ecclayout *ecc_layout;
}; };
struct s3c2410_platform_nand { struct s3c2410_platform_nand {