mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
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
|
||||
|
||||
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,
|
||||
and hardware ECC controller if available.
|
||||
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.
|
||||
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
|
||||
"soft_bch".
|
||||
- atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware.
|
||||
Only supported by at91sam9x5 or later sam9 product.
|
||||
- atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware,
|
||||
capable of BCH encoding and decoding, on devices where it is present.
|
||||
- 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
|
||||
are: 512, 1024.
|
||||
- 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.
|
||||
- nand-bus-width : 8 or 16 bus width if not present 8
|
||||
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
|
||||
- Nand Flash Controller(NFC) is a slave driver under Atmel nand flash
|
||||
- Required properties:
|
||||
- compatible : "atmel,sama5d3-nfc".
|
||||
- reg : should specify the address and size used for NFC command registers,
|
||||
NFC registers and NFC Sram. NFC Sram address and size can be absent
|
||||
if don't want to use it.
|
||||
- clocks: phandle to the peripheral clock
|
||||
- Optional properties:
|
||||
- atmel,write-by-sram: boolean to enable NFC write by sram.
|
||||
|
||||
Nand Flash Controller(NFC) is an optional sub-node
|
||||
Required properties:
|
||||
- compatible : "atmel,sama5d3-nfc" or "atmel,sama5d4-nfc".
|
||||
- reg : should specify the address and size used for NFC command registers,
|
||||
NFC registers and NFC SRAM. NFC SRAM address and size can be absent
|
||||
if don't want to use it.
|
||||
- clocks: phandle to the peripheral clock
|
||||
Optional properties:
|
||||
- atmel,write-by-sram: boolean to enable NFC write by SRAM.
|
||||
|
||||
Examples:
|
||||
nand0: nand@40000000,0 {
|
||||
|
@ -3,7 +3,9 @@
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-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,
|
||||
the second contains the memory mapping address and length
|
||||
- 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
|
||||
bus, you should enable this property.
|
||||
(Please check the board's schematic.)
|
||||
- big-endian : That means the IP register is big endian
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,6 @@ struct jz_nand_platform_data {
|
||||
int num_partitions;
|
||||
struct mtd_partition *partitions;
|
||||
|
||||
struct nand_ecclayout *ecc_layout;
|
||||
|
||||
unsigned char banks[JZ_NAND_NUM_BANKS];
|
||||
|
||||
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 */
|
||||
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 "
|
||||
"for IFC\n");
|
||||
ret = -ENODEV;
|
||||
|
@ -142,7 +142,7 @@ config MTD_AR7_PARTS
|
||||
|
||||
config MTD_BCM63XX_PARTS
|
||||
tristate "BCM63XX CFE partitioning support"
|
||||
depends on BCM63XX
|
||||
depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
|
||||
select CRC32
|
||||
help
|
||||
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;
|
||||
size_t bytes_read;
|
||||
int err;
|
||||
|
||||
if (mtd_read(master, offset, sizeof(buf), &bytes_read,
|
||||
(uint8_t *)&buf) < 0) {
|
||||
pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
|
||||
offset);
|
||||
err = mtd_read(master, offset, sizeof(buf), &bytes_read,
|
||||
(uint8_t *)&buf);
|
||||
if (err && !mtd_is_bitflip(err)) {
|
||||
pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
|
||||
offset, err);
|
||||
goto out_default;
|
||||
}
|
||||
|
||||
@ -95,6 +97,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
|
||||
int trx_part = -1;
|
||||
int last_trx_part = -1;
|
||||
int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
|
||||
int err;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
for (offset = 0; offset <= master->size - blocksize;
|
||||
offset += blocksize) {
|
||||
/* Nothing more in higher memory */
|
||||
if (offset >= 0x2000000)
|
||||
/* Nothing more in higher memory on BCM47XX (MIPS) */
|
||||
if (config_enabled(CONFIG_BCM47XX) && offset >= 0x2000000)
|
||||
break;
|
||||
|
||||
if (curr_part >= BCM47XXPART_MAX_PARTS) {
|
||||
@ -128,10 +131,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
|
||||
}
|
||||
|
||||
/* Read beginning of the block */
|
||||
if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
|
||||
&bytes_read, (uint8_t *)buf) < 0) {
|
||||
pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
|
||||
offset);
|
||||
err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
|
||||
&bytes_read, (uint8_t *)buf);
|
||||
if (err && !mtd_is_bitflip(err)) {
|
||||
pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
|
||||
offset, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -254,10 +258,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
|
||||
}
|
||||
|
||||
/* Read middle of the block */
|
||||
if (mtd_read(master, offset + 0x8000, 0x4,
|
||||
&bytes_read, (uint8_t *)buf) < 0) {
|
||||
pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
|
||||
offset);
|
||||
err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read,
|
||||
(uint8_t *)buf);
|
||||
if (err && !mtd_is_bitflip(err)) {
|
||||
pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
|
||||
offset, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -277,10 +282,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
|
||||
}
|
||||
|
||||
offset = master->size - possible_nvram_sizes[i];
|
||||
if (mtd_read(master, offset, 0x4, &bytes_read,
|
||||
(uint8_t *)buf) < 0) {
|
||||
pr_err("mtd_read error while reading at offset 0x%X!\n",
|
||||
offset);
|
||||
err = mtd_read(master, offset, 0x4, &bytes_read,
|
||||
(uint8_t *)buf);
|
||||
if (err && !mtd_is_bitflip(err)) {
|
||||
pr_err("mtd_read error while reading (offset 0x%X): %d\n",
|
||||
offset, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bcm963xx_nvram.h>
|
||||
#include <linux/bcm963xx_tag.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/module.h>
|
||||
@ -34,12 +35,15 @@
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/mach-bcm63xx/bcm63xx_nvram.h>
|
||||
#include <asm/mach-bcm63xx/board_bcm963xx.h>
|
||||
#define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */
|
||||
|
||||
#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)
|
||||
{
|
||||
@ -58,68 +62,130 @@ static int bcm63xx_detect_cfe(struct mtd_info *master)
|
||||
return 0;
|
||||
|
||||
/* 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);
|
||||
buf[retlen] = 0;
|
||||
|
||||
return strncmp("CFE1CFE1", buf, 8);
|
||||
}
|
||||
|
||||
static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
static int bcm63xx_read_nvram(struct mtd_info *master,
|
||||
struct bcm963xx_nvram *nvram)
|
||||
{
|
||||
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 */
|
||||
int nrparts = 3, curpart = 0;
|
||||
struct bcm_tag *buf;
|
||||
struct bcm_tag *buf = NULL;
|
||||
struct mtd_partition *parts;
|
||||
int ret;
|
||||
size_t retlen;
|
||||
unsigned int rootfsaddr, kerneladdr, spareaddr;
|
||||
unsigned int rootfslen, kernellen, sparelen, totallen;
|
||||
unsigned int cfelen, nvramlen;
|
||||
unsigned int cfe_erasesize;
|
||||
int i;
|
||||
u32 computed_crc;
|
||||
bool rootfs_first = false;
|
||||
|
||||
if (bcm63xx_detect_cfe(master))
|
||||
return -EINVAL;
|
||||
|
||||
cfe_erasesize = max_t(uint32_t, master->erasesize,
|
||||
BCM63XX_CFE_BLOCK_SIZE);
|
||||
BCM963XX_CFE_BLOCK_SIZE);
|
||||
|
||||
cfelen = cfe_erasesize;
|
||||
nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K;
|
||||
nvramlen = nvram->psi_size * SZ_1K;
|
||||
nvramlen = roundup(nvramlen, cfe_erasesize);
|
||||
|
||||
/* Allocate memory for buffer */
|
||||
buf = vmalloc(sizeof(struct bcm_tag));
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get the tag */
|
||||
ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen,
|
||||
(void *)buf);
|
||||
ret = bcm63xx_read_image_tag(master, "rootfs", cfelen, buf);
|
||||
if (!ret) {
|
||||
STR_NULL_TERMINATE(buf->flash_image_start);
|
||||
if (kstrtouint(buf->flash_image_start, 10, &rootfsaddr) ||
|
||||
rootfsaddr < BCM963XX_EXTENDED_SIZE) {
|
||||
pr_err("invalid rootfs address: %*ph\n",
|
||||
(int)sizeof(buf->flash_image_start),
|
||||
buf->flash_image_start);
|
||||
goto invalid_tag;
|
||||
}
|
||||
|
||||
if (retlen != sizeof(struct bcm_tag)) {
|
||||
vfree(buf);
|
||||
return -EIO;
|
||||
}
|
||||
STR_NULL_TERMINATE(buf->kernel_address);
|
||||
if (kstrtouint(buf->kernel_address, 10, &kerneladdr) ||
|
||||
kerneladdr < BCM963XX_EXTENDED_SIZE) {
|
||||
pr_err("invalid kernel address: %*ph\n",
|
||||
(int)sizeof(buf->kernel_address),
|
||||
buf->kernel_address);
|
||||
goto invalid_tag;
|
||||
}
|
||||
|
||||
computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
|
||||
offsetof(struct bcm_tag, header_crc));
|
||||
if (computed_crc == buf->header_crc) {
|
||||
char *boardid = &(buf->board_id[0]);
|
||||
char *tagversion = &(buf->tag_version[0]);
|
||||
STR_NULL_TERMINATE(buf->kernel_length);
|
||||
if (kstrtouint(buf->kernel_length, 10, &kernellen)) {
|
||||
pr_err("invalid kernel length: %*ph\n",
|
||||
(int)sizeof(buf->kernel_length),
|
||||
buf->kernel_length);
|
||||
goto invalid_tag;
|
||||
}
|
||||
|
||||
sscanf(buf->flash_image_start, "%u", &rootfsaddr);
|
||||
sscanf(buf->kernel_address, "%u", &kerneladdr);
|
||||
sscanf(buf->kernel_length, "%u", &kernellen);
|
||||
sscanf(buf->total_length, "%u", &totallen);
|
||||
|
||||
pr_info("CFE boot tag found with version %s and board type %s\n",
|
||||
tagversion, boardid);
|
||||
STR_NULL_TERMINATE(buf->total_length);
|
||||
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;
|
||||
rootfsaddr = rootfsaddr - BCM963XX_EXTENDED_SIZE;
|
||||
@ -134,13 +200,14 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
||||
rootfsaddr = kerneladdr + kernellen;
|
||||
rootfslen = spareaddr - rootfsaddr;
|
||||
}
|
||||
} else {
|
||||
pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n",
|
||||
buf->header_crc, computed_crc);
|
||||
} else if (ret > 0) {
|
||||
invalid_tag:
|
||||
kernellen = 0;
|
||||
rootfslen = 0;
|
||||
rootfsaddr = 0;
|
||||
spareaddr = cfelen;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
sparelen = master->size - spareaddr - nvramlen;
|
||||
|
||||
@ -151,11 +218,10 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
||||
if (kernellen > 0)
|
||||
nrparts++;
|
||||
|
||||
/* Ask kernel for more memory */
|
||||
parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
|
||||
if (!parts) {
|
||||
vfree(buf);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Start building partition list */
|
||||
@ -206,9 +272,43 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
||||
sparelen);
|
||||
|
||||
*pparts = parts;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
vfree(buf);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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 = {
|
||||
|
@ -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)
|
||||
* @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
|
||||
* @oobavail: 8 available bytes remaining after ECC toll
|
||||
*/
|
||||
static struct nand_ecclayout docg3_oobinfo = {
|
||||
.eccbytes = 8,
|
||||
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
|
||||
.oobfree = {{0, 7}, {15, 1} },
|
||||
.oobavail = 8,
|
||||
};
|
||||
|
||||
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;
|
||||
break;
|
||||
case MTD_OPS_AUTO_OOB:
|
||||
oobdelta = mtd->ecclayout->oobavail;
|
||||
oobdelta = mtd->oobavail;
|
||||
break;
|
||||
default:
|
||||
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->_block_isbad = doc_block_isbad;
|
||||
mtd->ecclayout = &docg3_oobinfo;
|
||||
mtd->oobavail = 8;
|
||||
mtd->ecc_strength = DOC_ECC_BCH_T;
|
||||
|
||||
return 0;
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
static unsigned long total_size = CONFIG_MTDRAM_TOTAL_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_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_param(erase_size, ulong, 0);
|
||||
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
|
||||
|
||||
// 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->size = size;
|
||||
mtd->writesize = 1;
|
||||
mtd->writebufsize = 64; /* Mimic CFI NOR flashes */
|
||||
mtd->writebufsize = writebuf_size;
|
||||
mtd->erasesize = MTDRAM_ERASE_SIZE;
|
||||
mtd->priv = mapped_address;
|
||||
|
||||
|
@ -126,10 +126,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
if (ops->oobbuf) {
|
||||
size_t len, pages;
|
||||
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB)
|
||||
len = mtd->oobavail;
|
||||
else
|
||||
len = mtd->oobsize;
|
||||
len = mtd_oobavail(mtd, ops);
|
||||
pages = mtd_div_by_ws(mtd->size, mtd);
|
||||
pages -= mtd_div_by_ws(from, mtd);
|
||||
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))
|
||||
return MTDSWAP_SCANNED_BAD;
|
||||
|
||||
ops.ooblen = 2 * d->mtd->ecclayout->oobavail;
|
||||
ops.ooblen = 2 * d->mtd->oobavail;
|
||||
ops.oobbuf = d->oob_buf;
|
||||
ops.ooboffs = 0;
|
||||
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;
|
||||
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) {
|
||||
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.len = mtd->writesize;
|
||||
ops.ooblen = mtd->ecclayout->oobavail;
|
||||
ops.ooblen = mtd->oobavail;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = d->page_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++) {
|
||||
patt = mtdswap_test_patt(test + i);
|
||||
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);
|
||||
if (ret)
|
||||
goto error;
|
||||
@ -964,7 +964,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
|
||||
if (p1[j] != patt)
|
||||
goto error;
|
||||
|
||||
for (j = 0; j < mtd->ecclayout->oobavail; j++)
|
||||
for (j = 0; j < mtd->oobavail; j++)
|
||||
if (p2[j] != (unsigned char)patt)
|
||||
goto error;
|
||||
|
||||
@ -1387,7 +1387,7 @@ static int mtdswap_init(struct mtdswap_dev *d, unsigned int eblocks,
|
||||
if (!d->page_buf)
|
||||
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)
|
||||
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 int eblocks, eavailable, bad_blocks, spare_cnt;
|
||||
uint64_t swap_size, use_size, size_limit;
|
||||
struct nand_ecclayout *oinfo;
|
||||
int ret;
|
||||
|
||||
parts = &partitions[0];
|
||||
@ -1447,17 +1446,10 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
return;
|
||||
}
|
||||
|
||||
oinfo = mtd->ecclayout;
|
||||
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) {
|
||||
if (!mtd->oobsize || mtd->oobavail < MTDSWAP_OOBSIZE) {
|
||||
printk(KERN_ERR "%s: Not enough free bytes in OOB, "
|
||||
"%d available, %zu needed.\n",
|
||||
MTDSWAP_PREFIX, oinfo->oobavail, MTDSWAP_OOBSIZE);
|
||||
MTDSWAP_PREFIX, mtd->oobavail, MTDSWAP_OOBSIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||
config MTD_NAND_GPIO
|
||||
tristate "GPIO assisted NAND Flash driver"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables a NAND flash driver where control signals are
|
||||
connected to GPIO pins, and commands and data are communicated
|
||||
@ -310,6 +311,7 @@ config MTD_NAND_CAFE
|
||||
config MTD_NAND_CS553X
|
||||
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
|
||||
depends on X86_32
|
||||
depends on !UML && HAS_IOMEM
|
||||
help
|
||||
The CS553x companion chips for the AMD Geode processor
|
||||
include NAND flash controllers with built-in hardware ECC
|
||||
@ -463,6 +465,7 @@ config MTD_NAND_MPC5121_NFC
|
||||
config MTD_NAND_VF610_NFC
|
||||
tristate "Support for Freescale NFC for VF610/MPC5125"
|
||||
depends on (SOC_VF610 || COMPILE_TEST)
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash Controller on some Freescale
|
||||
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
|
||||
@ -553,4 +556,11 @@ config MTD_NAND_HISI504
|
||||
help
|
||||
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
|
||||
|
@ -56,5 +56,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
|
||||
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
|
||||
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
|
||||
|
@ -65,6 +65,11 @@ module_param(on_flash_bbt, int, 0);
|
||||
|
||||
struct atmel_nand_caps {
|
||||
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
|
||||
@ -111,6 +116,7 @@ struct atmel_nfc {
|
||||
/* Point to the sram bank which include readed data via NFC */
|
||||
void *data_in_sram;
|
||||
bool will_write_sram;
|
||||
const struct atmel_nand_nfc_caps *caps;
|
||||
};
|
||||
static struct atmel_nfc nand_nfc;
|
||||
|
||||
@ -140,6 +146,7 @@ struct atmel_nand_host {
|
||||
int pmecc_cw_len; /* Length of codeword */
|
||||
|
||||
void __iomem *pmerrloc_base;
|
||||
void __iomem *pmerrloc_el_base;
|
||||
void __iomem *pmecc_rom_base;
|
||||
|
||||
/* 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
|
||||
* 12-bits 20-bytes 21-bytes
|
||||
* 24-bits 39-bytes 42-bytes
|
||||
* 32-bits 52-bytes 56-bytes
|
||||
*/
|
||||
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;
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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));
|
||||
} else {
|
||||
/* 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);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
@ -1017,6 +1025,9 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
|
||||
case 24:
|
||||
val = PMECC_CFG_BCH_ERR24;
|
||||
break;
|
||||
case 32:
|
||||
val = PMECC_CFG_BCH_ERR32;
|
||||
break;
|
||||
}
|
||||
|
||||
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 (host->pmecc_corr_cap == 0) {
|
||||
if (*cap > host->caps->pmecc_max_correction)
|
||||
return -EINVAL;
|
||||
|
||||
/* use the most fitable ecc bits (the near bigger one ) */
|
||||
if (*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;
|
||||
else if (*cap <= 24)
|
||||
host->pmecc_corr_cap = 24;
|
||||
else if (*cap <= 32)
|
||||
host->pmecc_corr_cap = 32;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1205,6 +1221,8 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
|
||||
err_no = PTR_ERR(host->pmerrloc_base);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
static const struct of_device_id atmel_nand_dt_ids[];
|
||||
|
||||
static int atmel_of_init_port(struct atmel_nand_host *host,
|
||||
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;
|
||||
|
||||
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 (val >= 32) {
|
||||
@ -1547,10 +1563,16 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
|
||||
* them from NAND ONFI parameters.
|
||||
*/
|
||||
if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) {
|
||||
if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
|
||||
(val != 24)) {
|
||||
if (val > host->caps->pmecc_max_correction) {
|
||||
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);
|
||||
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 ((val != 512) && (val != 1024)) {
|
||||
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);
|
||||
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);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
if (pending & NFC_SR_RB_EDGE) {
|
||||
if (pending & host->nfc->caps->rb_mask) {
|
||||
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;
|
||||
}
|
||||
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)
|
||||
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);
|
||||
|
||||
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)
|
||||
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;
|
||||
|
||||
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",
|
||||
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)
|
||||
@ -1956,8 +1978,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
nfc_prepare_interrupt(host, NFC_SR_RB_EDGE);
|
||||
nfc_wait_interrupt(host, NFC_SR_RB_EDGE);
|
||||
nfc_prepare_interrupt(host, host->nfc->caps->rb_mask);
|
||||
nfc_wait_interrupt(host, host->nfc->caps->rb_mask);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2304,17 +2326,34 @@ static int atmel_nand_remove(struct platform_device *pdev)
|
||||
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 = {
|
||||
.pmecc_correct_erase_page = false,
|
||||
.pmecc_max_correction = 24,
|
||||
};
|
||||
|
||||
static const struct atmel_nand_caps sama5d4_caps = {
|
||||
.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[] = {
|
||||
{ .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps },
|
||||
{ .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps },
|
||||
{ .compatible = "atmel,sama5d2-nand", .data = &sama5d2_caps },
|
||||
{ /* 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_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;
|
||||
}
|
||||
|
||||
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[] = {
|
||||
{ .compatible = "atmel,sama5d3-nfc" },
|
||||
{ .compatible = "atmel,sama5d3-nfc", .data = &sama5d3_nfc_caps },
|
||||
{ .compatible = "atmel,sama5d4-nfc", .data = &sama5d4_nfc_caps },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atmel_nand_nfc_match);
|
||||
|
@ -43,6 +43,7 @@
|
||||
#define PMECC_CFG_BCH_ERR8 (2 << 0)
|
||||
#define PMECC_CFG_BCH_ERR12 (3 << 0)
|
||||
#define PMECC_CFG_BCH_ERR24 (4 << 0)
|
||||
#define PMECC_CFG_BCH_ERR32 (5 << 0)
|
||||
|
||||
#define PMECC_CFG_SECTOR512 (0 << 4)
|
||||
#define PMECC_CFG_SECTOR1024 (1 << 4)
|
||||
@ -108,7 +109,11 @@
|
||||
#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
|
||||
#define PMERRLOC_CALC_DONE (1 << 0)
|
||||
#define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */
|
||||
#define ATMEL_PMERRLOC_ELx 0x08c /* Error location x */
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
#define pmecc_readl_relaxed(addr, reg) \
|
||||
@ -136,7 +141,7 @@
|
||||
readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
|
||||
|
||||
#define pmerrloc_readl_el_relaxed(addr, n) \
|
||||
readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4))
|
||||
readl_relaxed((addr) + ((n) * 4))
|
||||
|
||||
/* Galois field dimension */
|
||||
#define PMECC_GF_DIMENSION_13 13
|
||||
|
@ -42,7 +42,8 @@
|
||||
#define NFC_SR_UNDEF (1 << 21)
|
||||
#define NFC_SR_AWB (1 << 22)
|
||||
#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_IDR 0x10
|
||||
|
@ -311,6 +311,36 @@ static const u16 brcmnand_regs_v60[] = {
|
||||
[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 {
|
||||
BRCMNAND_CS_CFG_EXT = 0,
|
||||
BRCMNAND_CS_CFG,
|
||||
@ -406,7 +436,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
|
||||
}
|
||||
|
||||
/* 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;
|
||||
else if (ctrl->nand_version >= 0x0500)
|
||||
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)
|
||||
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)
|
||||
break;
|
||||
}
|
||||
out:
|
||||
/* Sum available OOB */
|
||||
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++)
|
||||
layout->oobavail += layout->oobfree[i].length;
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
@ -537,7 +537,7 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
pretend all blocks are good. */
|
||||
|
@ -225,7 +225,6 @@ struct docg4_priv {
|
||||
static struct nand_ecclayout docg4_oobinfo = {
|
||||
.eccbytes = 9,
|
||||
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
.oobavail = 5,
|
||||
.oobfree = { {.offset = 2, .length = 5} }
|
||||
};
|
||||
|
||||
@ -1121,7 +1120,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
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 */
|
||||
return 0;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
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 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;
|
||||
|
||||
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
switch (chip->ecc_step_ds) {
|
||||
case SZ_512:
|
||||
@ -158,19 +158,19 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
|
||||
dev_err(this->dev,
|
||||
"unsupported nand chip. ecc bits : %d, ecc size : %d\n",
|
||||
chip->ecc_strength_ds, chip->ecc_step_ds);
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
geo->ecc_chunk_size = chip->ecc_step_ds;
|
||||
geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
|
||||
if (!gpmi_check_ecc(this))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
/* Keep the C >= O */
|
||||
if (geo->ecc_chunk_size < mtd->oobsize) {
|
||||
dev_err(this->dev,
|
||||
"unsupported nand chip. ecc size: %d, oob size : %d\n",
|
||||
chip->ecc_step_ds, mtd->oobsize);
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
if (!this->swap_block_mark)
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
/* For bit swap. */
|
||||
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_bit_offset = block_mark_bit_offset % 8;
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!gpmi_check_ecc(this)) {
|
||||
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,
|
||||
this->devdata->bch_max_ecc_strength);
|
||||
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)
|
||||
{
|
||||
if (of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")
|
||||
&& set_geometry_by_ecc_info(this))
|
||||
return 0;
|
||||
return legacy_set_geometry(this);
|
||||
if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
|
||||
|| legacy_set_geometry(this))
|
||||
return set_geometry_by_ecc_info(this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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 = {
|
||||
.driver = {
|
||||
.name = "gpmi-nand",
|
||||
.pm = &gpmi_pm_ops,
|
||||
.of_match_table = gpmi_nand_id_table,
|
||||
},
|
||||
.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 = {
|
||||
.oobavail = 6,
|
||||
.oobfree = { {2, 6} },
|
||||
};
|
||||
|
||||
|
@ -427,9 +427,6 @@ static int jz_nand_probe(struct platform_device *pdev)
|
||||
chip->ecc.strength = 4;
|
||||
chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
|
||||
|
||||
if (pdata)
|
||||
chip->ecc.layout = pdata->ecc_layout;
|
||||
|
||||
chip->chip_delay = 50;
|
||||
chip->cmd_ctrl = jz_nand_cmd_ctrl;
|
||||
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.size = mtd->writesize;
|
||||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.layout = &lpc32xx_nand_oob;
|
||||
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)
|
||||
{
|
||||
struct device_node *rootnode, *dn = op->dev.of_node;
|
||||
struct device_node *dn = op->dev.of_node;
|
||||
struct clk *clk;
|
||||
struct device *dev = &op->dev;
|
||||
struct mpc5121_nfc_prv *prv;
|
||||
@ -712,18 +712,15 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Support external chip-select logic on ADS5121 board */
|
||||
rootnode = of_find_node_by_path("/");
|
||||
if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) {
|
||||
if (of_machine_is_compatible("fsl,mpc5121ads")) {
|
||||
retval = ads5121_chipselect_init(mtd);
|
||||
if (retval) {
|
||||
dev_err(dev, "Chipselect init error!\n");
|
||||
of_node_put(rootnode);
|
||||
return retval;
|
||||
}
|
||||
|
||||
chip->select_chip = ads5121_select_chip;
|
||||
}
|
||||
of_node_put(rootnode);
|
||||
|
||||
/* Enable NFC clock */
|
||||
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
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset from device start
|
||||
* @getchip: 0, if the chip is already selected
|
||||
*
|
||||
* 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);
|
||||
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;
|
||||
|
||||
if (getchip) {
|
||||
chipnr = (int)(ofs >> chip->chip_shift);
|
||||
|
||||
nand_get_device(mtd, FL_READING);
|
||||
|
||||
/* Select the NAND device */
|
||||
chip->select_chip(mtd, chipnr);
|
||||
}
|
||||
|
||||
do {
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
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++;
|
||||
} while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
|
||||
|
||||
if (getchip) {
|
||||
chip->select_chip(mtd, -1);
|
||||
nand_release_device(mtd);
|
||||
}
|
||||
|
||||
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
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset from device start
|
||||
* @getchip: 0, if the chip is already selected
|
||||
* @allowbbt: 1, if its allowed to access the bbt area
|
||||
*
|
||||
* Check, if the block is bad. Either by reading the bad block table or
|
||||
* calling of the scan function.
|
||||
*/
|
||||
static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
|
||||
int allowbbt)
|
||||
static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (!chip->bbt)
|
||||
return chip->block_bad(mtd, ofs, getchip);
|
||||
return chip->block_bad(mtd, ofs);
|
||||
|
||||
/* Return info from the table */
|
||||
return nand_isbad_bbt(mtd, ofs, allowbbt);
|
||||
@ -566,8 +549,8 @@ void nand_wait_ready(struct mtd_info *mtd)
|
||||
cond_resched();
|
||||
} while (time_before(jiffies, timeo));
|
||||
|
||||
pr_warn_ratelimited(
|
||||
"timeout while waiting for chip to become ready\n");
|
||||
if (!chip->dev_ready(mtd))
|
||||
pr_warn_ratelimited("timeout while waiting for chip to become ready\n");
|
||||
out:
|
||||
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;
|
||||
uint32_t readlen = ops->len;
|
||||
uint32_t oobreadlen = ops->ooblen;
|
||||
uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
|
||||
mtd->oobavail : mtd->oobsize;
|
||||
uint32_t max_oobsize = mtd_oobavail(mtd, ops);
|
||||
|
||||
uint8_t *bufpoi, *oob, *buf;
|
||||
int use_bufpoi;
|
||||
@ -2075,10 +2057,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
|
||||
stats = mtd->ecc_stats;
|
||||
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB)
|
||||
len = chip->ecc.layout->oobavail;
|
||||
else
|
||||
len = mtd->oobsize;
|
||||
len = mtd_oobavail(mtd, ops);
|
||||
|
||||
if (unlikely(ops->ooboffs >= len)) {
|
||||
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 oobwritelen = ops->ooblen;
|
||||
uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
|
||||
mtd->oobavail : mtd->oobsize;
|
||||
uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
|
||||
|
||||
uint8_t *oob = ops->oobbuf;
|
||||
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",
|
||||
__func__, (unsigned int)to, (int)ops->ooblen);
|
||||
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB)
|
||||
len = chip->ecc.layout->oobavail;
|
||||
else
|
||||
len = mtd->oobsize;
|
||||
len = mtd_oobavail(mtd, ops);
|
||||
|
||||
/* Do not allow write past end of page */
|
||||
if ((ops->ooboffs + ops->ooblen) > len) {
|
||||
@ -2957,7 +2932,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
while (len) {
|
||||
/* Check if we have a bad block, we do not erase bad blocks! */
|
||||
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",
|
||||
__func__, page);
|
||||
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)
|
||||
{
|
||||
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. */
|
||||
ecc->bytes = DIV_ROUND_UP(
|
||||
ecc->strength * fls(8 * ecc->size), 8);
|
||||
ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
|
||||
&ecc->layout);
|
||||
ecc->bytes = 0;
|
||||
ecc->priv = nand_bch_init(mtd);
|
||||
if (!ecc->priv) {
|
||||
pr_warn("BCH ECC initialization failed!\n");
|
||||
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 out of band area.
|
||||
*/
|
||||
ecc->layout->oobavail = 0;
|
||||
for (i = 0; ecc->layout->oobfree[i].length
|
||||
&& i < ARRAY_SIZE(ecc->layout->oobfree); i++)
|
||||
ecc->layout->oobavail += ecc->layout->oobfree[i].length;
|
||||
mtd->oobavail = ecc->layout->oobavail;
|
||||
mtd->oobavail = 0;
|
||||
if (ecc->layout) {
|
||||
for (i = 0; ecc->layout->oobfree[i].length; i++)
|
||||
mtd->oobavail += ecc->layout->oobfree[i].length;
|
||||
}
|
||||
|
||||
/* ECC sanity check: warn if it's too weak */
|
||||
if (!nand_ecc_strength_good(mtd))
|
||||
|
@ -1373,5 +1373,3 @@ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
|
||||
|
||||
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
|
||||
* @mtd: MTD block structure
|
||||
* @eccsize: ecc block size in bytes
|
||||
* @eccbytes: ecc length in bytes
|
||||
* @ecclayout: output default layout
|
||||
*
|
||||
* Returns:
|
||||
* 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)
|
||||
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
|
||||
*/
|
||||
struct nand_bch_control *
|
||||
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
|
||||
struct nand_ecclayout **ecclayout)
|
||||
struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
unsigned int m, t, eccsteps, i;
|
||||
struct nand_ecclayout *layout;
|
||||
struct nand_ecclayout *layout = nand->ecc.layout;
|
||||
struct nand_bch_control *nbc = NULL;
|
||||
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) {
|
||||
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;
|
||||
|
||||
/* if no ecc placement scheme was provided, build one */
|
||||
if (!*ecclayout) {
|
||||
if (!layout) {
|
||||
|
||||
/* handle large page devices only */
|
||||
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].length = mtd->oobsize-2-layout->eccbytes;
|
||||
|
||||
*ecclayout = layout;
|
||||
nand->ecc.layout = layout;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
goto fail;
|
||||
}
|
||||
if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) {
|
||||
if (layout->eccbytes != (eccsteps*eccbytes)) {
|
||||
printk(KERN_WARNING "invalid ecc layout\n");
|
||||
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++)
|
||||
nbc->eccmask[i] ^= 0xff;
|
||||
|
||||
if (!eccstrength)
|
||||
nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
|
||||
|
||||
return nbc;
|
||||
fail:
|
||||
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) },
|
||||
{"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
|
||||
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
|
||||
SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K),
|
||||
4 },
|
||||
SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
|
||||
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 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;
|
||||
spin_lock(&nand->lock);
|
||||
val = __raw_readl(REG_SMISR);
|
||||
val = __raw_readl(nand->reg + REG_SMISR);
|
||||
val &= READYBUSY;
|
||||
spin_unlock(&nand->lock);
|
||||
|
||||
|
@ -1807,13 +1807,19 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
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 */
|
||||
ecclayout = &info->oobinfo;
|
||||
nand_chip->ecc.layout = ecclayout;
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_HAM1_CODE_SW:
|
||||
nand_chip->ecc.mode = NAND_ECC_SOFT;
|
||||
break;
|
||||
|
||||
case OMAP_ECC_HAM1_CODE_HW:
|
||||
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
|
||||
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->eccpos[ecclayout->eccbytes - 1] + 1;
|
||||
/* software bch library is used for locating errors */
|
||||
nand_chip->ecc.priv = nand_bch_init(mtd,
|
||||
nand_chip->ecc.size,
|
||||
nand_chip->ecc.bytes,
|
||||
&ecclayout);
|
||||
nand_chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!nand_chip->ecc.priv) {
|
||||
dev_err(&info->pdev->dev, "unable to use BCH library\n");
|
||||
err = -EINVAL;
|
||||
@ -1925,10 +1928,7 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
ecclayout->oobfree->offset = 1 +
|
||||
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
|
||||
/* software bch library is used for locating errors */
|
||||
nand_chip->ecc.priv = nand_bch_init(mtd,
|
||||
nand_chip->ecc.size,
|
||||
nand_chip->ecc.bytes,
|
||||
&ecclayout);
|
||||
nand_chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!nand_chip->ecc.priv) {
|
||||
dev_err(&info->pdev->dev, "unable to use BCH library\n");
|
||||
err = -EINVAL;
|
||||
@ -2002,9 +2002,6 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
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 */
|
||||
ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
|
||||
/* 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;
|
||||
goto return_error;
|
||||
}
|
||||
nand_chip->ecc.layout = ecclayout;
|
||||
|
||||
scan_tail:
|
||||
/* 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.ecc.hwctl = pdata->ctrl.hwcontrol;
|
||||
data->chip.ecc.layout = pdata->chip.ecclayout;
|
||||
data->chip.ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
@ -131,11 +131,23 @@
|
||||
#define READ_ID_BYTES 7
|
||||
|
||||
/* macros for registers read/write */
|
||||
#define nand_writel(info, off, val) \
|
||||
writel_relaxed((val), (info)->mmio_base + (off))
|
||||
#define nand_writel(info, off, val) \
|
||||
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) \
|
||||
readl_relaxed((info)->mmio_base + (off))
|
||||
#define nand_readl(info, 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 */
|
||||
enum {
|
||||
@ -199,7 +211,6 @@ struct pxa3xx_nand_info {
|
||||
struct dma_chan *dma_chan;
|
||||
dma_cookie_t dma_cookie;
|
||||
int drcmr_dat;
|
||||
int drcmr_cmd;
|
||||
|
||||
unsigned char *data_buff;
|
||||
unsigned char *oob_buff;
|
||||
@ -222,15 +233,44 @@ struct pxa3xx_nand_info {
|
||||
int use_spare; /* use spare ? */
|
||||
int need_wait;
|
||||
|
||||
unsigned int data_size; /* data to be read from FIFO */
|
||||
unsigned int chunk_size; /* split commands chunk size */
|
||||
unsigned int oob_size;
|
||||
/* Amount of real data per full chunk */
|
||||
unsigned int chunk_size;
|
||||
|
||||
/* Amount of spare data per full chunk */
|
||||
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_err_cnt;
|
||||
unsigned int max_bitflips;
|
||||
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 */
|
||||
uint32_t reg_ndcr;
|
||||
uint32_t ndtr0cs0;
|
||||
@ -526,25 +566,6 @@ static int pxa3xx_nand_init(struct pxa3xx_nand_host *host)
|
||||
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
|
||||
* 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)
|
||||
{
|
||||
unsigned int do_bytes = min(info->data_size, info->chunk_size);
|
||||
|
||||
switch (info->state) {
|
||||
case STATE_PIO_WRITING:
|
||||
writesl(info->mmio_base + NDDB,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(do_bytes, 4));
|
||||
if (info->step_chunk_size)
|
||||
writesl(info->mmio_base + NDDB,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(info->step_chunk_size, 4));
|
||||
|
||||
if (info->oob_size > 0)
|
||||
if (info->step_spare_size)
|
||||
writesl(info->mmio_base + NDDB,
|
||||
info->oob_buff + info->oob_buff_pos,
|
||||
DIV_ROUND_UP(info->oob_size, 4));
|
||||
DIV_ROUND_UP(info->step_spare_size, 4));
|
||||
break;
|
||||
case STATE_PIO_READING:
|
||||
drain_fifo(info,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(do_bytes, 4));
|
||||
if (info->step_chunk_size)
|
||||
drain_fifo(info,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(info->step_chunk_size, 4));
|
||||
|
||||
if (info->oob_size > 0)
|
||||
if (info->step_spare_size)
|
||||
drain_fifo(info,
|
||||
info->oob_buff + info->oob_buff_pos,
|
||||
DIV_ROUND_UP(info->oob_size, 4));
|
||||
DIV_ROUND_UP(info->step_spare_size, 4));
|
||||
break;
|
||||
default:
|
||||
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 */
|
||||
info->data_buff_pos += do_bytes;
|
||||
info->oob_buff_pos += info->oob_size;
|
||||
info->data_size -= do_bytes;
|
||||
info->data_buff_pos += info->step_chunk_size;
|
||||
info->oob_buff_pos += info->step_spare_size;
|
||||
}
|
||||
|
||||
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);
|
||||
BUG();
|
||||
}
|
||||
info->sg.length = info->data_size +
|
||||
(info->oob_size ? info->spare_size + info->ecc_size : 0);
|
||||
info->sg.length = info->chunk_size;
|
||||
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);
|
||||
|
||||
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 */
|
||||
info->buf_start = 0;
|
||||
info->buf_count = 0;
|
||||
info->oob_size = 0;
|
||||
info->data_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_spare = 1;
|
||||
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_PAGEPROG:
|
||||
info->use_ecc = 1;
|
||||
case NAND_CMD_READOOB:
|
||||
pxa3xx_set_datasize(info, mtd);
|
||||
break;
|
||||
case NAND_CMD_PARAM:
|
||||
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)
|
||||
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,
|
||||
* 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)
|
||||
| NDCB0_LEN_OVRD
|
||||
| NDCB0_EXT_CMD_TYPE(ext_cmd_type);
|
||||
info->ndcb3 = info->chunk_size +
|
||||
info->oob_size;
|
||||
info->ndcb3 = info->step_chunk_size +
|
||||
info->step_spare_size;
|
||||
}
|
||||
|
||||
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)
|
||||
| addr_cycle
|
||||
| command;
|
||||
/* No data transfer in this case */
|
||||
info->data_size = 0;
|
||||
exec_cmd = 1;
|
||||
}
|
||||
break;
|
||||
@ -1014,6 +1041,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
||||
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 */
|
||||
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)
|
||||
| NDCB0_LEN_OVRD
|
||||
| NDCB0_EXT_CMD_TYPE(ext_cmd_type);
|
||||
info->ndcb3 = info->chunk_size +
|
||||
info->oob_size;
|
||||
info->ndcb3 = info->step_chunk_size +
|
||||
info->step_spare_size;
|
||||
|
||||
/*
|
||||
* This is the command dispatch that completes a chunked
|
||||
* page program operation.
|
||||
*/
|
||||
if (info->data_size == 0) {
|
||||
if (info->cur_chunk == info->ntotalchunks) {
|
||||
info->ndcb0 = NDCB0_CMD_TYPE(0x1)
|
||||
| NDCB0_EXT_CMD_TYPE(ext_cmd_type)
|
||||
| command;
|
||||
@ -1058,7 +1093,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
||||
| command;
|
||||
info->ndcb1 = (column & 0xFF);
|
||||
info->ndcb3 = INIT_BUFFER_SIZE;
|
||||
info->data_size = INIT_BUFFER_SIZE;
|
||||
info->step_chunk_size = INIT_BUFFER_SIZE;
|
||||
break;
|
||||
|
||||
case NAND_CMD_READID:
|
||||
@ -1068,7 +1103,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
||||
| command;
|
||||
info->ndcb1 = (column & 0xFF);
|
||||
|
||||
info->data_size = 8;
|
||||
info->step_chunk_size = 8;
|
||||
break;
|
||||
case NAND_CMD_STATUS:
|
||||
info->buf_count = 1;
|
||||
@ -1076,7 +1111,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
||||
| NDCB0_ADDR_CYC(1)
|
||||
| command;
|
||||
|
||||
info->data_size = 8;
|
||||
info->step_chunk_size = 8;
|
||||
break;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
@ -1217,6 +1252,7 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
||||
init_completion(&info->dev_ready);
|
||||
do {
|
||||
info->state = STATE_PREPARED;
|
||||
|
||||
exec_cmd = prepare_set_command(info, command, ext_cmd_type,
|
||||
column, page_addr);
|
||||
if (!exec_cmd) {
|
||||
@ -1236,22 +1272,30 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
||||
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 */
|
||||
if (info->data_size == 0 && command != NAND_CMD_PAGEPROG)
|
||||
if (info->cur_chunk == info->ntotalchunks && command != NAND_CMD_PAGEPROG)
|
||||
break;
|
||||
|
||||
/*
|
||||
* After a splitted program command sequence has issued
|
||||
* the command dispatch, the command sequence is complete.
|
||||
*/
|
||||
if (info->data_size == 0 &&
|
||||
if (info->cur_chunk == (info->ntotalchunks + 1) &&
|
||||
command == NAND_CMD_PAGEPROG &&
|
||||
ext_cmd_type == EXT_CMD_TYPE_DISPATCH)
|
||||
break;
|
||||
|
||||
if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) {
|
||||
/* 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;
|
||||
else
|
||||
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.
|
||||
*/
|
||||
} else if (command == NAND_CMD_PAGEPROG &&
|
||||
info->data_size == 0) {
|
||||
info->cur_chunk == info->ntotalchunks) {
|
||||
ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
|
||||
}
|
||||
} while (1);
|
||||
@ -1506,6 +1550,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
int strength, int ecc_stepsize, int page_size)
|
||||
{
|
||||
if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
|
||||
info->nfullchunks = 1;
|
||||
info->ntotalchunks = 1;
|
||||
info->chunk_size = 2048;
|
||||
info->spare_size = 40;
|
||||
info->ecc_size = 24;
|
||||
@ -1514,6 +1560,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
ecc->strength = 1;
|
||||
|
||||
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
|
||||
info->nfullchunks = 1;
|
||||
info->ntotalchunks = 1;
|
||||
info->chunk_size = 512;
|
||||
info->spare_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) {
|
||||
info->ecc_bch = 1;
|
||||
info->nfullchunks = 1;
|
||||
info->ntotalchunks = 1;
|
||||
info->chunk_size = 2048;
|
||||
info->spare_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) {
|
||||
info->ecc_bch = 1;
|
||||
info->nfullchunks = 2;
|
||||
info->ntotalchunks = 2;
|
||||
info->chunk_size = 2048;
|
||||
info->spare_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) {
|
||||
info->ecc_bch = 1;
|
||||
info->nfullchunks = 4;
|
||||
info->ntotalchunks = 5;
|
||||
info->chunk_size = 1024;
|
||||
info->spare_size = 0;
|
||||
info->last_chunk_size = 0;
|
||||
info->last_spare_size = 64;
|
||||
info->ecc_size = 32;
|
||||
ecc->mode = NAND_ECC_HW;
|
||||
ecc->size = info->chunk_size;
|
||||
@ -1738,7 +1794,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (use_dma) {
|
||||
if (!np && use_dma) {
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (r == NULL) {
|
||||
dev_err(&pdev->dev,
|
||||
@ -1747,15 +1803,6 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||
goto fail_disable_clk;
|
||||
}
|
||||
info->drcmr_dat = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (r == NULL) {
|
||||
dev_err(&pdev->dev,
|
||||
"no resource defined for cmd DMA\n");
|
||||
ret = -ENXIO;
|
||||
goto fail_disable_clk;
|
||||
}
|
||||
info->drcmr_cmd = r->start;
|
||||
}
|
||||
|
||||
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;
|
||||
#endif
|
||||
|
||||
if (set->ecc_layout != NULL)
|
||||
chip->ecc.layout = set->ecc_layout;
|
||||
|
||||
if (set->disable_ecc)
|
||||
chip->ecc.mode = NAND_ECC_NONE;
|
||||
|
||||
|
@ -60,6 +60,7 @@
|
||||
#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
|
||||
#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
|
||||
#define NFC_REG_SPARE_AREA 0x00A0
|
||||
#define NFC_REG_PAT_ID 0x00A4
|
||||
#define NFC_RAM0_BASE 0x0400
|
||||
#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);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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 *oob, int oob_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 sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||
int raw_mode = 0;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
if (*cur_off != data_off)
|
||||
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)
|
||||
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)
|
||||
return ret;
|
||||
|
||||
sunxi_nfc_randomizer_enable(mtd);
|
||||
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
|
||||
nfc->regs + NFC_REG_CMD);
|
||||
|
||||
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
||||
sunxi_nfc_randomizer_disable(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*cur_off = oob_off + ecc->bytes + 4;
|
||||
|
||||
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)));
|
||||
|
||||
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
|
||||
|
||||
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)) {
|
||||
/*
|
||||
* 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,
|
||||
oob, ecc->bytes + 4,
|
||||
NULL, 0, ecc->strength);
|
||||
if (ret >= 0)
|
||||
raw_mode = 1;
|
||||
} else {
|
||||
/*
|
||||
* 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)),
|
||||
oob);
|
||||
|
||||
/* De-randomize the Bad Block Marker. */
|
||||
if (bbm && nand->options & NAND_NEED_SCRAMBLING)
|
||||
sunxi_nfc_randomize_bbm(mtd, page, oob);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
*cur_off = oob_off + ecc->bytes + 4;
|
||||
|
||||
return 0;
|
||||
return raw_mode;
|
||||
}
|
||||
|
||||
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_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,
|
||||
offset + mtd->writesize, -1);
|
||||
|
||||
sunxi_nfc_read_buf(mtd, oob + offset, len);
|
||||
if (!randomize)
|
||||
sunxi_nfc_read_buf(mtd, oob + offset, len);
|
||||
else
|
||||
sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
|
||||
false, page);
|
||||
|
||||
*cur_off = mtd->oobsize + mtd->writesize;
|
||||
}
|
||||
@ -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,
|
||||
const u8 *data, int data_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 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)
|
||||
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 */
|
||||
writel(sunxi_nfc_buf_to_user_data(oob),
|
||||
nfc->regs + NFC_REG_USER_DATA(0));
|
||||
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),
|
||||
nfc->regs + NFC_REG_USER_DATA(0));
|
||||
}
|
||||
|
||||
if (data_off + ecc->size != oob_off)
|
||||
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)
|
||||
return ret;
|
||||
|
||||
sunxi_nfc_randomizer_enable(mtd);
|
||||
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
|
||||
NFC_ACCESS_DIR | NFC_ECC_OP,
|
||||
nfc->regs + NFC_REG_CMD);
|
||||
|
||||
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
||||
sunxi_nfc_randomizer_disable(mtd);
|
||||
if (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,
|
||||
u8 *oob, int *cur_off)
|
||||
u8 *oob, int *cur_off,
|
||||
int page)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
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,
|
||||
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;
|
||||
}
|
||||
@ -725,6 +945,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
unsigned int max_bitflips = 0;
|
||||
int ret, i, cur_off = 0;
|
||||
bool raw_mode = false;
|
||||
|
||||
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,
|
||||
oob_off + mtd->writesize,
|
||||
&cur_off, &max_bitflips);
|
||||
if (ret)
|
||||
&cur_off, &max_bitflips,
|
||||
!i, page);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret)
|
||||
raw_mode = true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -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,
|
||||
oob_off + mtd->writesize,
|
||||
&cur_off);
|
||||
&cur_off, !i, page);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (oob_required)
|
||||
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off);
|
||||
if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
|
||||
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
|
||||
&cur_off, page);
|
||||
|
||||
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;
|
||||
unsigned int max_bitflips = 0;
|
||||
int ret, i, cur_off = 0;
|
||||
bool raw_mode = false;
|
||||
|
||||
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,
|
||||
oob_off, &cur_off,
|
||||
&max_bitflips);
|
||||
if (ret)
|
||||
&max_bitflips, !i, page);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret)
|
||||
raw_mode = true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -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));
|
||||
|
||||
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)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (oob_required)
|
||||
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off);
|
||||
if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
|
||||
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
|
||||
&cur_off, page);
|
||||
|
||||
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)
|
||||
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);
|
||||
if (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;
|
||||
}
|
||||
|
||||
/* propagate ecc.layout to mtd_info */
|
||||
mtd->ecclayout = chip->ecc.layout;
|
||||
chip->ecc.read_page = vf610_nfc_read_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,
|
||||
(int)len);
|
||||
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB)
|
||||
oobsize = this->ecclayout->oobavail;
|
||||
else
|
||||
oobsize = mtd->oobsize;
|
||||
|
||||
oobsize = mtd_oobavail(mtd, ops);
|
||||
oobcolumn = from & (mtd->oobsize - 1);
|
||||
|
||||
/* 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,
|
||||
(int)len);
|
||||
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB)
|
||||
oobsize = this->ecclayout->oobavail;
|
||||
else
|
||||
oobsize = mtd->oobsize;
|
||||
|
||||
oobsize = mtd_oobavail(mtd, ops);
|
||||
oobcolumn = from & (mtd->oobsize - 1);
|
||||
|
||||
/* 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;
|
||||
|
||||
if (mode == MTD_OPS_AUTO_OOB)
|
||||
oobsize = this->ecclayout->oobavail;
|
||||
oobsize = mtd->oobavail;
|
||||
else
|
||||
oobsize = mtd->oobsize;
|
||||
|
||||
@ -1885,12 +1877,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
|
||||
/* Check zero length */
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB)
|
||||
oobsize = this->ecclayout->oobavail;
|
||||
else
|
||||
oobsize = mtd->oobsize;
|
||||
|
||||
oobsize = mtd_oobavail(mtd, ops);
|
||||
oobcolumn = to & (mtd->oobsize - 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;
|
||||
|
||||
if (mode == MTD_OPS_AUTO_OOB)
|
||||
oobsize = this->ecclayout->oobavail;
|
||||
oobsize = mtd->oobavail;
|
||||
else
|
||||
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)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
ret = mtd_block_markbad(mtd, ofs);
|
||||
ret = this->block_markbad(mtd, ofs);
|
||||
onenand_release_device(mtd);
|
||||
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 out of band area
|
||||
*/
|
||||
this->ecclayout->oobavail = 0;
|
||||
mtd->oobavail = 0;
|
||||
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
|
||||
this->ecclayout->oobfree[i].length; i++)
|
||||
this->ecclayout->oobavail +=
|
||||
this->ecclayout->oobfree[i].length;
|
||||
mtd->oobavail = this->ecclayout->oobavail;
|
||||
mtd->oobavail += this->ecclayout->oobfree[i].length;
|
||||
|
||||
mtd->ecclayout = this->ecclayout;
|
||||
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.
|
||||
*
|
||||
*/
|
||||
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 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);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(onenand_scan_bbt);
|
||||
EXPORT_SYMBOL(onenand_default_bbt);
|
||||
|
@ -9,6 +9,7 @@ if MTD_SPI_NOR
|
||||
|
||||
config MTD_MT81xx_NOR
|
||||
tristate "Mediatek MT81xx SPI NOR flash controller"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables access to SPI NOR flash, using MT81xx SPI NOR flash
|
||||
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
|
||||
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
|
||||
help
|
||||
This enables support for the Quad SPI controller in master mode.
|
||||
|
@ -213,6 +213,7 @@ enum fsl_qspi_devtype {
|
||||
FSL_QUADSPI_IMX6SX,
|
||||
FSL_QUADSPI_IMX7D,
|
||||
FSL_QUADSPI_IMX6UL,
|
||||
FSL_QUADSPI_LS1021A,
|
||||
};
|
||||
|
||||
struct fsl_qspi_devtype_data {
|
||||
@ -258,6 +259,14 @@ static struct fsl_qspi_devtype_data imx6ul_data = {
|
||||
| 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
|
||||
struct fsl_qspi {
|
||||
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
|
||||
@ -275,6 +284,7 @@ struct fsl_qspi {
|
||||
u32 clk_rate;
|
||||
unsigned int chip_base_addr; /* We may support two chips. */
|
||||
bool has_second_chip;
|
||||
bool big_endian;
|
||||
struct mutex lock;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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)
|
||||
{
|
||||
writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
|
||||
writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
|
||||
qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
|
||||
qspi_writel(q, QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
|
||||
}
|
||||
|
||||
static inline void fsl_qspi_lock_lut(struct fsl_qspi *q)
|
||||
{
|
||||
writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
|
||||
writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
|
||||
qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
|
||||
qspi_writel(q, QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* clear interrupt */
|
||||
reg = readl(q->iobase + QUADSPI_FR);
|
||||
writel(reg, q->iobase + QUADSPI_FR);
|
||||
reg = qspi_readl(q, q->iobase + QUADSPI_FR);
|
||||
qspi_writel(q, reg, q->iobase + QUADSPI_FR);
|
||||
|
||||
if (reg & QUADSPI_FR_TFF_MASK)
|
||||
complete(&q->c);
|
||||
@ -348,7 +380,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
||||
|
||||
/* Clear all the LUT table */
|
||||
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 */
|
||||
lut_base = SEQID_QUAD_READ * 4;
|
||||
@ -364,14 +396,15 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
||||
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));
|
||||
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));
|
||||
|
||||
/* Write enable */
|
||||
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 */
|
||||
lut_base = SEQID_PP * 4;
|
||||
@ -385,13 +418,15 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
||||
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));
|
||||
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 */
|
||||
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));
|
||||
|
||||
/* Erase a sector */
|
||||
@ -400,40 +435,46 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
||||
cmd = q->nor[0].erase_opcode;
|
||||
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));
|
||||
|
||||
/* Erase the whole chip */
|
||||
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));
|
||||
|
||||
/* READ ID */
|
||||
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));
|
||||
|
||||
/* Write Register */
|
||||
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));
|
||||
|
||||
/* Read Configuration Register */
|
||||
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));
|
||||
|
||||
/* Write disable */
|
||||
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) */
|
||||
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) */
|
||||
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);
|
||||
}
|
||||
@ -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);
|
||||
|
||||
/* 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);
|
||||
writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
|
||||
qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr,
|
||||
base + QUADSPI_SFAR);
|
||||
qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
|
||||
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 {
|
||||
reg2 = readl(base + QUADSPI_SR);
|
||||
reg2 = qspi_readl(q, base + QUADSPI_SR);
|
||||
if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) {
|
||||
udelay(1);
|
||||
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 */
|
||||
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. */
|
||||
if (!wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000))) {
|
||||
dev_err(q->dev,
|
||||
"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
|
||||
cmd, addr, readl(base + QUADSPI_FR),
|
||||
readl(base + QUADSPI_SR));
|
||||
cmd, addr, qspi_readl(q, base + QUADSPI_FR),
|
||||
qspi_readl(q, base + QUADSPI_SR));
|
||||
err = -ETIMEDOUT;
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
|
||||
/* restore the MCR */
|
||||
writel(reg, base + QUADSPI_MCR);
|
||||
qspi_writel(q, reg, base + QUADSPI_MCR);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -533,7 +576,7 @@ static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf)
|
||||
int i = 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);
|
||||
dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n",
|
||||
q->chip_base_addr, tmp);
|
||||
@ -561,9 +604,9 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q)
|
||||
{
|
||||
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;
|
||||
writel(reg, q->iobase + QUADSPI_MCR);
|
||||
qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
|
||||
|
||||
/*
|
||||
* The minimum delay : 1 AHB + 2 SFCK clocks.
|
||||
@ -572,7 +615,7 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q)
|
||||
udelay(1);
|
||||
|
||||
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,
|
||||
@ -586,20 +629,20 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
|
||||
q->chip_base_addr, to, count);
|
||||
|
||||
/* clear the TX FIFO. */
|
||||
tmp = readl(q->iobase + QUADSPI_MCR);
|
||||
writel(tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR);
|
||||
tmp = qspi_readl(q, q->iobase + QUADSPI_MCR);
|
||||
qspi_writel(q, tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR);
|
||||
|
||||
/* fill the TX data to the FIFO */
|
||||
for (j = 0, i = ((count + 3) / 4); j < i; j++) {
|
||||
tmp = fsl_qspi_endian_xchg(q, *txbuf);
|
||||
writel(tmp, q->iobase + QUADSPI_TBDR);
|
||||
qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR);
|
||||
txbuf++;
|
||||
}
|
||||
|
||||
/* fill the TXFIFO upto 16 bytes for i.MX7d */
|
||||
if (needs_fill_txfifo(q))
|
||||
for (; i < 4; i++)
|
||||
writel(tmp, q->iobase + QUADSPI_TBDR);
|
||||
qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR);
|
||||
|
||||
/* Trigger it */
|
||||
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;
|
||||
void __iomem *base = q->iobase;
|
||||
|
||||
writel(nor_size + q->memmap_phy, base + QUADSPI_SFA1AD);
|
||||
writel(nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD);
|
||||
writel(nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD);
|
||||
writel(nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD);
|
||||
qspi_writel(q, nor_size + q->memmap_phy, base + QUADSPI_SFA1AD);
|
||||
qspi_writel(q, nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD);
|
||||
qspi_writel(q, nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD);
|
||||
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;
|
||||
|
||||
/* AHB configuration for access buffer 0/1/2 .*/
|
||||
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
|
||||
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
|
||||
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
|
||||
qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
|
||||
qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
|
||||
qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
|
||||
/*
|
||||
* Set ADATSZ with the maximum AHB buffer size to improve the
|
||||
* read performance.
|
||||
*/
|
||||
writel(QUADSPI_BUF3CR_ALLMST_MASK | ((q->devtype_data->ahb_buf_size / 8)
|
||||
<< QUADSPI_BUF3CR_ADATSZ_SHIFT), base + QUADSPI_BUF3CR);
|
||||
qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK |
|
||||
((q->devtype_data->ahb_buf_size / 8)
|
||||
<< QUADSPI_BUF3CR_ADATSZ_SHIFT),
|
||||
base + QUADSPI_BUF3CR);
|
||||
|
||||
/* We only use the buffer3 */
|
||||
writel(0, base + QUADSPI_BUF0IND);
|
||||
writel(0, base + QUADSPI_BUF1IND);
|
||||
writel(0, base + QUADSPI_BUF2IND);
|
||||
qspi_writel(q, 0, base + QUADSPI_BUF0IND);
|
||||
qspi_writel(q, 0, base + QUADSPI_BUF1IND);
|
||||
qspi_writel(q, 0, base + QUADSPI_BUF2IND);
|
||||
|
||||
/* Set the default lut sequence for AHB Read. */
|
||||
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);
|
||||
}
|
||||
|
||||
@ -713,7 +758,7 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
|
||||
return ret;
|
||||
|
||||
/* 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);
|
||||
udelay(1);
|
||||
|
||||
@ -721,24 +766,24 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
|
||||
fsl_qspi_init_lut(q);
|
||||
|
||||
/* 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);
|
||||
|
||||
reg = readl(base + QUADSPI_SMPR);
|
||||
writel(reg & ~(QUADSPI_SMPR_FSDLY_MASK
|
||||
reg = qspi_readl(q, base + QUADSPI_SMPR);
|
||||
qspi_writel(q, reg & ~(QUADSPI_SMPR_FSDLY_MASK
|
||||
| QUADSPI_SMPR_FSPHS_MASK
|
||||
| QUADSPI_SMPR_HSENA_MASK
|
||||
| QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* clear all interrupt status */
|
||||
writel(0xffffffff, q->iobase + QUADSPI_FR);
|
||||
qspi_writel(q, 0xffffffff, q->iobase + QUADSPI_FR);
|
||||
|
||||
/* enable the interrupt */
|
||||
writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
|
||||
qspi_writel(q, QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
|
||||
|
||||
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,imx7d-qspi", .data = (void *)&imx7d_data, },
|
||||
{ .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
|
||||
{ .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
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))
|
||||
return PTR_ERR(q->iobase);
|
||||
|
||||
q->big_endian = of_property_read_bool(np, "big-endian");
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"QuadSPI-memory");
|
||||
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 */
|
||||
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
|
||||
writel(0x0, q->iobase + QUADSPI_RSER);
|
||||
qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
|
||||
qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
|
||||
|
||||
mutex_destroy(&q->lock);
|
||||
|
||||
|
@ -371,8 +371,8 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init mtk_nor_init(struct mt8173_nor *mt8173_nor,
|
||||
struct device_node *flash_node)
|
||||
static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
|
||||
struct device_node *flash_node)
|
||||
{
|
||||
int ret;
|
||||
struct spi_nor *nor;
|
||||
|
@ -61,14 +61,20 @@ struct flash_info {
|
||||
u16 addr_width;
|
||||
|
||||
u16 flags;
|
||||
#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
|
||||
#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
|
||||
#define SST_WRITE 0x04 /* use SST byte programming */
|
||||
#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
|
||||
#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
|
||||
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
|
||||
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
|
||||
#define USE_FSR 0x80 /* use flag status register */
|
||||
#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
|
||||
#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
|
||||
#define SST_WRITE BIT(2) /* use SST byte programming */
|
||||
#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */
|
||||
#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */
|
||||
#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */
|
||||
#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
|
||||
#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])
|
||||
@ -434,32 +440,58 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
|
||||
} else {
|
||||
pow = ((sr & mask) ^ mask) >> shift;
|
||||
*len = mtd->size >> pow;
|
||||
*ofs = mtd->size - *len;
|
||||
if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)
|
||||
*ofs = 0;
|
||||
else
|
||||
*ofs = mtd->size - *len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1 if the entire region is locked, 0 otherwise
|
||||
* Return 1 if the entire region is locked (if @locked is true) or unlocked (if
|
||||
* @locked is false); 0 otherwise
|
||||
*/
|
||||
static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
|
||||
u8 sr)
|
||||
static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
|
||||
u8 sr, bool locked)
|
||||
{
|
||||
loff_t lock_offs;
|
||||
uint64_t lock_len;
|
||||
|
||||
if (!len)
|
||||
return 1;
|
||||
|
||||
stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
|
||||
|
||||
return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
|
||||
if (locked)
|
||||
/* Requested range is a sub-range of locked range */
|
||||
return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
|
||||
else
|
||||
/* Requested range does not overlap with locked range */
|
||||
return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
|
||||
}
|
||||
|
||||
static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
|
||||
u8 sr)
|
||||
{
|
||||
return stm_check_lock_status_sr(nor, ofs, len, sr, true);
|
||||
}
|
||||
|
||||
static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
|
||||
u8 sr)
|
||||
{
|
||||
return stm_check_lock_status_sr(nor, ofs, len, sr, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock a region of the flash. Compatible with ST Micro and similar flash.
|
||||
* 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:
|
||||
* - TB: top/bottom protect - only handle TB=0 (top protect)
|
||||
* - SEC: sector/block protect - only handle SEC=0 (block protect)
|
||||
* - 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):
|
||||
*
|
||||
* 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 | 1 | 0 | 4 MB | Upper 1/2
|
||||
* 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.
|
||||
*/
|
||||
@ -481,20 +520,39 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
int status_old, status_new;
|
||||
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
||||
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;
|
||||
|
||||
status_old = read_sr(nor);
|
||||
if (status_old < 0)
|
||||
return status_old;
|
||||
|
||||
/* SPI NOR always locks to the end */
|
||||
if (ofs + len != mtd->size) {
|
||||
/* Does combined region extend to end? */
|
||||
if (!stm_is_locked_sr(nor, ofs + len, mtd->size - ofs - len,
|
||||
status_old))
|
||||
return -EINVAL;
|
||||
len = mtd->size - ofs;
|
||||
}
|
||||
/* If nothing in our range is unlocked, we don't need to do anything */
|
||||
if (stm_is_locked_sr(nor, ofs, len, status_old))
|
||||
return 0;
|
||||
|
||||
/* 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))
|
||||
can_be_top = false;
|
||||
|
||||
if (!can_be_bottom && !can_be_top)
|
||||
return -EINVAL;
|
||||
|
||||
/* Prefer top, if both are valid */
|
||||
use_top = can_be_top;
|
||||
|
||||
/* lock_len: length of region that should end up locked */
|
||||
if (use_top)
|
||||
lock_len = mtd->size - ofs;
|
||||
else
|
||||
lock_len = ofs + len;
|
||||
|
||||
/*
|
||||
* Need smallest pow such that:
|
||||
@ -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 = ilog2(mtd->size) - ilog2(len);
|
||||
pow = ilog2(mtd->size) - ilog2(lock_len);
|
||||
val = mask - (pow << shift);
|
||||
if (val & ~mask)
|
||||
return -EINVAL;
|
||||
@ -513,10 +571,20 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
if (!(val & mask))
|
||||
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 */
|
||||
if ((status_new & mask) <= (status_old & mask))
|
||||
if ((status_new & mask) < (status_old & mask))
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
||||
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;
|
||||
|
||||
status_old = read_sr(nor);
|
||||
if (status_old < 0)
|
||||
return status_old;
|
||||
|
||||
/* Cannot unlock; would unlock larger region than requested */
|
||||
if (stm_is_locked_sr(nor, ofs - mtd->erasesize, mtd->erasesize,
|
||||
status_old))
|
||||
/* If nothing in our range is locked, we don't need to do anything */
|
||||
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))
|
||||
can_be_bottom = false;
|
||||
|
||||
if (!can_be_bottom && !can_be_top)
|
||||
return -EINVAL;
|
||||
|
||||
/* Prefer top, if both are valid */
|
||||
use_top = can_be_top;
|
||||
|
||||
/* lock_len: length of region that should remain locked */
|
||||
if (use_top)
|
||||
lock_len = mtd->size - (ofs + len);
|
||||
else
|
||||
lock_len = ofs;
|
||||
|
||||
/*
|
||||
* 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 = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len));
|
||||
if (ofs + len == mtd->size) {
|
||||
pow = ilog2(mtd->size) - order_base_2(lock_len);
|
||||
if (lock_len == 0) {
|
||||
val = 0; /* fully unlocked */
|
||||
} else {
|
||||
val = mask - (pow << shift);
|
||||
@ -567,10 +658,21 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
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 */
|
||||
if ((status_new & mask) >= (status_old & mask))
|
||||
if ((status_new & mask) > (status_old & mask))
|
||||
return -EINVAL;
|
||||
|
||||
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) },
|
||||
{ "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) },
|
||||
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
|
||||
{ "n25q128a13", INFO(0x20ba18, 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, 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) },
|
||||
{ "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) },
|
||||
{ "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) },
|
||||
{ "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) },
|
||||
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "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) },
|
||||
{ "w25x32", INFO(0xef3016, 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) },
|
||||
{ "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) },
|
||||
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int status;
|
||||
@ -1152,12 +1228,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
|
||||
}
|
||||
return status;
|
||||
case SNOR_MFR_MICRON:
|
||||
status = micron_quad_enable(nor);
|
||||
if (status) {
|
||||
dev_err(nor->dev, "Micron quad-read not enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return status;
|
||||
return 0;
|
||||
default:
|
||||
status = spansion_quad_enable(nor);
|
||||
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 ||
|
||||
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_sr(nor, 0);
|
||||
spi_nor_wait_till_ready(nor);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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_unlock = stm_unlock;
|
||||
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)
|
||||
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
|
||||
/* 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");
|
||||
}
|
||||
|
||||
if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
|
||||
if (use_offset != 0 || use_len < mtd->oobavail) {
|
||||
int k;
|
||||
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = mtd->ecclayout->oobavail;
|
||||
ops.ooblen = mtd->oobavail;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
ops.oobbuf = readbuf;
|
||||
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",
|
||||
(long long)addr);
|
||||
errcnt += 1;
|
||||
@ -244,7 +244,7 @@ static int verify_eraseblock(int ebnum)
|
||||
/* verify post-(use_offset + use_len) area for 0xff */
|
||||
k = use_offset + use_len;
|
||||
bitflips += memffshow(addr, k, readbuf + k,
|
||||
mtd->ecclayout->oobavail - k);
|
||||
mtd->oobavail - k);
|
||||
|
||||
if (bitflips > bitflip_limit) {
|
||||
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;
|
||||
int err = 0;
|
||||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
size_t len = mtd->ecclayout->oobavail * pgcnt;
|
||||
size_t oobavail = mtd->ecclayout->oobavail;
|
||||
size_t len = mtd->oobavail * pgcnt;
|
||||
size_t oobavail = mtd->oobavail;
|
||||
size_t bitflips;
|
||||
int i;
|
||||
|
||||
@ -394,8 +394,8 @@ static int __init mtd_oobtest_init(void)
|
||||
goto out;
|
||||
|
||||
use_offset = 0;
|
||||
use_len = mtd->ecclayout->oobavail;
|
||||
use_len_max = mtd->ecclayout->oobavail;
|
||||
use_len = mtd->oobavail;
|
||||
use_len_max = mtd->oobavail;
|
||||
vary_offset = 0;
|
||||
|
||||
/* First test: write all OOB, read it back and verify */
|
||||
@ -460,8 +460,8 @@ static int __init mtd_oobtest_init(void)
|
||||
|
||||
/* Write all eraseblocks */
|
||||
use_offset = 0;
|
||||
use_len = mtd->ecclayout->oobavail;
|
||||
use_len_max = mtd->ecclayout->oobavail;
|
||||
use_len = mtd->oobavail;
|
||||
use_len_max = mtd->oobavail;
|
||||
vary_offset = 1;
|
||||
prandom_seed_state(&rnd_state, 5);
|
||||
|
||||
@ -471,8 +471,8 @@ static int __init mtd_oobtest_init(void)
|
||||
|
||||
/* Check all eraseblocks */
|
||||
use_offset = 0;
|
||||
use_len = mtd->ecclayout->oobavail;
|
||||
use_len_max = mtd->ecclayout->oobavail;
|
||||
use_len = mtd->oobavail;
|
||||
use_len_max = mtd->oobavail;
|
||||
vary_offset = 1;
|
||||
prandom_seed_state(&rnd_state, 5);
|
||||
err = verify_all_eraseblocks();
|
||||
@ -480,8 +480,8 @@ static int __init mtd_oobtest_init(void)
|
||||
goto out;
|
||||
|
||||
use_offset = 0;
|
||||
use_len = mtd->ecclayout->oobavail;
|
||||
use_len_max = mtd->ecclayout->oobavail;
|
||||
use_len = mtd->oobavail;
|
||||
use_len_max = mtd->oobavail;
|
||||
vary_offset = 0;
|
||||
|
||||
/* Fourth test: try to write off end of device */
|
||||
@ -501,7 +501,7 @@ static int __init mtd_oobtest_init(void)
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = 1;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = mtd->ecclayout->oobavail;
|
||||
ops.ooboffs = mtd->oobavail;
|
||||
ops.datbuf = NULL;
|
||||
ops.oobbuf = writebuf;
|
||||
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.ooblen = 1;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = mtd->ecclayout->oobavail;
|
||||
ops.ooboffs = mtd->oobavail;
|
||||
ops.datbuf = NULL;
|
||||
ops.oobbuf = readbuf;
|
||||
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.len = 0;
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = mtd->ecclayout->oobavail + 1;
|
||||
ops.ooblen = mtd->oobavail + 1;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
@ -563,7 +563,7 @@ static int __init mtd_oobtest_init(void)
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = mtd->ecclayout->oobavail + 1;
|
||||
ops.ooblen = mtd->oobavail + 1;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
@ -587,7 +587,7 @@ static int __init mtd_oobtest_init(void)
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = mtd->ecclayout->oobavail;
|
||||
ops.ooblen = mtd->oobavail;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 1;
|
||||
ops.datbuf = NULL;
|
||||
@ -607,7 +607,7 @@ static int __init mtd_oobtest_init(void)
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = mtd->ecclayout->oobavail;
|
||||
ops.ooblen = mtd->oobavail;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 1;
|
||||
ops.datbuf = NULL;
|
||||
@ -638,7 +638,7 @@ static int __init mtd_oobtest_init(void)
|
||||
for (i = 0; i < ebcnt - 1; ++i) {
|
||||
int cnt = 2;
|
||||
int pg;
|
||||
size_t sz = mtd->ecclayout->oobavail;
|
||||
size_t sz = mtd->oobavail;
|
||||
if (bbt[i] || bbt[i + 1])
|
||||
continue;
|
||||
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) {
|
||||
if (bbt[i] || bbt[i + 1])
|
||||
continue;
|
||||
prandom_bytes_state(&rnd_state, writebuf,
|
||||
mtd->ecclayout->oobavail * 2);
|
||||
prandom_bytes_state(&rnd_state, writebuf, mtd->oobavail * 2);
|
||||
addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = mtd->ecclayout->oobavail * 2;
|
||||
ops.ooblen = mtd->oobavail * 2;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
@ -688,7 +687,7 @@ static int __init mtd_oobtest_init(void)
|
||||
if (err)
|
||||
goto out;
|
||||
if (memcmpshow(addr, readbuf, writebuf,
|
||||
mtd->ecclayout->oobavail * 2)) {
|
||||
mtd->oobavail * 2)) {
|
||||
pr_err("error: verify failed at %#llx\n",
|
||||
(long long)addr);
|
||||
errcnt += 1;
|
||||
|
@ -49,7 +49,6 @@ static struct nand_ecclayout spinand_oob_64 = {
|
||||
17, 18, 19, 20, 21, 22,
|
||||
33, 34, 35, 36, 37, 38,
|
||||
49, 50, 51, 52, 53, 54, },
|
||||
.oobavail = 32,
|
||||
.oobfree = {
|
||||
{.offset = 8,
|
||||
.length = 8},
|
||||
|
@ -78,7 +78,6 @@
|
||||
#define BL_ALL_UNLOCKED 0
|
||||
|
||||
struct spinand_info {
|
||||
struct nand_ecclayout *ecclayout;
|
||||
struct spi_device *spi;
|
||||
void *priv;
|
||||
};
|
||||
|
@ -134,38 +134,60 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
if (mutex_lock_interruptible(&c->alloc_sem))
|
||||
return -EINTR;
|
||||
|
||||
|
||||
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);
|
||||
if (!c->unchecked_size)
|
||||
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);
|
||||
|
||||
if (!xattr)
|
||||
xattr = jffs2_verify_xattr(c);
|
||||
|
||||
spin_lock(&c->inocache_lock);
|
||||
/* Instead of doing the inodes in numeric order, doing a lookup
|
||||
* in the hash for each possible number, just walk the hash
|
||||
* buckets of *existing* inodes. This means that we process
|
||||
* them out-of-order, but it can be a lot faster if there's
|
||||
* a sparse inode# space. Which there often is. */
|
||||
want_ino = c->check_ino;
|
||||
for (bucket = c->check_ino % c->inocache_hashsize ; bucket < c->inocache_hashsize; bucket++) {
|
||||
for (ic = c->inocache_list[bucket]; ic; ic = ic->next) {
|
||||
if (ic->ino < want_ino)
|
||||
continue;
|
||||
|
||||
ic = jffs2_get_ino_cache(c, c->checked_ino++);
|
||||
if (ic->state != INO_STATE_CHECKEDABSENT &&
|
||||
ic->state != INO_STATE_PRESENT)
|
||||
goto got_next; /* with inocache_lock held */
|
||||
|
||||
if (!ic) {
|
||||
spin_unlock(&c->inocache_lock);
|
||||
continue;
|
||||
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) {
|
||||
jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n",
|
||||
ic->ino);
|
||||
@ -176,8 +198,6 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
switch(ic->state) {
|
||||
case INO_STATE_CHECKEDABSENT:
|
||||
case INO_STATE_PRESENT:
|
||||
jffs2_dbg(1, "Skipping ino #%u already checked\n",
|
||||
ic->ino);
|
||||
spin_unlock(&c->inocache_lock);
|
||||
continue;
|
||||
|
||||
@ -196,7 +216,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
ic->ino);
|
||||
/* We need to come back again for the _same_ inode. We've
|
||||
made no progress in this case, but that should be OK */
|
||||
c->checked_ino--;
|
||||
c->check_ino = ic->ino;
|
||||
|
||||
mutex_unlock(&c->alloc_sem);
|
||||
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
|
||||
|
@ -49,7 +49,7 @@ struct jffs2_sb_info {
|
||||
struct mtd_info *mtd;
|
||||
|
||||
uint32_t highest_ino;
|
||||
uint32_t checked_ino;
|
||||
uint32_t check_ino; /* *NEXT* inode to be checked */
|
||||
|
||||
unsigned int flags;
|
||||
|
||||
|
@ -846,8 +846,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
|
||||
return 1;
|
||||
|
||||
if (c->unchecked_size) {
|
||||
jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
|
||||
c->unchecked_size, c->checked_ino);
|
||||
jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, check_ino #%d\n",
|
||||
c->unchecked_size, c->check_ino);
|
||||
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)
|
||||
{
|
||||
struct nand_ecclayout *oinfo = c->mtd->ecclayout;
|
||||
|
||||
if (!c->mtd->oobsize)
|
||||
return 0;
|
||||
|
||||
/* Cleanmarker is out-of-band, so inline size zero */
|
||||
c->cleanmarker_size = 0;
|
||||
|
||||
if (!oinfo || oinfo->oobavail == 0) {
|
||||
if (c->mtd->oobavail == 0) {
|
||||
pr_err("inconsistent device description\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
jffs2_dbg(1, "using OOB on NAND\n");
|
||||
|
||||
c->oobavail = oinfo->oobavail;
|
||||
c->oobavail = c->mtd->oobavail;
|
||||
|
||||
/* Initialise write buffer */
|
||||
init_rwsem(&c->wbuf_sem);
|
||||
|
@ -166,7 +166,6 @@ struct bbm_info {
|
||||
};
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif /* __LINUX_MTD_BBM_H */
|
||||
|
@ -44,7 +44,6 @@ struct INFTLrecord {
|
||||
unsigned int nb_blocks; /* number of physical blocks */
|
||||
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
|
||||
struct erase_info instr;
|
||||
struct nand_ecclayout oobinfo;
|
||||
};
|
||||
|
||||
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. */
|
||||
void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
|
||||
|
||||
/* set_vpp() must handle being reentered -- enable, enable, disable
|
||||
must leave it enabled. */
|
||||
/* This will be called with 1 as parameter when the first map user
|
||||
* 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);
|
||||
|
||||
unsigned long pfow_base;
|
||||
|
@ -105,7 +105,6 @@ struct mtd_oob_ops {
|
||||
struct nand_ecclayout {
|
||||
__u32 eccbytes;
|
||||
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
|
||||
__u32 oobavail;
|
||||
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;
|
||||
}
|
||||
|
||||
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_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
void **virt, resource_size_t *phys);
|
||||
|
@ -168,6 +168,12 @@ typedef enum {
|
||||
/* Device supports subpage reads */
|
||||
#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 */
|
||||
#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 (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
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);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||
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
|
||||
* @options: Option flags, e.g. 16bit buswidth
|
||||
* @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
|
||||
*/
|
||||
struct platform_nand_chip {
|
||||
@ -904,7 +909,6 @@ struct platform_nand_chip {
|
||||
int chip_offset;
|
||||
int nr_partitions;
|
||||
struct mtd_partition *partitions;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
int chip_delay;
|
||||
unsigned int 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
|
||||
*/
|
||||
struct nand_bch_control *
|
||||
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
|
||||
unsigned int eccbytes, struct nand_ecclayout **ecclayout);
|
||||
struct nand_bch_control *nand_bch_init(struct mtd_info *mtd);
|
||||
/*
|
||||
* Release BCH encoder/decoder resources
|
||||
*/
|
||||
@ -58,9 +56,7 @@ nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline struct nand_bch_control *
|
||||
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
|
||||
unsigned int eccbytes, struct nand_ecclayout **ecclayout)
|
||||
static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ struct NFTLrecord {
|
||||
unsigned int nb_blocks; /* number of physical blocks */
|
||||
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
|
||||
struct erase_info instr;
|
||||
struct nand_ecclayout oobinfo;
|
||||
};
|
||||
|
||||
int NFTL_mount(struct NFTLrecord *s);
|
||||
|
@ -85,6 +85,7 @@
|
||||
#define SR_BP0 BIT(2) /* Block protect 0 */
|
||||
#define SR_BP1 BIT(3) /* Block protect 1 */
|
||||
#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_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
|
||||
@ -116,6 +117,7 @@ enum spi_nor_ops {
|
||||
|
||||
enum spi_nor_option_flags {
|
||||
SNOR_F_USE_FSR = BIT(0),
|
||||
SNOR_F_HAS_SR_TB = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,6 @@ struct s3c2410_nand_set {
|
||||
char *name;
|
||||
int *nr_map;
|
||||
struct mtd_partition *partitions;
|
||||
struct nand_ecclayout *ecc_layout;
|
||||
};
|
||||
|
||||
struct s3c2410_platform_nand {
|
||||
|
Loading…
Reference in New Issue
Block a user