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:
commit
8f40842e42
@ -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:
|
||||||
|
- compatible : "atmel,sama5d3-nfc" or "atmel,sama5d4-nfc".
|
||||||
- reg : should specify the address and size used for NFC command registers,
|
- reg : should specify the address and size used for NFC command registers,
|
||||||
NFC registers and NFC Sram. NFC Sram address and size can be absent
|
NFC registers and NFC SRAM. NFC SRAM address and size can be absent
|
||||||
if don't want to use it.
|
if don't want to use it.
|
||||||
- clocks: phandle to the peripheral clock
|
- clocks: phandle to the peripheral clock
|
||||||
- Optional properties:
|
Optional properties:
|
||||||
- atmel,write-by-sram: boolean to enable NFC write by sram.
|
- atmel,write-by-sram: boolean to enable NFC write by SRAM.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
nand0: nand@40000000,0 {
|
nand0: nand@40000000,0 {
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
86
Documentation/devicetree/bindings/mtd/qcom_nandc.txt
Normal file
86
Documentation/devicetree/bindings/mtd/qcom_nandc.txt
Normal 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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 *,
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 (retlen != sizeof(struct bcm_tag)) {
|
if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) ||
|
||||||
vfree(buf);
|
rootfsaddr < BCM963XX_EXTENDED_SIZE) {
|
||||||
return -EIO;
|
pr_err("invalid rootfs address: %*ph\n",
|
||||||
|
(int)sizeof(buf->flash_image_start),
|
||||||
|
buf->flash_image_start);
|
||||||
|
goto invalid_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
|
STR_NULL_TERMINATE(buf->kernel_address);
|
||||||
offsetof(struct bcm_tag, header_crc));
|
if (kstrtouint(buf->kernel_address, 10, &kerneladdr) ||
|
||||||
if (computed_crc == buf->header_crc) {
|
kerneladdr < BCM963XX_EXTENDED_SIZE) {
|
||||||
char *boardid = &(buf->board_id[0]);
|
pr_err("invalid kernel address: %*ph\n",
|
||||||
char *tagversion = &(buf->tag_version[0]);
|
(int)sizeof(buf->kernel_address),
|
||||||
|
buf->kernel_address);
|
||||||
|
goto invalid_tag;
|
||||||
|
}
|
||||||
|
|
||||||
sscanf(buf->flash_image_start, "%u", &rootfsaddr);
|
STR_NULL_TERMINATE(buf->kernel_length);
|
||||||
sscanf(buf->kernel_address, "%u", &kerneladdr);
|
if (kstrtouint(buf->kernel_length, 10, &kernellen)) {
|
||||||
sscanf(buf->kernel_length, "%u", &kernellen);
|
pr_err("invalid kernel length: %*ph\n",
|
||||||
sscanf(buf->total_length, "%u", &totallen);
|
(int)sizeof(buf->kernel_length),
|
||||||
|
buf->kernel_length);
|
||||||
|
goto invalid_tag;
|
||||||
|
}
|
||||||
|
|
||||||
pr_info("CFE boot tag found with version %s and board type %s\n",
|
STR_NULL_TERMINATE(buf->total_length);
|
||||||
tagversion, boardid);
|
if (kstrtouint(buf->total_length, 10, &totallen)) {
|
||||||
|
pr_err("invalid total length: %*ph\n",
|
||||||
|
(int)sizeof(buf->total_length),
|
||||||
|
buf->total_length);
|
||||||
|
goto invalid_tag;
|
||||||
|
}
|
||||||
|
|
||||||
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 = {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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. */
|
||||||
|
@ -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;
|
||||||
|
@ -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 set_geometry_by_ecc_info(this);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
return legacy_set_geometry(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
|
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
|
||||||
@ -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,
|
||||||
|
@ -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} },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
@ -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))
|
||||||
|
@ -1373,5 +1373,3 @@ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(nand_scan_bbt);
|
|
||||||
|
@ -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);
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
@ -132,10 +132,22 @@
|
|||||||
|
|
||||||
/* 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:
|
||||||
|
if (info->step_chunk_size)
|
||||||
writesl(info->mmio_base + NDDB,
|
writesl(info->mmio_base + NDDB,
|
||||||
info->data_buff + info->data_buff_pos,
|
info->data_buff + info->data_buff_pos,
|
||||||
DIV_ROUND_UP(do_bytes, 4));
|
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:
|
||||||
|
if (info->step_chunk_size)
|
||||||
drain_fifo(info,
|
drain_fifo(info,
|
||||||
info->data_buff + info->data_buff_pos,
|
info->data_buff + info->data_buff_pos,
|
||||||
DIV_ROUND_UP(do_bytes, 4));
|
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);
|
||||||
|
2223
drivers/mtd/nand/qcom_nandc.c
Normal file
2223
drivers/mtd/nand/qcom_nandc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
if (!randomize)
|
||||||
sunxi_nfc_read_buf(mtd, oob + offset, len);
|
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 */
|
||||||
|
if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
|
||||||
|
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),
|
writel(sunxi_nfc_buf_to_user_data(oob),
|
||||||
nfc->regs + NFC_REG_USER_DATA(0));
|
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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -371,7 +371,7 @@ 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;
|
||||||
|
@ -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;
|
||||||
|
if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)
|
||||||
|
*ofs = 0;
|
||||||
|
else
|
||||||
*ofs = mtd->size - *len;
|
*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);
|
||||||
|
|
||||||
|
if (locked)
|
||||||
|
/* Requested range is a sub-range of locked range */
|
||||||
return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
|
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,
|
|
||||||
|
/* If anything below us is unlocked, we can't use 'bottom' protection */
|
||||||
|
if (!stm_is_locked_sr(nor, 0, ofs, status_old))
|
||||||
|
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))
|
status_old))
|
||||||
|
can_be_top = false;
|
||||||
|
|
||||||
|
if (!can_be_bottom && !can_be_top)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
len = mtd->size - ofs;
|
|
||||||
}
|
/* 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))
|
||||||
|
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))
|
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 */
|
||||||
|
@ -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;
|
||||||
|
@ -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},
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -134,37 +134,59 @@ 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
|
||||||
ic = jffs2_get_ino_cache(c, c->checked_ino++);
|
* in the hash for each possible number, just walk the hash
|
||||||
|
* buckets of *existing* inodes. This means that we process
|
||||||
if (!ic) {
|
* them out-of-order, but it can be a lot faster if there's
|
||||||
spin_unlock(&c->inocache_lock);
|
* 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;
|
continue;
|
||||||
|
|
||||||
|
if (ic->state != INO_STATE_CHECKEDABSENT &&
|
||||||
|
ic->state != INO_STATE_PRESENT)
|
||||||
|
goto got_next; /* with inocache_lock held */
|
||||||
|
|
||||||
|
jffs2_dbg(1, "Skipping ino #%u already checked\n",
|
||||||
|
ic->ino);
|
||||||
}
|
}
|
||||||
|
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",
|
||||||
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user