MTD updates for 4.4-rc1:
Core * WARN (in some cases) when a struct mtd_info is registered multiple times; in the past this was "supported", but it's still error prone for future development. There's only one ugly case of this left in the tree (that we're aware of) and the owners are aware of the problems there. * fix potential deadlock in the blkdev removal path NOTE: the (potential) deadlock was introduced in a for-stable patch. This one is also marked for -stable. * ioctl(BLKPG) compat_ioctl support; resolves issues with 32-bit user space vs. 64-bit kernel space * Set MTD parent device correctly throughout the tree, so the tree structure appears correctly in sysfs; many drivers were missing this (soft) requirement * Move device tree partitions (ofpart) into a dedicated 'partitions' subnode; this helps to disambiguate whether a node is a partition or some other auxiliary data * Improve error handling for partitioning failures NAND * General: Increase timeout period, for corner-case systems with less-than-accurate jiffies * Fix OF-based autoloading of several NAND drivers when built as modules * pxa3xx_nand: - Rework timing configuration to be more dynamic - Refactor PM support * brcmnand: prepare for NorthStar 2 support (ARM64, 16-bit NAND chips) * sunxi_nand: refactoring and a few bug fixes * vf610: new NAND driver * FSMC: add SW BCH support; support common NAND DT bindings * lpc32xx_slc: refactor and improve timing calculations logic * denali: support for rev 5.1 SPI NOR * Layering improvements * Added Winbond lock/unlock support * Added mtd_is_locked() (i.e., ioctl(MEMISLOCKED)) support * Increase full-chip-erase timeout linearly with flash size * fsl-quadspi: fix compile for non-ARM architectures * New flash support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWPPj0AAoJEFySrpd9RFgtJEgP/RnfXRHBX51cUl4r8XrxiGtz zlH5zFOCYWEOtlGjoD25wp+A5RRjWi62At6KmLJZncf0clJ65fyKHjt/JEg6YuI6 DUTMepTwyC2Wh7Ux1ZEH3KOnl64xh5p+Wf7Tl4yUr0DCd26VEwE9o7tPRlahv9nx OhGCWS+uSsxW0Q2wDLCypFzXsnDBeoGxDO7VCIzle4d3aJ4PCcQIXINlr6ZYpocT VTTadPCMsmYq6AgV5W3KYGYLj62ITiN2YxdJRacg+QkKLfl21u2wVy9Ahk/oj5TG bV0DHiz6ky1F2K/bvJibcRFABiYup9UXIo1IzwLloSxEJ/GfennMC29xn9K/0gid e2kO/Ajh/bihgJXzoAoZD/40YJK40X5VW9rQZgo482sMBGLmJOzZiOHOIEXaVohs djQ7sbCwPmLwqp7C+6WTn8frp6ntIe9iXdmjDuR/WlPP0Sh5rI3cUeBQrJXEYxwc aYt1Zxkst6gEMPQJ/S2TiKwWm0BxVWEUEjKmt9FPWOaQnwmoeju0Y/6jIvs1TQKM vO8cmS17QyPUWT2kGIDmZ51KayY26uDC8/NA2t1HDYCiFbpDp61kgu2wyoBUTg7q YIAvjtnwOaG9qk0SLfZu4FEgNi4a/bC1bxGxChsi+S2krpNQAeMlPY394cf6OdsE j+CV/Ko0DguO26bO0MWr =UzSW -----END PGP SIGNATURE----- Merge tag 'for-linus-20151106' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: "Core: - WARN (in some cases) when a struct mtd_info is registered multiple times; in the past this was "supported", but it's still error prone for future development. There's only one ugly case of this left in the tree (that we're aware of) and the owners are aware of the problems there. - fix potential deadlock in the blkdev removal path NOTE: the (potential) deadlock was introduced in a for-stable patch. This one is also marked for -stable. - ioctl(BLKPG) compat_ioctl support; resolves issues with 32-bit user space vs 64-bit kernel space - Set MTD parent device correctly throughout the tree, so the tree structure appears correctly in sysfs; many drivers were missing this (soft) requirement - Move device tree partitions (ofpart) into a dedicated 'partitions' subnode; this helps to disambiguate whether a node is a partition or some other auxiliary data - Improve error handling for partitioning failures NAND: - General: Increase timeout period, for corner-case systems with less-than-accurate jiffies - Fix OF-based autoloading of several NAND drivers when built as modules - pxa3xx_nand: - Rework timing configuration to be more dynamic - Refactor PM support - brcmnand: prepare for NorthStar 2 support (ARM64, 16-bit NAND chips) - sunxi_nand: refactoring and a few bug fixes - vf610: new NAND driver - FSMC: add SW BCH support; support common NAND DT bindings - lpc32xx_slc: refactor and improve timing calculations logic - denali: support for rev 5.1 SPI NOR: - Layering improvements - Added Winbond lock/unlock support - Added mtd_is_locked() (i.e., ioctl(MEMISLOCKED)) support - Increase full-chip-erase timeout linearly with flash size - fsl-quadspi: fix compile for non-ARM architectures - New flash support" * tag 'for-linus-20151106' of git://git.infradead.org/linux-mtd: (169 commits) mtd: don't WARN about overloaded users of mtd->reboot_notifier.notifier_call mtd: nand: sunxi: avoid retrieving data before ECC pass mtd: nand: sunxi: fix sunxi_nfc_hw_ecc_read/write_chunk() mtd: blkdevs: fix potential deadlock + lockdep warnings mtd: ofpart: move ofpart partitions to a dedicated dt node doc: dt: mtd: support partitions in a special 'partitions' subnode mtd: brcmnand: Force 8bit mode before doing nand_scan_ident() mtd: brcmnand: factor out CFG and CFG_EXT bitfields mtd: mtdpart: Do not fail mtd probe when parsing partitions fails mtd: fsl-quadspi: fix macro collision problems with READ/WRITE mtd: warn when registering the same master many times mtd: fixup corner case error handling in mtd_device_parse_register() mtd: tests: Replace timeval with ktime_t mtd: fsmc_nand: Add BCH4 SW ECC support for SPEAr600 mtd: nand: vf610_nfc: use nand_check_erased_ecc_chunk() helper mtd: nand: increase ready wait timeout and report timeouts mtd: docg3: off by one in doc_register_sysfs() mtd: pxa3xx_nand: clean up the pxa3xx timings mtd: pxa3xx_nand: rework flash detection and timing setup mtd: pxa3xx_nand: add helpers to setup the timings ...
This commit is contained in:
commit
02f0d3f758
|
@ -30,6 +30,12 @@ Optional properties:
|
||||||
command is asserted. Zero means one cycle, 255 means 256
|
command is asserted. Zero means one cycle, 255 means 256
|
||||||
cycles.
|
cycles.
|
||||||
- bank: default NAND bank to use (0-3 are valid, 0 is the default).
|
- bank: default NAND bank to use (0-3 are valid, 0 is the default).
|
||||||
|
- nand-ecc-mode : see nand.txt
|
||||||
|
- nand-ecc-strength : see nand.txt
|
||||||
|
- nand-ecc-step-size : see nand.txt
|
||||||
|
|
||||||
|
Can support 1-bit HW ECC (default) or if stronger correction is required,
|
||||||
|
software-based BCH.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,17 @@ Partitions can be represented by sub-nodes of an mtd device. This can be used
|
||||||
on platforms which have strong conventions about which portions of a flash are
|
on platforms which have strong conventions about which portions of a flash are
|
||||||
used for what purposes, but which don't use an on-flash partition table such
|
used for what purposes, but which don't use an on-flash partition table such
|
||||||
as RedBoot.
|
as RedBoot.
|
||||||
NOTE: if the sub-node has a compatible string, then it is not a partition.
|
|
||||||
|
|
||||||
#address-cells & #size-cells must both be present in the mtd device. There are
|
The partition table should be a subnode of the mtd node and should be named
|
||||||
two valid values for both:
|
'partitions'. Partitions are defined in subnodes of the partitions node.
|
||||||
|
|
||||||
|
For backwards compatibility partitions as direct subnodes of the mtd device are
|
||||||
|
supported. This use is discouraged.
|
||||||
|
NOTE: also for backwards compatibility, direct subnodes that have a compatible
|
||||||
|
string are not considered partitions, as they may be used for other bindings.
|
||||||
|
|
||||||
|
#address-cells & #size-cells must both be present in the partitions subnode of the
|
||||||
|
mtd device. There are two valid values for both:
|
||||||
<1>: for partitions that require a single 32-bit cell to represent their
|
<1>: for partitions that require a single 32-bit cell to represent their
|
||||||
size/address (aka the value is below 4 GiB)
|
size/address (aka the value is below 4 GiB)
|
||||||
<2>: for partitions that require two 32-bit cells to represent their
|
<2>: for partitions that require two 32-bit cells to represent their
|
||||||
|
@ -28,44 +35,50 @@ Examples:
|
||||||
|
|
||||||
|
|
||||||
flash@0 {
|
flash@0 {
|
||||||
#address-cells = <1>;
|
partitions {
|
||||||
#size-cells = <1>;
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
partition@0 {
|
partition@0 {
|
||||||
label = "u-boot";
|
label = "u-boot";
|
||||||
reg = <0x0000000 0x100000>;
|
reg = <0x0000000 0x100000>;
|
||||||
read-only;
|
read-only;
|
||||||
};
|
};
|
||||||
|
|
||||||
uimage@100000 {
|
uimage@100000 {
|
||||||
reg = <0x0100000 0x200000>;
|
reg = <0x0100000 0x200000>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
flash@1 {
|
flash@1 {
|
||||||
#address-cells = <1>;
|
partitions {
|
||||||
#size-cells = <2>;
|
#address-cells = <1>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
|
||||||
/* a 4 GiB partition */
|
/* a 4 GiB partition */
|
||||||
partition@0 {
|
partition@0 {
|
||||||
label = "filesystem";
|
label = "filesystem";
|
||||||
reg = <0x00000000 0x1 0x00000000>;
|
reg = <0x00000000 0x1 0x00000000>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
flash@2 {
|
flash@2 {
|
||||||
#address-cells = <2>;
|
partitions {
|
||||||
#size-cells = <2>;
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
|
||||||
/* an 8 GiB partition */
|
/* an 8 GiB partition */
|
||||||
partition@0 {
|
partition@0 {
|
||||||
label = "filesystem #1";
|
label = "filesystem #1";
|
||||||
reg = <0x0 0x00000000 0x2 0x00000000>;
|
reg = <0x0 0x00000000 0x2 0x00000000>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* a 4 GiB partition */
|
/* a 4 GiB partition */
|
||||||
partition@200000000 {
|
partition@200000000 {
|
||||||
label = "filesystem #2";
|
label = "filesystem #2";
|
||||||
reg = <0x2 0x00000000 0x1 0x00000000>;
|
reg = <0x2 0x00000000 0x1 0x00000000>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
59
Documentation/devicetree/bindings/mtd/vf610-nfc.txt
Normal file
59
Documentation/devicetree/bindings/mtd/vf610-nfc.txt
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
Freescale's NAND flash controller (NFC)
|
||||||
|
|
||||||
|
This variant of the Freescale NAND flash controller (NFC) can be found on
|
||||||
|
Vybrid (vf610), MPC5125, MCF54418 and Kinetis K70.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be set to "fsl,vf610-nfc".
|
||||||
|
- reg: address range of the NFC.
|
||||||
|
- interrupts: interrupt of the NFC.
|
||||||
|
- #address-cells: shall be set to 1. Encode the nand CS.
|
||||||
|
- #size-cells : shall be set to 0.
|
||||||
|
- assigned-clocks: main clock from the SoC, for Vybrid <&clks VF610_CLK_NFC>;
|
||||||
|
- assigned-clock-rates: The NAND bus timing is derived from this clock
|
||||||
|
rate and should not exceed maximum timing for any NAND memory chip
|
||||||
|
in a board stuffing. Typical NAND memory timings derived from this
|
||||||
|
clock are found in the SoC hardware reference manual. Furthermore,
|
||||||
|
there might be restrictions on maximum rates when using hardware ECC.
|
||||||
|
|
||||||
|
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
||||||
|
representing partitions.
|
||||||
|
|
||||||
|
Required children nodes:
|
||||||
|
Children nodes represent the available nand chips. Currently the driver can
|
||||||
|
only handle one NAND chip.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be set to "fsl,vf610-nfc-cs".
|
||||||
|
- nand-bus-width: see nand.txt
|
||||||
|
- nand-ecc-mode: see nand.txt
|
||||||
|
|
||||||
|
Required properties for hardware ECC:
|
||||||
|
- nand-ecc-strength: supported strengths are 24 and 32 bit (see nand.txt)
|
||||||
|
- nand-ecc-step-size: step size equals page size, currently only 2k pages are
|
||||||
|
supported
|
||||||
|
- nand-on-flash-bbt: see nand.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
nfc: nand@400e0000 {
|
||||||
|
compatible = "fsl,vf610-nfc";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x400e0000 0x4000>;
|
||||||
|
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&clks VF610_CLK_NFC>;
|
||||||
|
clock-names = "nfc";
|
||||||
|
assigned-clocks = <&clks VF610_CLK_NFC>;
|
||||||
|
assigned-clock-rates = <33000000>;
|
||||||
|
|
||||||
|
nand@0 {
|
||||||
|
compatible = "fsl,vf610-nfc-nandcs";
|
||||||
|
reg = <0>;
|
||||||
|
nand-bus-width = <8>;
|
||||||
|
nand-ecc-mode = "hw";
|
||||||
|
nand-ecc-strength = <32>;
|
||||||
|
nand-ecc-step-size = <2048>;
|
||||||
|
nand-on-flash-bbt;
|
||||||
|
};
|
||||||
|
};
|
|
@ -11270,6 +11270,12 @@ S: Maintained
|
||||||
F: Documentation/fb/uvesafb.txt
|
F: Documentation/fb/uvesafb.txt
|
||||||
F: drivers/video/fbdev/uvesafb.*
|
F: drivers/video/fbdev/uvesafb.*
|
||||||
|
|
||||||
|
VF610 NAND DRIVER
|
||||||
|
M: Stefan Agner <stefan@agner.ch>
|
||||||
|
L: linux-mtd@lists.infradead.org
|
||||||
|
S: Supported
|
||||||
|
F: drivers/mtd/nand/vf610_nfc.c
|
||||||
|
|
||||||
VFAT/FAT/MSDOS FILESYSTEM
|
VFAT/FAT/MSDOS FILESYSTEM
|
||||||
M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
|
M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
|
@ -27,8 +27,6 @@ static struct regulator_consumer_supply dummy_supplies[] = {
|
||||||
REGULATOR_SUPPLY("vdd33a", "smsc911x"),
|
REGULATOR_SUPPLY("vdd33a", "smsc911x"),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
|
||||||
|
|
||||||
static struct mtd_partition rsk_partitions[] = {
|
static struct mtd_partition rsk_partitions[] = {
|
||||||
{
|
{
|
||||||
.name = "Bootloader",
|
.name = "Bootloader",
|
||||||
|
@ -50,7 +48,6 @@ static struct physmap_flash_data flash_data = {
|
||||||
.parts = rsk_partitions,
|
.parts = rsk_partitions,
|
||||||
.nr_parts = ARRAY_SIZE(rsk_partitions),
|
.nr_parts = ARRAY_SIZE(rsk_partitions),
|
||||||
.width = 2,
|
.width = 2,
|
||||||
.part_probe_types = part_probes,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct resource flash_resource = {
|
static struct resource flash_resource = {
|
||||||
|
|
|
@ -48,6 +48,8 @@
|
||||||
* edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
|
* edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "mtd: " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
|
@ -55,9 +57,6 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
|
||||||
/* error message prefix */
|
|
||||||
#define ERRP "mtd: "
|
|
||||||
|
|
||||||
/* debug macro */
|
/* debug macro */
|
||||||
#if 0
|
#if 0
|
||||||
#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
|
#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
|
||||||
|
@ -115,9 +114,8 @@ static struct mtd_partition * newpart(char *s,
|
||||||
s++;
|
s++;
|
||||||
} else {
|
} else {
|
||||||
size = memparse(s, &s);
|
size = memparse(s, &s);
|
||||||
if (size < PAGE_SIZE) {
|
if (!size) {
|
||||||
printk(KERN_ERR ERRP "partition size too small (%llx)\n",
|
pr_err("partition has size 0\n");
|
||||||
size);
|
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +140,7 @@ static struct mtd_partition * newpart(char *s,
|
||||||
name = ++s;
|
name = ++s;
|
||||||
p = strchr(name, delim);
|
p = strchr(name, delim);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim);
|
pr_err("no closing %c found in partition name\n", delim);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
name_len = p - name;
|
name_len = p - name;
|
||||||
|
@ -170,7 +168,7 @@ static struct mtd_partition * newpart(char *s,
|
||||||
/* test if more partitions are following */
|
/* test if more partitions are following */
|
||||||
if (*s == ',') {
|
if (*s == ',') {
|
||||||
if (size == SIZE_REMAINING) {
|
if (size == SIZE_REMAINING) {
|
||||||
printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n");
|
pr_err("no partitions allowed after a fill-up partition\n");
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
/* more partitions follow, parse them */
|
/* more partitions follow, parse them */
|
||||||
|
@ -237,7 +235,7 @@ static int mtdpart_setup_real(char *s)
|
||||||
/* fetch <mtd-id> */
|
/* fetch <mtd-id> */
|
||||||
p = strchr(s, ':');
|
p = strchr(s, ':');
|
||||||
if (!p) {
|
if (!p) {
|
||||||
printk(KERN_ERR ERRP "no mtd-id\n");
|
pr_err("no mtd-id\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
mtd_id_len = p - mtd_id;
|
mtd_id_len = p - mtd_id;
|
||||||
|
@ -289,7 +287,7 @@ static int mtdpart_setup_real(char *s)
|
||||||
|
|
||||||
/* does another spec follow? */
|
/* does another spec follow? */
|
||||||
if (*s != ';') {
|
if (*s != ';') {
|
||||||
printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s);
|
pr_err("bad character after partition (%c)\n", *s);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
s++;
|
s++;
|
||||||
|
@ -343,17 +341,15 @@ static int parse_cmdline_partitions(struct mtd_info *master,
|
||||||
part->parts[i].size = master->size - offset;
|
part->parts[i].size = master->size - offset;
|
||||||
|
|
||||||
if (offset + part->parts[i].size > master->size) {
|
if (offset + part->parts[i].size > master->size) {
|
||||||
printk(KERN_WARNING ERRP
|
pr_warn("%s: partitioning exceeds flash size, truncating\n",
|
||||||
"%s: partitioning exceeds flash size, truncating\n",
|
part->mtd_id);
|
||||||
part->mtd_id);
|
|
||||||
part->parts[i].size = master->size - offset;
|
part->parts[i].size = master->size - offset;
|
||||||
}
|
}
|
||||||
offset += part->parts[i].size;
|
offset += part->parts[i].size;
|
||||||
|
|
||||||
if (part->parts[i].size == 0) {
|
if (part->parts[i].size == 0) {
|
||||||
printk(KERN_WARNING ERRP
|
pr_warn("%s: skipping zero sized partition\n",
|
||||||
"%s: skipping zero sized partition\n",
|
part->mtd_id);
|
||||||
part->mtd_id);
|
|
||||||
part->num_parts--;
|
part->num_parts--;
|
||||||
memmove(&part->parts[i], &part->parts[i + 1],
|
memmove(&part->parts[i], &part->parts[i + 1],
|
||||||
sizeof(*part->parts) * (part->num_parts - i));
|
sizeof(*part->parts) * (part->num_parts - i));
|
||||||
|
|
|
@ -237,13 +237,14 @@ static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s)
|
static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s,
|
||||||
|
struct device *dev)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = &b47s->mtd;
|
struct mtd_info *mtd = &b47s->mtd;
|
||||||
|
|
||||||
mtd->priv = b47s;
|
mtd->priv = b47s;
|
||||||
|
mtd->dev.parent = dev;
|
||||||
mtd->name = "bcm47xxsflash";
|
mtd->name = "bcm47xxsflash";
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
|
|
||||||
mtd->type = MTD_NORFLASH;
|
mtd->type = MTD_NORFLASH;
|
||||||
mtd->flags = MTD_CAP_NORFLASH;
|
mtd->flags = MTD_CAP_NORFLASH;
|
||||||
|
@ -300,7 +301,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
|
||||||
b47s->blocksize = sflash->blocksize;
|
b47s->blocksize = sflash->blocksize;
|
||||||
b47s->numblocks = sflash->numblocks;
|
b47s->numblocks = sflash->numblocks;
|
||||||
b47s->size = sflash->size;
|
b47s->size = sflash->size;
|
||||||
bcm47xxsflash_fill_mtd(b47s);
|
bcm47xxsflash_fill_mtd(b47s, &pdev->dev);
|
||||||
|
|
||||||
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
|
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -1620,20 +1620,30 @@ static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
|
||||||
static int doc_register_sysfs(struct platform_device *pdev,
|
static int doc_register_sysfs(struct platform_device *pdev,
|
||||||
struct docg3_cascade *cascade)
|
struct docg3_cascade *cascade)
|
||||||
{
|
{
|
||||||
int ret = 0, floor, i = 0;
|
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
int floor;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS &&
|
for (floor = 0;
|
||||||
cascade->floors[floor]; floor++)
|
floor < DOC_MAX_NBFLOORS && cascade->floors[floor];
|
||||||
for (i = 0; !ret && i < 4; i++)
|
floor++) {
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
|
ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
|
||||||
if (!ret)
|
if (ret)
|
||||||
return 0;
|
goto remove_files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
remove_files:
|
||||||
do {
|
do {
|
||||||
while (--i >= 0)
|
while (--i >= 0)
|
||||||
device_remove_file(dev, &doc_sys_attrs[floor][i]);
|
device_remove_file(dev, &doc_sys_attrs[floor][i]);
|
||||||
i = 4;
|
i = 4;
|
||||||
} while (--floor >= 0);
|
} while (--floor >= 0);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1843,7 +1853,6 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||||
mtd->erasesize /= 2;
|
mtd->erasesize /= 2;
|
||||||
mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
|
mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
|
||||||
mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
|
mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
mtd->_erase = doc_erase;
|
mtd->_erase = doc_erase;
|
||||||
mtd->_read = doc_read;
|
mtd->_read = doc_read;
|
||||||
mtd->_write = doc_write;
|
mtd->_write = doc_write;
|
||||||
|
@ -1885,6 +1894,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
|
||||||
if (!mtd)
|
if (!mtd)
|
||||||
goto nomem2;
|
goto nomem2;
|
||||||
mtd->priv = docg3;
|
mtd->priv = docg3;
|
||||||
|
mtd->dev.parent = dev;
|
||||||
bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1,
|
bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1,
|
||||||
8 * DOC_LAYOUT_PAGE_SIZE);
|
8 * DOC_LAYOUT_PAGE_SIZE);
|
||||||
docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL);
|
docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL);
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
struct m25p {
|
struct m25p {
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct spi_nor spi_nor;
|
struct spi_nor spi_nor;
|
||||||
struct mtd_info mtd;
|
|
||||||
u8 command[MAX_CMD_SIZE];
|
u8 command[MAX_CMD_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,8 +61,7 @@ static int m25p_cmdsz(struct spi_nor *nor)
|
||||||
return 1 + nor->addr_width;
|
return 1 + nor->addr_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
|
static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
||||||
int wr_en)
|
|
||||||
{
|
{
|
||||||
struct m25p *flash = nor->priv;
|
struct m25p *flash = nor->priv;
|
||||||
struct spi_device *spi = flash->spi;
|
struct spi_device *spi = flash->spi;
|
||||||
|
@ -159,7 +157,7 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset)
|
||||||
struct m25p *flash = nor->priv;
|
struct m25p *flash = nor->priv;
|
||||||
|
|
||||||
dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
|
dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
|
||||||
flash->mtd.erasesize / 1024, (u32)offset);
|
flash->spi_nor.mtd.erasesize / 1024, (u32)offset);
|
||||||
|
|
||||||
/* Set up command buffer. */
|
/* Set up command buffer. */
|
||||||
flash->command[0] = nor->erase_opcode;
|
flash->command[0] = nor->erase_opcode;
|
||||||
|
@ -201,11 +199,10 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
nor->read_reg = m25p80_read_reg;
|
nor->read_reg = m25p80_read_reg;
|
||||||
|
|
||||||
nor->dev = &spi->dev;
|
nor->dev = &spi->dev;
|
||||||
nor->mtd = &flash->mtd;
|
nor->flash_node = spi->dev.of_node;
|
||||||
nor->priv = flash;
|
nor->priv = flash;
|
||||||
|
|
||||||
spi_set_drvdata(spi, flash);
|
spi_set_drvdata(spi, flash);
|
||||||
flash->mtd.priv = nor;
|
|
||||||
flash->spi = spi;
|
flash->spi = spi;
|
||||||
|
|
||||||
if (spi->mode & SPI_RX_QUAD)
|
if (spi->mode & SPI_RX_QUAD)
|
||||||
|
@ -214,7 +211,7 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
mode = SPI_NOR_DUAL;
|
mode = SPI_NOR_DUAL;
|
||||||
|
|
||||||
if (data && data->name)
|
if (data && data->name)
|
||||||
flash->mtd.name = data->name;
|
nor->mtd.name = data->name;
|
||||||
|
|
||||||
/* For some (historical?) reason many platforms provide two different
|
/* For some (historical?) reason many platforms provide two different
|
||||||
* names in flash_platform_data: "name" and "type". Quite often name is
|
* names in flash_platform_data: "name" and "type". Quite often name is
|
||||||
|
@ -232,7 +229,7 @@ static int m25p_probe(struct spi_device *spi)
|
||||||
|
|
||||||
ppdata.of_node = spi->dev.of_node;
|
ppdata.of_node = spi->dev.of_node;
|
||||||
|
|
||||||
return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
|
return mtd_device_parse_register(&nor->mtd, NULL, &ppdata,
|
||||||
data ? data->parts : NULL,
|
data ? data->parts : NULL,
|
||||||
data ? data->nr_parts : 0);
|
data ? data->nr_parts : 0);
|
||||||
}
|
}
|
||||||
|
@ -243,7 +240,7 @@ static int m25p_remove(struct spi_device *spi)
|
||||||
struct m25p *flash = spi_get_drvdata(spi);
|
struct m25p *flash = spi_get_drvdata(spi);
|
||||||
|
|
||||||
/* Clean up MTD stuff. */
|
/* Clean up MTD stuff. */
|
||||||
return mtd_device_unregister(&flash->mtd);
|
return mtd_device_unregister(&flash->spi_nor.mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -648,7 +648,6 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
|
||||||
device->size = nr_pages * pagesize;
|
device->size = nr_pages * pagesize;
|
||||||
device->erasesize = pagesize;
|
device->erasesize = pagesize;
|
||||||
device->writesize = pagesize;
|
device->writesize = pagesize;
|
||||||
device->owner = THIS_MODULE;
|
|
||||||
device->type = MTD_DATAFLASH;
|
device->type = MTD_DATAFLASH;
|
||||||
device->flags = MTD_WRITEABLE;
|
device->flags = MTD_WRITEABLE;
|
||||||
device->_erase = dataflash_erase;
|
device->_erase = dataflash_erase;
|
||||||
|
|
|
@ -32,8 +32,29 @@ MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
|
||||||
// We could store these in the mtd structure, but we only support 1 device..
|
// We could store these in the mtd structure, but we only support 1 device..
|
||||||
static struct mtd_info *mtd_info;
|
static struct mtd_info *mtd_info;
|
||||||
|
|
||||||
|
static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Start address must align on block boundary */
|
||||||
|
if (mtd_mod_by_eb(ofs, mtd)) {
|
||||||
|
pr_debug("%s: unaligned address\n", __func__);
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Length must align on block boundary */
|
||||||
|
if (mtd_mod_by_eb(len, mtd)) {
|
||||||
|
pr_debug("%s: length not block aligned\n", __func__);
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
{
|
{
|
||||||
|
if (check_offs_len(mtd, instr->addr, instr->len))
|
||||||
|
return -EINVAL;
|
||||||
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
|
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
|
||||||
instr->state = MTD_ERASE_DONE;
|
instr->state = MTD_ERASE_DONE;
|
||||||
mtd_erase_callback(instr);
|
mtd_erase_callback(instr);
|
||||||
|
|
|
@ -854,6 +854,7 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
|
||||||
else
|
else
|
||||||
flash->mtd.name = flash_devices[flash_index].name;
|
flash->mtd.name = flash_devices[flash_index].name;
|
||||||
|
|
||||||
|
flash->mtd.dev.parent = &pdev->dev;
|
||||||
flash->mtd.type = MTD_NORFLASH;
|
flash->mtd.type = MTD_NORFLASH;
|
||||||
flash->mtd.writesize = 1;
|
flash->mtd.writesize = 1;
|
||||||
flash->mtd.flags = MTD_CAP_NORFLASH;
|
flash->mtd.flags = MTD_CAP_NORFLASH;
|
||||||
|
|
|
@ -374,9 +374,8 @@ static int sst25l_probe(struct spi_device *spi)
|
||||||
data = dev_get_platdata(&spi->dev);
|
data = dev_get_platdata(&spi->dev);
|
||||||
if (data && data->name)
|
if (data && data->name)
|
||||||
flash->mtd.name = data->name;
|
flash->mtd.name = data->name;
|
||||||
else
|
|
||||||
flash->mtd.name = dev_name(&spi->dev);
|
|
||||||
|
|
||||||
|
flash->mtd.dev.parent = &spi->dev;
|
||||||
flash->mtd.type = MTD_NORFLASH;
|
flash->mtd.type = MTD_NORFLASH;
|
||||||
flash->mtd.flags = MTD_CAP_NORFLASH;
|
flash->mtd.flags = MTD_CAP_NORFLASH;
|
||||||
flash->mtd.erasesize = flash_info->erase_size;
|
flash->mtd.erasesize = flash_info->erase_size;
|
||||||
|
|
|
@ -460,6 +460,7 @@ static int lpddr2_nvm_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* Populate mtd_info data structure */
|
/* Populate mtd_info data structure */
|
||||||
*mtd = (struct mtd_info) {
|
*mtd = (struct mtd_info) {
|
||||||
|
.dev = { .parent = &pdev->dev },
|
||||||
.name = pdev->dev.init_name,
|
.name = pdev->dev.init_name,
|
||||||
.type = MTD_RAM,
|
.type = MTD_RAM,
|
||||||
.priv = map,
|
.priv = map,
|
||||||
|
|
|
@ -266,7 +266,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
|
||||||
kfree(state);
|
kfree(state);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
state->mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
mtd_device_parse_register(state->mtd, part_probe_types, NULL,
|
mtd_device_parse_register(state->mtd, part_probe_types, NULL,
|
||||||
pdata->parts, pdata->nr_parts);
|
pdata->parts, pdata->nr_parts);
|
||||||
|
|
|
@ -90,7 +90,7 @@ static int vr_nor_mtd_setup(struct vr_nor_mtd *p)
|
||||||
if (!p->info)
|
if (!p->info)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
p->info->owner = THIS_MODULE;
|
p->info->dev.parent = &p->dev->dev;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,7 +226,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
|
||||||
err = -ENXIO;
|
err = -ENXIO;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
info->mtd->owner = THIS_MODULE;
|
info->mtd->dev.parent = &dev->dev;
|
||||||
|
|
||||||
/* Use the fast version */
|
/* Use the fast version */
|
||||||
info->map.write = ixp4xx_write16;
|
info->map.write = ixp4xx_write16;
|
||||||
|
|
|
@ -160,7 +160,7 @@ ltq_mtd_probe(struct platform_device *pdev)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
ltq_mtd->mtd->owner = THIS_MODULE;
|
ltq_mtd->mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
cfi = ltq_mtd->map->fldrv_priv;
|
cfi = ltq_mtd->map->fldrv_priv;
|
||||||
cfi->addr_unlock1 ^= 1;
|
cfi->addr_unlock1 ^= 1;
|
||||||
|
|
|
@ -195,7 +195,7 @@ static int latch_addr_flash_probe(struct platform_device *dev)
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
goto iounmap;
|
goto iounmap;
|
||||||
}
|
}
|
||||||
info->mtd->owner = THIS_MODULE;
|
info->mtd->dev.parent = &dev->dev;
|
||||||
|
|
||||||
mtd_device_parse_register(info->mtd, NULL, NULL,
|
mtd_device_parse_register(info->mtd, NULL, NULL,
|
||||||
latch_addr_data->parts,
|
latch_addr_data->parts,
|
||||||
|
|
|
@ -700,6 +700,7 @@ static const struct pcmcia_device_id pcmciamtd_ids[] = {
|
||||||
PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0),
|
PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0),
|
||||||
PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965),
|
PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965),
|
||||||
PCMCIA_DEVICE_PROD_ID12("PRETEC", " 2MB SRAM CARD", 0xebf91155, 0x805360ca),
|
PCMCIA_DEVICE_PROD_ID12("PRETEC", " 2MB SRAM CARD", 0xebf91155, 0x805360ca),
|
||||||
|
PCMCIA_DEVICE_PROD_ID12("PRETEC", " 4MB SRAM CARD", 0xebf91155, 0x20b6bf17),
|
||||||
PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b),
|
PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b),
|
||||||
PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad),
|
PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad),
|
||||||
PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05),
|
PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05),
|
||||||
|
|
|
@ -167,7 +167,6 @@ static int physmap_flash_probe(struct platform_device *dev)
|
||||||
} else {
|
} else {
|
||||||
devices_found++;
|
devices_found++;
|
||||||
}
|
}
|
||||||
info->mtd[i]->owner = THIS_MODULE;
|
|
||||||
info->mtd[i]->dev.parent = &dev->dev;
|
info->mtd[i]->dev.parent = &dev->dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -290,7 +290,6 @@ static int of_flash_probe(struct platform_device *dev)
|
||||||
} else {
|
} else {
|
||||||
info->list_size++;
|
info->list_size++;
|
||||||
}
|
}
|
||||||
info->list[i].mtd->owner = THIS_MODULE;
|
|
||||||
info->list[i].mtd->dev.parent = &dev->dev;
|
info->list[i].mtd->dev.parent = &dev->dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,6 @@ static int platram_probe(struct platform_device *pdev)
|
||||||
goto exit_free;
|
goto exit_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->mtd->owner = THIS_MODULE;
|
|
||||||
info->mtd->dev.parent = &pdev->dev;
|
info->mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
platram_setrw(info, PLATRAM_RW);
|
platram_setrw(info, PLATRAM_RW);
|
||||||
|
|
|
@ -71,8 +71,8 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
|
||||||
info->map.name);
|
info->map.name);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
info->map.cached =
|
info->map.cached = memremap(info->map.phys, info->map.size,
|
||||||
ioremap_cache(info->map.phys, info->map.size);
|
MEMREMAP_WB);
|
||||||
if (!info->map.cached)
|
if (!info->map.cached)
|
||||||
printk(KERN_WARNING "Failed to ioremap cached %s\n",
|
printk(KERN_WARNING "Failed to ioremap cached %s\n",
|
||||||
info->map.name);
|
info->map.name);
|
||||||
|
@ -93,7 +93,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
|
||||||
iounmap(info->map.cached);
|
iounmap(info->map.cached);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
info->mtd->owner = THIS_MODULE;
|
info->mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
mtd_device_parse_register(info->mtd, probes, NULL, flash->parts,
|
mtd_device_parse_register(info->mtd, probes, NULL, flash->parts,
|
||||||
flash->nr_parts);
|
flash->nr_parts);
|
||||||
|
@ -111,7 +111,7 @@ static int pxa2xx_flash_remove(struct platform_device *dev)
|
||||||
map_destroy(info->mtd);
|
map_destroy(info->mtd);
|
||||||
iounmap(info->map.virt);
|
iounmap(info->map.virt);
|
||||||
if (info->map.cached)
|
if (info->map.cached)
|
||||||
iounmap(info->map.cached);
|
memunmap(info->map.cached);
|
||||||
kfree(info);
|
kfree(info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
|
||||||
err = -ENXIO;
|
err = -ENXIO;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
info->mtd->owner = THIS_MODULE;
|
info->mtd->dev.parent = &dev->dev;
|
||||||
err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
|
err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
|
||||||
pdata->nr_parts);
|
pdata->nr_parts);
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,6 @@ static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *r
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
subdev->mtd->owner = THIS_MODULE;
|
|
||||||
|
|
||||||
printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
|
printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
|
||||||
phys, (unsigned)(subdev->mtd->size >> 20),
|
phys, (unsigned)(subdev->mtd->size >> 20),
|
||||||
|
@ -234,6 +233,7 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
|
||||||
if (info->mtd == NULL)
|
if (info->mtd == NULL)
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
}
|
}
|
||||||
|
info->mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
return info;
|
return info;
|
||||||
|
|
|
@ -192,8 +192,8 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
|
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
|
||||||
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
mutex_lock(&mtd_table_mutex);
|
mutex_lock(&mtd_table_mutex);
|
||||||
|
mutex_lock(&dev->lock);
|
||||||
|
|
||||||
if (dev->open)
|
if (dev->open)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
@ -217,8 +217,8 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
dev->open++;
|
dev->open++;
|
||||||
mutex_unlock(&mtd_table_mutex);
|
|
||||||
mutex_unlock(&dev->lock);
|
mutex_unlock(&dev->lock);
|
||||||
|
mutex_unlock(&mtd_table_mutex);
|
||||||
blktrans_dev_put(dev);
|
blktrans_dev_put(dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -228,8 +228,8 @@ error_release:
|
||||||
error_put:
|
error_put:
|
||||||
module_put(dev->tr->owner);
|
module_put(dev->tr->owner);
|
||||||
kref_put(&dev->ref, blktrans_dev_release);
|
kref_put(&dev->ref, blktrans_dev_release);
|
||||||
mutex_unlock(&mtd_table_mutex);
|
|
||||||
mutex_unlock(&dev->lock);
|
mutex_unlock(&dev->lock);
|
||||||
|
mutex_unlock(&mtd_table_mutex);
|
||||||
blktrans_dev_put(dev);
|
blktrans_dev_put(dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -241,8 +241,8 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
mutex_lock(&mtd_table_mutex);
|
mutex_lock(&mtd_table_mutex);
|
||||||
|
mutex_lock(&dev->lock);
|
||||||
|
|
||||||
if (--dev->open)
|
if (--dev->open)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
@ -256,8 +256,8 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
|
||||||
__put_mtd_device(dev->mtd);
|
__put_mtd_device(dev->mtd);
|
||||||
}
|
}
|
||||||
unlock:
|
unlock:
|
||||||
mutex_unlock(&mtd_table_mutex);
|
|
||||||
mutex_unlock(&dev->lock);
|
mutex_unlock(&dev->lock);
|
||||||
|
mutex_unlock(&mtd_table_mutex);
|
||||||
blktrans_dev_put(dev);
|
blktrans_dev_put(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
||||||
snprintf(gd->disk_name, sizeof(gd->disk_name),
|
snprintf(gd->disk_name, sizeof(gd->disk_name),
|
||||||
"%s%d", tr->name, new->devnum);
|
"%s%d", tr->name, new->devnum);
|
||||||
|
|
||||||
set_capacity(gd, (new->size * tr->blksize) >> 9);
|
set_capacity(gd, ((u64)new->size * tr->blksize) >> 9);
|
||||||
|
|
||||||
/* Create the request queue */
|
/* Create the request queue */
|
||||||
spin_lock_init(&new->queue_lock);
|
spin_lock_init(&new->queue_lock);
|
||||||
|
|
|
@ -498,21 +498,17 @@ static int shrink_ecclayout(const struct nand_ecclayout *from,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
|
static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
|
||||||
struct blkpg_ioctl_arg __user *arg)
|
struct blkpg_ioctl_arg *arg)
|
||||||
{
|
{
|
||||||
struct blkpg_ioctl_arg a;
|
|
||||||
struct blkpg_partition p;
|
struct blkpg_partition p;
|
||||||
|
|
||||||
if (!capable(CAP_SYS_ADMIN))
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
|
if (copy_from_user(&p, arg->data, sizeof(p)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
|
switch (arg->op) {
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
switch (a.op) {
|
|
||||||
case BLKPG_ADD_PARTITION:
|
case BLKPG_ADD_PARTITION:
|
||||||
|
|
||||||
/* Only master mtd device must be used to add partitions */
|
/* Only master mtd device must be used to add partitions */
|
||||||
|
@ -966,8 +962,13 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||||
|
|
||||||
case BLKPG:
|
case BLKPG:
|
||||||
{
|
{
|
||||||
ret = mtdchar_blkpg_ioctl(mtd,
|
struct blkpg_ioctl_arg __user *blk_arg = argp;
|
||||||
(struct blkpg_ioctl_arg __user *)arg);
|
struct blkpg_ioctl_arg a;
|
||||||
|
|
||||||
|
if (copy_from_user(&a, blk_arg, sizeof(a)))
|
||||||
|
ret = -EFAULT;
|
||||||
|
else
|
||||||
|
ret = mtdchar_blkpg_ioctl(mtd, &a);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1046,6 +1047,29 @@ static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd,
|
||||||
&buf_user->start);
|
&buf_user->start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BLKPG:
|
||||||
|
{
|
||||||
|
/* Convert from blkpg_compat_ioctl_arg to blkpg_ioctl_arg */
|
||||||
|
struct blkpg_compat_ioctl_arg __user *uarg = argp;
|
||||||
|
struct blkpg_compat_ioctl_arg compat_arg;
|
||||||
|
struct blkpg_ioctl_arg a;
|
||||||
|
|
||||||
|
if (copy_from_user(&compat_arg, uarg, sizeof(compat_arg))) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&a, 0, sizeof(a));
|
||||||
|
a.op = compat_arg.op;
|
||||||
|
a.flags = compat_arg.flags;
|
||||||
|
a.datalen = compat_arg.datalen;
|
||||||
|
a.data = compat_ptr(compat_arg.data);
|
||||||
|
|
||||||
|
ret = mtdchar_blkpg_ioctl(mtd, &a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ret = mtdchar_ioctl(file, cmd, (unsigned long)argp);
|
ret = mtdchar_ioctl(file, cmd, (unsigned long)argp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,6 +387,14 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||||
struct mtd_notifier *not;
|
struct mtd_notifier *not;
|
||||||
int i, error;
|
int i, error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* May occur, for instance, on buggy drivers which call
|
||||||
|
* mtd_device_parse_register() multiple times on the same master MTD,
|
||||||
|
* especially with CONFIG_MTD_PARTITIONED_MASTER=y.
|
||||||
|
*/
|
||||||
|
if (WARN_ONCE(mtd->backing_dev_info, "MTD already registered\n"))
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
mtd->backing_dev_info = &mtd_bdi;
|
mtd->backing_dev_info = &mtd_bdi;
|
||||||
|
|
||||||
BUG_ON(mtd->writesize == 0);
|
BUG_ON(mtd->writesize == 0);
|
||||||
|
@ -418,6 +426,15 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||||
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
|
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
|
||||||
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
|
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
|
||||||
|
|
||||||
|
if (mtd->dev.parent) {
|
||||||
|
if (!mtd->owner && mtd->dev.parent->driver)
|
||||||
|
mtd->owner = mtd->dev.parent->driver->owner;
|
||||||
|
if (!mtd->name)
|
||||||
|
mtd->name = dev_name(mtd->dev.parent);
|
||||||
|
} else {
|
||||||
|
pr_debug("mtd device won't show a device symlink in sysfs\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* Some chips always power up locked. Unlock them now */
|
/* Some chips always power up locked. Unlock them now */
|
||||||
if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
|
if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
|
||||||
error = mtd_unlock(mtd, 0, mtd->size);
|
error = mtd_unlock(mtd, 0, mtd->size);
|
||||||
|
@ -430,7 +447,7 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Caller should have set dev.parent to match the
|
/* Caller should have set dev.parent to match the
|
||||||
* physical device.
|
* physical device, if appropriate.
|
||||||
*/
|
*/
|
||||||
mtd->dev.type = &mtd_devtype;
|
mtd->dev.type = &mtd_devtype;
|
||||||
mtd->dev.class = &mtd_class;
|
mtd->dev.class = &mtd_class;
|
||||||
|
@ -579,9 +596,17 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
|
||||||
else
|
else
|
||||||
ret = nr_parts;
|
ret = nr_parts;
|
||||||
}
|
}
|
||||||
|
/* Didn't come up with either parsed OR fallback partitions */
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n",
|
||||||
|
ret);
|
||||||
|
/* Don't abort on errors; we can still use unpartitioned MTD */
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret >= 0)
|
ret = mtd_add_device_partitions(mtd, real_parts, ret);
|
||||||
ret = mtd_add_device_partitions(mtd, real_parts, ret);
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIXME: some drivers unfortunately call this function more than once.
|
* FIXME: some drivers unfortunately call this function more than once.
|
||||||
|
@ -591,11 +616,14 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
|
||||||
* does cause problems with parse_mtd_partitions() above (e.g.,
|
* does cause problems with parse_mtd_partitions() above (e.g.,
|
||||||
* cmdlineparts will register partitions more than once).
|
* cmdlineparts will register partitions more than once).
|
||||||
*/
|
*/
|
||||||
|
WARN_ONCE(mtd->_reboot && mtd->reboot_notifier.notifier_call,
|
||||||
|
"MTD already registered\n");
|
||||||
if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) {
|
if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) {
|
||||||
mtd->reboot_notifier.notifier_call = mtd_reboot_notifier;
|
mtd->reboot_notifier.notifier_call = mtd_reboot_notifier;
|
||||||
register_reboot_notifier(&mtd->reboot_notifier);
|
register_reboot_notifier(&mtd->reboot_notifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
kfree(real_parts);
|
kfree(real_parts);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1301,6 +1329,7 @@ static void __exit cleanup_mtd(void)
|
||||||
remove_proc_entry("mtd", NULL);
|
remove_proc_entry("mtd", NULL);
|
||||||
class_unregister(&mtd_class);
|
class_unregister(&mtd_class);
|
||||||
bdi_destroy(&mtd_bdi);
|
bdi_destroy(&mtd_bdi);
|
||||||
|
idr_destroy(&mtd_idr);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(init_mtd);
|
module_init(init_mtd);
|
||||||
|
|
|
@ -664,8 +664,10 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||||
|
|
||||||
for (i = 0; i < nbparts; i++) {
|
for (i = 0; i < nbparts; i++) {
|
||||||
slave = allocate_partition(master, parts + i, i, cur_offset);
|
slave = allocate_partition(master, parts + i, i, cur_offset);
|
||||||
if (IS_ERR(slave))
|
if (IS_ERR(slave)) {
|
||||||
|
del_mtd_partitions(master);
|
||||||
return PTR_ERR(slave);
|
return PTR_ERR(slave);
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&mtd_partitions_mutex);
|
mutex_lock(&mtd_partitions_mutex);
|
||||||
list_add(&slave->list, &mtd_partitions);
|
list_add(&slave->list, &mtd_partitions);
|
||||||
|
@ -753,26 +755,37 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
|
||||||
struct mtd_part_parser_data *data)
|
struct mtd_part_parser_data *data)
|
||||||
{
|
{
|
||||||
struct mtd_part_parser *parser;
|
struct mtd_part_parser *parser;
|
||||||
int ret = 0;
|
int ret, err = 0;
|
||||||
|
|
||||||
if (!types)
|
if (!types)
|
||||||
types = default_mtd_part_types;
|
types = default_mtd_part_types;
|
||||||
|
|
||||||
for ( ; ret <= 0 && *types; types++) {
|
for ( ; *types; types++) {
|
||||||
|
pr_debug("%s: parsing partitions %s\n", master->name, *types);
|
||||||
parser = get_partition_parser(*types);
|
parser = get_partition_parser(*types);
|
||||||
if (!parser && !request_module("%s", *types))
|
if (!parser && !request_module("%s", *types))
|
||||||
parser = get_partition_parser(*types);
|
parser = get_partition_parser(*types);
|
||||||
|
pr_debug("%s: got parser %s\n", master->name,
|
||||||
|
parser ? parser->name : NULL);
|
||||||
if (!parser)
|
if (!parser)
|
||||||
continue;
|
continue;
|
||||||
ret = (*parser->parse_fn)(master, pparts, data);
|
ret = (*parser->parse_fn)(master, pparts, data);
|
||||||
|
pr_debug("%s: parser %s: %i\n",
|
||||||
|
master->name, parser->name, ret);
|
||||||
put_partition_parser(parser);
|
put_partition_parser(parser);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
|
printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
|
||||||
ret, parser->name, master->name);
|
ret, parser->name, master->name);
|
||||||
break;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Stash the first error we see; only report it if no parser
|
||||||
|
* succeeds
|
||||||
|
*/
|
||||||
|
if (ret < 0 && !err)
|
||||||
|
err = ret;
|
||||||
}
|
}
|
||||||
return ret;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mtd_is_partition(const struct mtd_info *mtd)
|
int mtd_is_partition(const struct mtd_info *mtd)
|
||||||
|
|
|
@ -393,7 +393,7 @@ config MTD_NAND_GPMI_NAND
|
||||||
|
|
||||||
config MTD_NAND_BRCMNAND
|
config MTD_NAND_BRCMNAND
|
||||||
tristate "Broadcom STB NAND controller"
|
tristate "Broadcom STB NAND controller"
|
||||||
depends on ARM || MIPS
|
depends on ARM || ARM64 || MIPS
|
||||||
help
|
help
|
||||||
Enables the Broadcom NAND controller driver. The controller was
|
Enables the Broadcom NAND controller driver. The controller was
|
||||||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||||
|
@ -460,6 +460,17 @@ config MTD_NAND_MPC5121_NFC
|
||||||
This enables the driver for the NAND flash controller on the
|
This enables the driver for the NAND flash controller on the
|
||||||
MPC5121 SoC.
|
MPC5121 SoC.
|
||||||
|
|
||||||
|
config MTD_NAND_VF610_NFC
|
||||||
|
tristate "Support for Freescale NFC for VF610/MPC5125"
|
||||||
|
depends on (SOC_VF610 || COMPILE_TEST)
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash Controller on some Freescale
|
||||||
|
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
|
||||||
|
The driver supports a maximum 2k page size. With 2k pages and
|
||||||
|
64 bytes or more of OOB, hardware ECC with up to 32-bit error
|
||||||
|
correction is supported. Hardware ECC is only enabled through
|
||||||
|
device tree.
|
||||||
|
|
||||||
config MTD_NAND_MXC
|
config MTD_NAND_MXC
|
||||||
tristate "MXC NAND support"
|
tristate "MXC NAND support"
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC
|
||||||
|
|
|
@ -46,6 +46,7 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
|
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
|
||||||
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
|
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
|
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
|
||||||
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
|
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
|
||||||
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
|
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
|
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
|
||||||
|
|
|
@ -954,7 +954,8 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
|
static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
struct atmel_nand_host *host = chip->priv;
|
struct atmel_nand_host *host = chip->priv;
|
||||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||||
|
@ -2005,7 +2006,8 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
|
||||||
if (likely(!raw))
|
if (likely(!raw))
|
||||||
/* Need to write ecc into oob */
|
/* Need to write ecc into oob */
|
||||||
status = chip->ecc.write_page(mtd, chip, buf, oob_required);
|
status = chip->ecc.write_page(mtd, chip, buf, oob_required,
|
||||||
|
page);
|
||||||
|
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
|
@ -2126,7 +2128,7 @@ static int atmel_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
nand_chip->priv = host; /* link the private data structures */
|
nand_chip->priv = host; /* link the private data structures */
|
||||||
mtd->priv = nand_chip;
|
mtd->priv = nand_chip;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
/* Set address of NAND IO lines */
|
/* Set address of NAND IO lines */
|
||||||
nand_chip->IO_ADDR_R = host->io_base;
|
nand_chip->IO_ADDR_R = host->io_base;
|
||||||
|
|
|
@ -439,7 +439,7 @@ static int au1550nd_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
this = &ctx->chip;
|
this = &ctx->chip;
|
||||||
ctx->info.priv = this;
|
ctx->info.priv = this;
|
||||||
ctx->info.owner = THIS_MODULE;
|
ctx->info.dev.parent = &pdev->dev;
|
||||||
|
|
||||||
/* figure out which CS# r->start belongs to */
|
/* figure out which CS# r->start belongs to */
|
||||||
cs = find_nand_cs(r->start);
|
cs = find_nand_cs(r->start);
|
||||||
|
|
|
@ -34,7 +34,7 @@ static int bcm47xxnflash_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
b47n->nand_chip.priv = b47n;
|
b47n->nand_chip.priv = b47n;
|
||||||
b47n->mtd.owner = THIS_MODULE;
|
b47n->mtd.dev.parent = &pdev->dev;
|
||||||
b47n->mtd.priv = &b47n->nand_chip; /* Required */
|
b47n->mtd.priv = &b47n->nand_chip; /* Required */
|
||||||
b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash);
|
b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash);
|
||||||
|
|
||||||
|
|
|
@ -566,7 +566,8 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
|
static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
bf5xx_nand_write_buf(mtd, buf, mtd->writesize);
|
bf5xx_nand_write_buf(mtd, buf, mtd->writesize);
|
||||||
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
@ -782,7 +783,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
||||||
/* initialise mtd info data struct */
|
/* initialise mtd info data struct */
|
||||||
mtd = &info->mtd;
|
mtd = &info->mtd;
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
/* initialise the hardware */
|
/* initialise the hardware */
|
||||||
err = bf5xx_nand_hw_init(info);
|
err = bf5xx_nand_hw_init(info);
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
|
|
||||||
#include "brcmnand.h"
|
#include "brcmnand.h"
|
||||||
|
|
||||||
struct bcm63138_nand_soc_priv {
|
struct bcm63138_nand_soc {
|
||||||
|
struct brcmnand_soc soc;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,7 +36,8 @@ enum {
|
||||||
|
|
||||||
static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
|
static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
|
||||||
{
|
{
|
||||||
struct bcm63138_nand_soc_priv *priv = soc->priv;
|
struct bcm63138_nand_soc *priv =
|
||||||
|
container_of(soc, struct bcm63138_nand_soc, soc);
|
||||||
void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
|
void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
|
||||||
u32 val = brcmnand_readl(mmio);
|
u32 val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
@ -49,7 +51,8 @@ static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
|
||||||
|
|
||||||
static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||||
{
|
{
|
||||||
struct bcm63138_nand_soc_priv *priv = soc->priv;
|
struct bcm63138_nand_soc *priv =
|
||||||
|
container_of(soc, struct bcm63138_nand_soc, soc);
|
||||||
void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
|
void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
|
||||||
u32 val = brcmnand_readl(mmio);
|
u32 val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
@ -64,25 +67,20 @@ static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||||
static int bcm63138_nand_probe(struct platform_device *pdev)
|
static int bcm63138_nand_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct bcm63138_nand_soc_priv *priv;
|
struct bcm63138_nand_soc *priv;
|
||||||
struct brcmnand_soc *soc;
|
struct brcmnand_soc *soc;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
||||||
soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
|
|
||||||
if (!soc)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
soc = &priv->soc;
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base");
|
||||||
priv->base = devm_ioremap_resource(dev, res);
|
priv->base = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(priv->base))
|
if (IS_ERR(priv->base))
|
||||||
return PTR_ERR(priv->base);
|
return PTR_ERR(priv->base);
|
||||||
|
|
||||||
soc->pdev = pdev;
|
|
||||||
soc->priv = priv;
|
|
||||||
soc->ctlrdy_ack = bcm63138_nand_intc_ack;
|
soc->ctlrdy_ack = bcm63138_nand_intc_ack;
|
||||||
soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
|
soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
|
||||||
|
|
||||||
|
|
|
@ -344,6 +344,28 @@ static const u8 brcmnand_cs_offsets_cs0[] = {
|
||||||
[BRCMNAND_CS_TIMING2] = 0x14,
|
[BRCMNAND_CS_TIMING2] = 0x14,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had
|
||||||
|
* one config register, but once the bitfields overflowed, newer controllers
|
||||||
|
* (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
CFG_BLK_ADR_BYTES_SHIFT = 8,
|
||||||
|
CFG_COL_ADR_BYTES_SHIFT = 12,
|
||||||
|
CFG_FUL_ADR_BYTES_SHIFT = 16,
|
||||||
|
CFG_BUS_WIDTH_SHIFT = 23,
|
||||||
|
CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT),
|
||||||
|
CFG_DEVICE_SIZE_SHIFT = 24,
|
||||||
|
|
||||||
|
/* Only for pre-v7.1 (with no CFG_EXT register) */
|
||||||
|
CFG_PAGE_SIZE_SHIFT = 20,
|
||||||
|
CFG_BLK_SIZE_SHIFT = 28,
|
||||||
|
|
||||||
|
/* Only for v7.1+ (with CFG_EXT register) */
|
||||||
|
CFG_EXT_PAGE_SIZE_SHIFT = 0,
|
||||||
|
CFG_EXT_BLK_SIZE_SHIFT = 4,
|
||||||
|
};
|
||||||
|
|
||||||
/* BRCMNAND_INTFC_STATUS */
|
/* BRCMNAND_INTFC_STATUS */
|
||||||
enum {
|
enum {
|
||||||
INTFC_FLASH_STATUS = GENMASK(7, 0),
|
INTFC_FLASH_STATUS = GENMASK(7, 0),
|
||||||
|
@ -1544,9 +1566,9 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
|
||||||
dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
|
dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
|
||||||
|
|
||||||
if (unlikely((u32)buf & 0x03)) {
|
if (unlikely((unsigned long)buf & 0x03)) {
|
||||||
dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
|
dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
|
||||||
buf = (u32 *)((u32)buf & ~0x03);
|
buf = (u32 *)((unsigned long)buf & ~0x03);
|
||||||
}
|
}
|
||||||
|
|
||||||
brcmnand_wp(mtd, 0);
|
brcmnand_wp(mtd, 0);
|
||||||
|
@ -1606,7 +1628,7 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
struct brcmnand_host *host = chip->priv;
|
struct brcmnand_host *host = chip->priv;
|
||||||
void *oob = oob_required ? chip->oob_poi : NULL;
|
void *oob = oob_required ? chip->oob_poi : NULL;
|
||||||
|
@ -1617,7 +1639,7 @@ static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
|
||||||
static int brcmnand_write_page_raw(struct mtd_info *mtd,
|
static int brcmnand_write_page_raw(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, const uint8_t *buf,
|
struct nand_chip *chip, const uint8_t *buf,
|
||||||
int oob_required)
|
int oob_required, int page)
|
||||||
{
|
{
|
||||||
struct brcmnand_host *host = chip->priv;
|
struct brcmnand_host *host = chip->priv;
|
||||||
void *oob = oob_required ? chip->oob_poi : NULL;
|
void *oob = oob_required ? chip->oob_poi : NULL;
|
||||||
|
@ -1720,17 +1742,19 @@ static int brcmnand_set_cfg(struct brcmnand_host *host,
|
||||||
}
|
}
|
||||||
device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
|
device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
|
||||||
|
|
||||||
tmp = (cfg->blk_adr_bytes << 8) |
|
tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) |
|
||||||
(cfg->col_adr_bytes << 12) |
|
(cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) |
|
||||||
(cfg->ful_adr_bytes << 16) |
|
(cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) |
|
||||||
(!!(cfg->device_width == 16) << 23) |
|
(!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) |
|
||||||
(device_size << 24);
|
(device_size << CFG_DEVICE_SIZE_SHIFT);
|
||||||
if (cfg_offs == cfg_ext_offs) {
|
if (cfg_offs == cfg_ext_offs) {
|
||||||
tmp |= (page_size << 20) | (block_size << 28);
|
tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) |
|
||||||
|
(block_size << CFG_BLK_SIZE_SHIFT);
|
||||||
nand_writereg(ctrl, cfg_offs, tmp);
|
nand_writereg(ctrl, cfg_offs, tmp);
|
||||||
} else {
|
} else {
|
||||||
nand_writereg(ctrl, cfg_offs, tmp);
|
nand_writereg(ctrl, cfg_offs, tmp);
|
||||||
tmp = page_size | (block_size << 4);
|
tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) |
|
||||||
|
(block_size << CFG_EXT_BLK_SIZE_SHIFT);
|
||||||
nand_writereg(ctrl, cfg_ext_offs, tmp);
|
nand_writereg(ctrl, cfg_ext_offs, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1792,7 +1816,8 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
|
||||||
|
|
||||||
memset(cfg, 0, sizeof(*cfg));
|
memset(cfg, 0, sizeof(*cfg));
|
||||||
|
|
||||||
ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size",
|
ret = of_property_read_u32(chip->flash_node,
|
||||||
|
"brcm,nand-oob-sector-size",
|
||||||
&oob_sector);
|
&oob_sector);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* Use detected size */
|
/* Use detected size */
|
||||||
|
@ -1888,6 +1913,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
struct nand_chip *chip;
|
struct nand_chip *chip;
|
||||||
int ret;
|
int ret;
|
||||||
|
u16 cfg_offs;
|
||||||
struct mtd_part_parser_data ppdata = { .of_node = dn };
|
struct mtd_part_parser_data ppdata = { .of_node = dn };
|
||||||
|
|
||||||
ret = of_property_read_u32(dn, "reg", &host->cs);
|
ret = of_property_read_u32(dn, "reg", &host->cs);
|
||||||
|
@ -1899,7 +1925,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
|
||||||
mtd = &host->mtd;
|
mtd = &host->mtd;
|
||||||
chip = &host->chip;
|
chip = &host->chip;
|
||||||
|
|
||||||
chip->dn = dn;
|
chip->flash_node = dn;
|
||||||
chip->priv = host;
|
chip->priv = host;
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
|
mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
|
||||||
|
@ -1930,6 +1956,15 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
|
||||||
|
|
||||||
chip->controller = &ctrl->controller;
|
chip->controller = &ctrl->controller;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The bootloader might have configured 16bit mode but
|
||||||
|
* NAND READID command only works in 8bit mode. We force
|
||||||
|
* 8bit mode here to ensure that NAND READID commands works.
|
||||||
|
*/
|
||||||
|
cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
|
||||||
|
nand_writereg(ctrl, cfg_offs,
|
||||||
|
nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
|
||||||
|
|
||||||
if (nand_scan_ident(mtd, 1, NULL))
|
if (nand_scan_ident(mtd, 1, NULL))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,6 @@ struct platform_device;
|
||||||
struct dev_pm_ops;
|
struct dev_pm_ops;
|
||||||
|
|
||||||
struct brcmnand_soc {
|
struct brcmnand_soc {
|
||||||
struct platform_device *pdev;
|
|
||||||
void *priv;
|
|
||||||
bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
|
bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
|
||||||
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
|
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
|
||||||
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare);
|
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare);
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
|
|
||||||
#include "brcmnand.h"
|
#include "brcmnand.h"
|
||||||
|
|
||||||
struct iproc_nand_soc_priv {
|
struct iproc_nand_soc {
|
||||||
|
struct brcmnand_soc soc;
|
||||||
|
|
||||||
void __iomem *idm_base;
|
void __iomem *idm_base;
|
||||||
void __iomem *ext_base;
|
void __iomem *ext_base;
|
||||||
spinlock_t idm_lock;
|
spinlock_t idm_lock;
|
||||||
|
@ -37,7 +39,8 @@ struct iproc_nand_soc_priv {
|
||||||
|
|
||||||
static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
|
static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
|
||||||
{
|
{
|
||||||
struct iproc_nand_soc_priv *priv = soc->priv;
|
struct iproc_nand_soc *priv =
|
||||||
|
container_of(soc, struct iproc_nand_soc, soc);
|
||||||
void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;
|
void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;
|
||||||
u32 val = brcmnand_readl(mmio);
|
u32 val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
@ -51,7 +54,8 @@ static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
|
||||||
|
|
||||||
static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||||
{
|
{
|
||||||
struct iproc_nand_soc_priv *priv = soc->priv;
|
struct iproc_nand_soc *priv =
|
||||||
|
container_of(soc, struct iproc_nand_soc, soc);
|
||||||
void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
|
void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
|
||||||
u32 val;
|
u32 val;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -72,7 +76,8 @@ static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||||
|
|
||||||
static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
|
static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
|
||||||
{
|
{
|
||||||
struct iproc_nand_soc_priv *priv = soc->priv;
|
struct iproc_nand_soc *priv =
|
||||||
|
container_of(soc, struct iproc_nand_soc, soc);
|
||||||
void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
|
void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
|
||||||
u32 val;
|
u32 val;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -94,17 +99,14 @@ static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
|
||||||
static int iproc_nand_probe(struct platform_device *pdev)
|
static int iproc_nand_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct iproc_nand_soc_priv *priv;
|
struct iproc_nand_soc *priv;
|
||||||
struct brcmnand_soc *soc;
|
struct brcmnand_soc *soc;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
||||||
soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
|
|
||||||
if (!soc)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
soc = &priv->soc;
|
||||||
|
|
||||||
spin_lock_init(&priv->idm_lock);
|
spin_lock_init(&priv->idm_lock);
|
||||||
|
|
||||||
|
@ -118,8 +120,6 @@ static int iproc_nand_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(priv->ext_base))
|
if (IS_ERR(priv->ext_base))
|
||||||
return PTR_ERR(priv->ext_base);
|
return PTR_ERR(priv->ext_base);
|
||||||
|
|
||||||
soc->pdev = pdev;
|
|
||||||
soc->priv = priv;
|
|
||||||
soc->ctlrdy_ack = iproc_nand_intc_ack;
|
soc->ctlrdy_ack = iproc_nand_intc_ack;
|
||||||
soc->ctlrdy_set_enabled = iproc_nand_intc_set;
|
soc->ctlrdy_set_enabled = iproc_nand_intc_set;
|
||||||
soc->prepare_data_bus = iproc_nand_apb_access;
|
soc->prepare_data_bus = iproc_nand_apb_access;
|
||||||
|
|
|
@ -516,7 +516,8 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
|
||||||
|
|
||||||
static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
|
static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
struct cafe_priv *cafe = mtd->priv;
|
struct cafe_priv *cafe = mtd->priv;
|
||||||
|
|
||||||
|
@ -604,7 +605,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
||||||
|
|
||||||
mtd->dev.parent = &pdev->dev;
|
mtd->dev.parent = &pdev->dev;
|
||||||
mtd->priv = cafe;
|
mtd->priv = cafe;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
|
|
||||||
cafe->pdev = pdev;
|
cafe->pdev = pdev;
|
||||||
cafe->mmio = pci_iomap(pdev, 0, 0);
|
cafe->mmio = pci_iomap(pdev, 0, 0);
|
||||||
|
|
|
@ -683,9 +683,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
|
||||||
info->vaddr = vaddr;
|
info->vaddr = vaddr;
|
||||||
|
|
||||||
info->mtd.priv = &info->chip;
|
info->mtd.priv = &info->chip;
|
||||||
info->mtd.name = dev_name(&pdev->dev);
|
|
||||||
info->mtd.owner = THIS_MODULE;
|
|
||||||
|
|
||||||
info->mtd.dev.parent = &pdev->dev;
|
info->mtd.dev.parent = &pdev->dev;
|
||||||
|
|
||||||
info->chip.IO_ADDR_R = vaddr;
|
info->chip.IO_ADDR_R = vaddr;
|
||||||
|
|
|
@ -458,8 +458,17 @@ static void find_valid_banks(struct denali_nand_info *denali)
|
||||||
static void detect_max_banks(struct denali_nand_info *denali)
|
static void detect_max_banks(struct denali_nand_info *denali)
|
||||||
{
|
{
|
||||||
uint32_t features = ioread32(denali->flash_reg + FEATURES);
|
uint32_t features = ioread32(denali->flash_reg + FEATURES);
|
||||||
|
/*
|
||||||
|
* Read the revision register, so we can calculate the max_banks
|
||||||
|
* properly: the encoding changed from rev 5.0 to 5.1
|
||||||
|
*/
|
||||||
|
u32 revision = MAKE_COMPARABLE_REVISION(
|
||||||
|
ioread32(denali->flash_reg + REVISION));
|
||||||
|
|
||||||
denali->max_banks = 2 << (features & FEATURES__N_BANKS);
|
if (revision < REVISION_5_1)
|
||||||
|
denali->max_banks = 2 << (features & FEATURES__N_BANKS);
|
||||||
|
else
|
||||||
|
denali->max_banks = 1 << (features & FEATURES__N_BANKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void detect_partition_feature(struct denali_nand_info *denali)
|
static void detect_partition_feature(struct denali_nand_info *denali)
|
||||||
|
@ -1105,7 +1114,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* by write_page above.
|
* by write_page above.
|
||||||
*/
|
*/
|
||||||
static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* for regular page writes, we let HW handle all the ECC
|
* for regular page writes, we let HW handle all the ECC
|
||||||
|
@ -1120,7 +1129,8 @@ static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* write_page() function above.
|
* write_page() function above.
|
||||||
*/
|
*/
|
||||||
static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* for raw page writes, we want to disable ECC and simply write
|
* for raw page writes, we want to disable ECC and simply write
|
||||||
|
@ -1304,7 +1314,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
||||||
*/
|
*/
|
||||||
addr = MODE_11 | BANK(denali->flash_bank);
|
addr = MODE_11 | BANK(denali->flash_bank);
|
||||||
index_addr(denali, addr | 0, 0x90);
|
index_addr(denali, addr | 0, 0x90);
|
||||||
index_addr(denali, addr | 1, 0);
|
index_addr(denali, addr | 1, col);
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
index_addr_read_data(denali, addr | 2, &id);
|
index_addr_read_data(denali, addr | 2, &id);
|
||||||
write_byte_to_buf(denali, id);
|
write_byte_to_buf(denali, id);
|
||||||
|
@ -1454,7 +1464,6 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
/* now that our ISR is registered, we can enable interrupts */
|
/* now that our ISR is registered, we can enable interrupts */
|
||||||
denali_set_intr_modes(denali, true);
|
denali_set_intr_modes(denali, true);
|
||||||
denali->mtd.name = "denali-nand";
|
denali->mtd.name = "denali-nand";
|
||||||
denali->mtd.owner = THIS_MODULE;
|
|
||||||
denali->mtd.priv = &denali->nand;
|
denali->mtd.priv = &denali->nand;
|
||||||
|
|
||||||
/* register the driver with the NAND core subsystem */
|
/* register the driver with the NAND core subsystem */
|
||||||
|
|
|
@ -178,6 +178,8 @@
|
||||||
|
|
||||||
#define REVISION 0x370
|
#define REVISION 0x370
|
||||||
#define REVISION__VALUE 0xffff
|
#define REVISION__VALUE 0xffff
|
||||||
|
#define MAKE_COMPARABLE_REVISION(x) swab16((x) & REVISION__VALUE)
|
||||||
|
#define REVISION_5_1 0x00000501
|
||||||
|
|
||||||
#define ONFI_DEVICE_FEATURES 0x380
|
#define ONFI_DEVICE_FEATURES 0x380
|
||||||
#define ONFI_DEVICE_FEATURES__VALUE 0x003f
|
#define ONFI_DEVICE_FEATURES__VALUE 0x003f
|
||||||
|
|
|
@ -977,13 +977,13 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
|
static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
return write_page(mtd, nand, buf, false);
|
return write_page(mtd, nand, buf, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
|
static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
return write_page(mtd, nand, buf, true);
|
return write_page(mtd, nand, buf, true);
|
||||||
}
|
}
|
||||||
|
@ -1113,7 +1113,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
|
||||||
/* write first page of block */
|
/* write first page of block */
|
||||||
write_page_prologue(mtd, g4_addr);
|
write_page_prologue(mtd, g4_addr);
|
||||||
docg4_write_page(mtd, nand, buf, 1);
|
docg4_write_page(mtd, nand, buf, 1, page);
|
||||||
ret = pageprog(mtd);
|
ret = pageprog(mtd);
|
||||||
|
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
|
@ -1316,7 +1316,7 @@ static int __init probe_docg4(struct platform_device *pdev)
|
||||||
doc = (struct docg4_priv *) (nand + 1);
|
doc = (struct docg4_priv *) (nand + 1);
|
||||||
mtd->priv = nand;
|
mtd->priv = nand;
|
||||||
nand->priv = doc;
|
nand->priv = doc;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = &pdev->dev;
|
||||||
doc->virtadr = virtadr;
|
doc->virtadr = virtadr;
|
||||||
doc->dev = dev;
|
doc->dev = dev;
|
||||||
|
|
||||||
|
|
|
@ -715,7 +715,7 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* waitfunc.
|
* waitfunc.
|
||||||
*/
|
*/
|
||||||
static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
||||||
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
@ -728,7 +728,7 @@ static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
*/
|
*/
|
||||||
static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
uint32_t offset, uint32_t data_len,
|
uint32_t offset, uint32_t data_len,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
||||||
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
@ -747,7 +747,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||||
|
|
||||||
/* Fill in fsl_elbc_mtd structure */
|
/* Fill in fsl_elbc_mtd structure */
|
||||||
priv->mtd.priv = chip;
|
priv->mtd.priv = chip;
|
||||||
priv->mtd.owner = THIS_MODULE;
|
priv->mtd.dev.parent = priv->dev;
|
||||||
|
|
||||||
/* set timeout to maximum */
|
/* set timeout to maximum */
|
||||||
priv->fmr = 15 << FMR_CWTO_SHIFT;
|
priv->fmr = 15 << FMR_CWTO_SHIFT;
|
||||||
|
@ -946,6 +946,7 @@ static const struct of_device_id fsl_elbc_nand_match[] = {
|
||||||
{ .compatible = "fsl,elbc-fcm-nand", },
|
{ .compatible = "fsl,elbc-fcm-nand", },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, fsl_elbc_nand_match);
|
||||||
|
|
||||||
static struct platform_driver fsl_elbc_nand_driver = {
|
static struct platform_driver fsl_elbc_nand_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
|
|
|
@ -772,7 +772,7 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* waitfunc.
|
* waitfunc.
|
||||||
*/
|
*/
|
||||||
static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
fsl_ifc_write_buf(mtd, buf, mtd->writesize);
|
fsl_ifc_write_buf(mtd, buf, mtd->writesize);
|
||||||
fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
@ -882,7 +882,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
|
||||||
|
|
||||||
/* Fill in fsl_ifc_mtd structure */
|
/* Fill in fsl_ifc_mtd structure */
|
||||||
priv->mtd.priv = chip;
|
priv->mtd.priv = chip;
|
||||||
priv->mtd.owner = THIS_MODULE;
|
priv->mtd.dev.parent = priv->dev;
|
||||||
|
|
||||||
/* fill in nand_chip structure */
|
/* fill in nand_chip structure */
|
||||||
/* set up function call table */
|
/* set up function call table */
|
||||||
|
@ -1163,6 +1163,7 @@ static const struct of_device_id fsl_ifc_nand_match[] = {
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match);
|
||||||
|
|
||||||
static struct platform_driver fsl_ifc_nand_driver = {
|
static struct platform_driver fsl_ifc_nand_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
|
|
|
@ -176,7 +176,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
|
||||||
fun->chip.dev_ready = fun_chip_ready;
|
fun->chip.dev_ready = fun_chip_ready;
|
||||||
|
|
||||||
fun->mtd.priv = &fun->chip;
|
fun->mtd.priv = &fun->chip;
|
||||||
fun->mtd.owner = THIS_MODULE;
|
fun->mtd.dev.parent = fun->dev;
|
||||||
|
|
||||||
flash_np = of_get_next_child(upm_np, NULL);
|
flash_np = of_get_next_child(upm_np, NULL);
|
||||||
if (!flash_np)
|
if (!flash_np)
|
||||||
|
|
|
@ -348,7 +348,7 @@ static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BUG();
|
dev_err(host->dev, "unsupported chip-select %d\n", chipnr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1017,18 +1017,23 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
mtd->priv = nand;
|
mtd->priv = nand;
|
||||||
nand->priv = host;
|
nand->priv = host;
|
||||||
|
|
||||||
host->mtd.owner = THIS_MODULE;
|
host->mtd.dev.parent = &pdev->dev;
|
||||||
nand->IO_ADDR_R = host->data_va;
|
nand->IO_ADDR_R = host->data_va;
|
||||||
nand->IO_ADDR_W = host->data_va;
|
nand->IO_ADDR_W = host->data_va;
|
||||||
nand->cmd_ctrl = fsmc_cmd_ctrl;
|
nand->cmd_ctrl = fsmc_cmd_ctrl;
|
||||||
nand->chip_delay = 30;
|
nand->chip_delay = 30;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup default ECC mode. nand_dt_init() called from nand_scan_ident()
|
||||||
|
* can overwrite this value if the DT provides a different value.
|
||||||
|
*/
|
||||||
nand->ecc.mode = NAND_ECC_HW;
|
nand->ecc.mode = NAND_ECC_HW;
|
||||||
nand->ecc.hwctl = fsmc_enable_hwecc;
|
nand->ecc.hwctl = fsmc_enable_hwecc;
|
||||||
nand->ecc.size = 512;
|
nand->ecc.size = 512;
|
||||||
nand->options = pdata->options;
|
nand->options = pdata->options;
|
||||||
nand->select_chip = fsmc_select_chip;
|
nand->select_chip = fsmc_select_chip;
|
||||||
nand->badblockbits = 7;
|
nand->badblockbits = 7;
|
||||||
|
nand->flash_node = np;
|
||||||
|
|
||||||
if (pdata->width == FSMC_NAND_BW16)
|
if (pdata->width == FSMC_NAND_BW16)
|
||||||
nand->options |= NAND_BUSWIDTH_16;
|
nand->options |= NAND_BUSWIDTH_16;
|
||||||
|
@ -1070,11 +1075,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
nand->ecc.correct = fsmc_bch8_correct_data;
|
nand->ecc.correct = fsmc_bch8_correct_data;
|
||||||
nand->ecc.bytes = 13;
|
nand->ecc.bytes = 13;
|
||||||
nand->ecc.strength = 8;
|
nand->ecc.strength = 8;
|
||||||
} else {
|
|
||||||
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
|
|
||||||
nand->ecc.correct = nand_correct_data;
|
|
||||||
nand->ecc.bytes = 3;
|
|
||||||
nand->ecc.strength = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1111,23 +1111,50 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
default:
|
default:
|
||||||
dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
|
dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
|
||||||
mtd->oobsize);
|
mtd->oobsize);
|
||||||
BUG();
|
ret = -EINVAL;
|
||||||
|
goto err_probe;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (host->mtd.oobsize) {
|
switch (nand->ecc.mode) {
|
||||||
case 16:
|
case NAND_ECC_HW:
|
||||||
nand->ecc.layout = &fsmc_ecc1_16_layout;
|
dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n");
|
||||||
|
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
|
||||||
|
nand->ecc.correct = nand_correct_data;
|
||||||
|
nand->ecc.bytes = 3;
|
||||||
|
nand->ecc.strength = 1;
|
||||||
break;
|
break;
|
||||||
case 64:
|
|
||||||
nand->ecc.layout = &fsmc_ecc1_64_layout;
|
case NAND_ECC_SOFT_BCH:
|
||||||
break;
|
dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
|
||||||
case 128:
|
|
||||||
nand->ecc.layout = &fsmc_ecc1_128_layout;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
|
dev_err(&pdev->dev, "Unsupported ECC mode!\n");
|
||||||
mtd->oobsize);
|
goto err_probe;
|
||||||
BUG();
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't set layout for BCH4 SW ECC. This will be
|
||||||
|
* generated later in nand_bch_init() later.
|
||||||
|
*/
|
||||||
|
if (nand->ecc.mode != NAND_ECC_SOFT_BCH) {
|
||||||
|
switch (host->mtd.oobsize) {
|
||||||
|
case 16:
|
||||||
|
nand->ecc.layout = &fsmc_ecc1_16_layout;
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
nand->ecc.layout = &fsmc_ecc1_64_layout;
|
||||||
|
break;
|
||||||
|
case 128:
|
||||||
|
nand->ecc.layout = &fsmc_ecc1_128_layout;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_warn(&pdev->dev,
|
||||||
|
"No oob scheme defined for oobsize %d\n",
|
||||||
|
mtd->oobsize);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto err_probe;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,7 +275,7 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
||||||
chip->cmd_ctrl = gpio_nand_cmd_ctrl;
|
chip->cmd_ctrl = gpio_nand_cmd_ctrl;
|
||||||
|
|
||||||
gpiomtd->mtd_info.priv = chip;
|
gpiomtd->mtd_info.priv = chip;
|
||||||
gpiomtd->mtd_info.owner = THIS_MODULE;
|
gpiomtd->mtd_info.dev.parent = &pdev->dev;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, gpiomtd);
|
platform_set_drvdata(pdev, gpiomtd);
|
||||||
|
|
||||||
|
|
|
@ -1160,7 +1160,7 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
struct gpmi_nand_data *this = chip->priv;
|
struct gpmi_nand_data *this = chip->priv;
|
||||||
struct bch_geometry *nfc_geo = &this->bch_geometry;
|
struct bch_geometry *nfc_geo = &this->bch_geometry;
|
||||||
|
@ -1446,7 +1446,7 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
|
||||||
static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
|
static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
int oob_required)
|
int oob_required, int page)
|
||||||
{
|
{
|
||||||
struct gpmi_nand_data *this = chip->priv;
|
struct gpmi_nand_data *this = chip->priv;
|
||||||
struct bch_geometry *nfc_geo = &this->bch_geometry;
|
struct bch_geometry *nfc_geo = &this->bch_geometry;
|
||||||
|
@ -1533,7 +1533,7 @@ static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
{
|
{
|
||||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
|
||||||
|
|
||||||
return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1);
|
return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
|
@ -1717,7 +1717,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
|
||||||
/* Write the first page of the current stride. */
|
/* Write the first page of the current stride. */
|
||||||
dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
|
dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
|
||||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
||||||
chip->ecc.write_page_raw(mtd, chip, buffer, 0);
|
chip->ecc.write_page_raw(mtd, chip, buffer, 0, page);
|
||||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||||
|
|
||||||
/* Wait for the write to finish. */
|
/* Wait for the write to finish. */
|
||||||
|
@ -1897,7 +1897,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
|
||||||
/* init the MTD data structures */
|
/* init the MTD data structures */
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
mtd->name = "gpmi-nand";
|
mtd->name = "gpmi-nand";
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = this->dev;
|
||||||
|
|
||||||
/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
|
/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
|
||||||
chip->priv = this;
|
chip->priv = this;
|
||||||
|
|
|
@ -590,7 +590,8 @@ static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hisi_nand_write_page_hwecc(struct mtd_info *mtd,
|
static int hisi_nand_write_page_hwecc(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
chip->write_buf(mtd, buf, mtd->writesize);
|
chip->write_buf(mtd, buf, mtd->writesize);
|
||||||
if (oob_required)
|
if (oob_required)
|
||||||
|
@ -737,7 +738,6 @@ static int hisi_nfc_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
mtd->name = "hisi_nand";
|
mtd->name = "hisi_nand";
|
||||||
mtd->dev.parent = &pdev->dev;
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
|
|
@ -434,7 +434,7 @@ static int jz_nand_probe(struct platform_device *pdev)
|
||||||
mtd = &nand->mtd;
|
mtd = &nand->mtd;
|
||||||
chip = &nand->chip;
|
chip = &nand->chip;
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = &pdev->dev;
|
||||||
mtd->name = "jz4740-nand";
|
mtd->name = "jz4740-nand";
|
||||||
|
|
||||||
chip->ecc.hwctl = jz_nand_hwctl;
|
chip->ecc.hwctl = jz_nand_hwctl;
|
||||||
|
|
|
@ -495,7 +495,8 @@ static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
|
||||||
static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,
|
static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
struct lpc32xx_nand_host *host = chip->priv;
|
struct lpc32xx_nand_host *host = chip->priv;
|
||||||
const uint8_t *oobbuf = chip->oob_poi;
|
const uint8_t *oobbuf = chip->oob_poi;
|
||||||
|
@ -682,7 +683,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
nand_chip->priv = host; /* link the private data structures */
|
nand_chip->priv = host; /* link the private data structures */
|
||||||
mtd->priv = nand_chip;
|
mtd->priv = nand_chip;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
mtd->dev.parent = &pdev->dev;
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
/* Get NAND clock */
|
/* Get NAND clock */
|
||||||
|
@ -692,7 +692,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
res = -ENOENT;
|
res = -ENOENT;
|
||||||
goto err_exit1;
|
goto err_exit1;
|
||||||
}
|
}
|
||||||
clk_enable(host->clk);
|
clk_prepare_enable(host->clk);
|
||||||
|
|
||||||
nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl;
|
nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl;
|
||||||
nand_chip->dev_ready = lpc32xx_nand_device_ready;
|
nand_chip->dev_ready = lpc32xx_nand_device_ready;
|
||||||
|
@ -800,7 +800,7 @@ err_exit3:
|
||||||
if (use_dma)
|
if (use_dma)
|
||||||
dma_release_channel(host->dma_chan);
|
dma_release_channel(host->dma_chan);
|
||||||
err_exit2:
|
err_exit2:
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
err_exit1:
|
err_exit1:
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
|
@ -822,7 +822,7 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
|
||||||
if (use_dma)
|
if (use_dma)
|
||||||
dma_release_channel(host->dma_chan);
|
dma_release_channel(host->dma_chan);
|
||||||
|
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
|
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
|
@ -837,7 +837,7 @@ static int lpc32xx_nand_resume(struct platform_device *pdev)
|
||||||
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
|
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
/* Re-enable NAND clock */
|
/* Re-enable NAND clock */
|
||||||
clk_enable(host->clk);
|
clk_prepare_enable(host->clk);
|
||||||
|
|
||||||
/* Fresh init of NAND controller */
|
/* Fresh init of NAND controller */
|
||||||
lpc32xx_nand_setup(host);
|
lpc32xx_nand_setup(host);
|
||||||
|
@ -856,7 +856,7 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
|
|
||||||
/* Disable clock */
|
/* Disable clock */
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,22 +94,25 @@
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* slc_tac register definitions
|
* slc_tac register definitions
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
/* Computation of clock cycles on basis of controller and device clock rates */
|
||||||
|
#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, DIV_ROUND_UP(c, n) - 1, 0xF) << s)
|
||||||
|
|
||||||
/* Clock setting for RDY write sample wait time in 2*n clocks */
|
/* Clock setting for RDY write sample wait time in 2*n clocks */
|
||||||
#define SLCTAC_WDR(n) (((n) & 0xF) << 28)
|
#define SLCTAC_WDR(n) (((n) & 0xF) << 28)
|
||||||
/* Write pulse width in clock cycles, 1 to 16 clocks */
|
/* Write pulse width in clock cycles, 1 to 16 clocks */
|
||||||
#define SLCTAC_WWIDTH(n) (((n) & 0xF) << 24)
|
#define SLCTAC_WWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 24))
|
||||||
/* Write hold time of control and data signals, 1 to 16 clocks */
|
/* Write hold time of control and data signals, 1 to 16 clocks */
|
||||||
#define SLCTAC_WHOLD(n) (((n) & 0xF) << 20)
|
#define SLCTAC_WHOLD(c, n) (SLCTAC_CLOCKS(c, n, 20))
|
||||||
/* Write setup time of control and data signals, 1 to 16 clocks */
|
/* Write setup time of control and data signals, 1 to 16 clocks */
|
||||||
#define SLCTAC_WSETUP(n) (((n) & 0xF) << 16)
|
#define SLCTAC_WSETUP(c, n) (SLCTAC_CLOCKS(c, n, 16))
|
||||||
/* Clock setting for RDY read sample wait time in 2*n clocks */
|
/* Clock setting for RDY read sample wait time in 2*n clocks */
|
||||||
#define SLCTAC_RDR(n) (((n) & 0xF) << 12)
|
#define SLCTAC_RDR(n) (((n) & 0xF) << 12)
|
||||||
/* Read pulse width in clock cycles, 1 to 16 clocks */
|
/* Read pulse width in clock cycles, 1 to 16 clocks */
|
||||||
#define SLCTAC_RWIDTH(n) (((n) & 0xF) << 8)
|
#define SLCTAC_RWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 8))
|
||||||
/* Read hold time of control and data signals, 1 to 16 clocks */
|
/* Read hold time of control and data signals, 1 to 16 clocks */
|
||||||
#define SLCTAC_RHOLD(n) (((n) & 0xF) << 4)
|
#define SLCTAC_RHOLD(c, n) (SLCTAC_CLOCKS(c, n, 4))
|
||||||
/* Read setup time of control and data signals, 1 to 16 clocks */
|
/* Read setup time of control and data signals, 1 to 16 clocks */
|
||||||
#define SLCTAC_RSETUP(n) (((n) & 0xF) << 0)
|
#define SLCTAC_RSETUP(c, n) (SLCTAC_CLOCKS(c, n, 0))
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* slc_ecc register definitions
|
* slc_ecc register definitions
|
||||||
|
@ -240,13 +243,13 @@ static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host)
|
||||||
|
|
||||||
/* Compute clock setup values */
|
/* Compute clock setup values */
|
||||||
tmp = SLCTAC_WDR(host->ncfg->wdr_clks) |
|
tmp = SLCTAC_WDR(host->ncfg->wdr_clks) |
|
||||||
SLCTAC_WWIDTH(1 + (clkrate / host->ncfg->wwidth)) |
|
SLCTAC_WWIDTH(clkrate, host->ncfg->wwidth) |
|
||||||
SLCTAC_WHOLD(1 + (clkrate / host->ncfg->whold)) |
|
SLCTAC_WHOLD(clkrate, host->ncfg->whold) |
|
||||||
SLCTAC_WSETUP(1 + (clkrate / host->ncfg->wsetup)) |
|
SLCTAC_WSETUP(clkrate, host->ncfg->wsetup) |
|
||||||
SLCTAC_RDR(host->ncfg->rdr_clks) |
|
SLCTAC_RDR(host->ncfg->rdr_clks) |
|
||||||
SLCTAC_RWIDTH(1 + (clkrate / host->ncfg->rwidth)) |
|
SLCTAC_RWIDTH(clkrate, host->ncfg->rwidth) |
|
||||||
SLCTAC_RHOLD(1 + (clkrate / host->ncfg->rhold)) |
|
SLCTAC_RHOLD(clkrate, host->ncfg->rhold) |
|
||||||
SLCTAC_RSETUP(1 + (clkrate / host->ncfg->rsetup));
|
SLCTAC_RSETUP(clkrate, host->ncfg->rsetup);
|
||||||
writel(tmp, SLC_TAC(host->io_base));
|
writel(tmp, SLC_TAC(host->io_base));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,7 +663,8 @@ static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd,
|
||||||
*/
|
*/
|
||||||
static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
|
static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf,
|
||||||
|
int oob_required, int page)
|
||||||
{
|
{
|
||||||
struct lpc32xx_nand_host *host = chip->priv;
|
struct lpc32xx_nand_host *host = chip->priv;
|
||||||
uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0];
|
uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0];
|
||||||
|
@ -689,7 +693,7 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
|
||||||
static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
int oob_required)
|
int oob_required, int page)
|
||||||
{
|
{
|
||||||
/* Raw writes can just use the FIFO interface */
|
/* Raw writes can just use the FIFO interface */
|
||||||
chip->write_buf(mtd, buf, chip->ecc.size * chip->ecc.steps);
|
chip->write_buf(mtd, buf, chip->ecc.size * chip->ecc.steps);
|
||||||
|
@ -810,7 +814,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
res = -ENOENT;
|
res = -ENOENT;
|
||||||
goto err_exit1;
|
goto err_exit1;
|
||||||
}
|
}
|
||||||
clk_enable(host->clk);
|
clk_prepare_enable(host->clk);
|
||||||
|
|
||||||
/* Set NAND IO addresses and command/ready functions */
|
/* Set NAND IO addresses and command/ready functions */
|
||||||
chip->IO_ADDR_R = SLC_DATA(host->io_base);
|
chip->IO_ADDR_R = SLC_DATA(host->io_base);
|
||||||
|
@ -915,7 +919,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
err_exit3:
|
err_exit3:
|
||||||
dma_release_channel(host->dma_chan);
|
dma_release_channel(host->dma_chan);
|
||||||
err_exit2:
|
err_exit2:
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
err_exit1:
|
err_exit1:
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
|
|
||||||
|
@ -939,7 +943,7 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
|
||||||
tmp &= ~SLCCFG_CE_LOW;
|
tmp &= ~SLCCFG_CE_LOW;
|
||||||
writel(tmp, SLC_CTRL(host->io_base));
|
writel(tmp, SLC_CTRL(host->io_base));
|
||||||
|
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -951,7 +955,7 @@ static int lpc32xx_nand_resume(struct platform_device *pdev)
|
||||||
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
|
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
/* Re-enable NAND clock */
|
/* Re-enable NAND clock */
|
||||||
clk_enable(host->clk);
|
clk_prepare_enable(host->clk);
|
||||||
|
|
||||||
/* Fresh init of NAND controller */
|
/* Fresh init of NAND controller */
|
||||||
lpc32xx_nand_setup(host);
|
lpc32xx_nand_setup(host);
|
||||||
|
@ -976,7 +980,7 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
|
||||||
lpc32xx_wp_enable(host);
|
lpc32xx_wp_enable(host);
|
||||||
|
|
||||||
/* Disable clock */
|
/* Disable clock */
|
||||||
clk_disable(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -659,6 +659,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||||
chip = &prv->chip;
|
chip = &prv->chip;
|
||||||
|
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
|
mtd->dev.parent = dev;
|
||||||
chip->priv = prv;
|
chip->priv = prv;
|
||||||
prv->dev = dev;
|
prv->dev = dev;
|
||||||
|
|
||||||
|
@ -841,6 +842,7 @@ static const struct of_device_id mpc5121_nfc_match[] = {
|
||||||
{ .compatible = "fsl,mpc5121-nfc", },
|
{ .compatible = "fsl,mpc5121-nfc", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, mpc5121_nfc_match);
|
||||||
|
|
||||||
static struct platform_driver mpc5121_nfc_driver = {
|
static struct platform_driver mpc5121_nfc_driver = {
|
||||||
.probe = mpc5121_nfc_probe,
|
.probe = mpc5121_nfc_probe,
|
||||||
|
|
|
@ -1458,6 +1458,7 @@ static const struct of_device_id mxcnd_dt_ids[] = {
|
||||||
},
|
},
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
|
||||||
|
|
||||||
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||||
{
|
{
|
||||||
|
@ -1516,7 +1517,6 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||||
this = &host->nand;
|
this = &host->nand;
|
||||||
mtd = &host->mtd;
|
mtd = &host->mtd;
|
||||||
mtd->priv = this;
|
mtd->priv = this;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
mtd->dev.parent = &pdev->dev;
|
mtd->dev.parent = &pdev->dev;
|
||||||
mtd->name = DRIVER_NAME;
|
mtd->name = DRIVER_NAME;
|
||||||
|
|
||||||
|
|
|
@ -543,23 +543,32 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for the ready pin, after a command. The timeout is caught later. */
|
/**
|
||||||
|
* nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
*
|
||||||
|
* Wait for the ready pin after a command, and warn if a timeout occurs.
|
||||||
|
*/
|
||||||
void nand_wait_ready(struct mtd_info *mtd)
|
void nand_wait_ready(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
unsigned long timeo = jiffies + msecs_to_jiffies(20);
|
unsigned long timeo = 400;
|
||||||
|
|
||||||
/* 400ms timeout */
|
|
||||||
if (in_interrupt() || oops_in_progress)
|
if (in_interrupt() || oops_in_progress)
|
||||||
return panic_nand_wait_ready(mtd, 400);
|
return panic_nand_wait_ready(mtd, timeo);
|
||||||
|
|
||||||
led_trigger_event(nand_led_trigger, LED_FULL);
|
led_trigger_event(nand_led_trigger, LED_FULL);
|
||||||
/* Wait until command is processed or timeout occurs */
|
/* Wait until command is processed or timeout occurs */
|
||||||
|
timeo = jiffies + msecs_to_jiffies(timeo);
|
||||||
do {
|
do {
|
||||||
if (chip->dev_ready(mtd))
|
if (chip->dev_ready(mtd))
|
||||||
break;
|
goto out;
|
||||||
touch_softlockup_watchdog();
|
cond_resched();
|
||||||
} while (time_before(jiffies, timeo));
|
} while (time_before(jiffies, timeo));
|
||||||
|
|
||||||
|
pr_warn_ratelimited(
|
||||||
|
"timeout while waiting for chip to become ready\n");
|
||||||
|
out:
|
||||||
led_trigger_event(nand_led_trigger, LED_OFF);
|
led_trigger_event(nand_led_trigger, LED_OFF);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nand_wait_ready);
|
EXPORT_SYMBOL_GPL(nand_wait_ready);
|
||||||
|
@ -885,15 +894,13 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
* @chip: NAND chip structure
|
* @chip: NAND chip structure
|
||||||
*
|
*
|
||||||
* Wait for command done. This applies to erase and program only. Erase can
|
* Wait for command done. This applies to erase and program only.
|
||||||
* take up to 400ms and program up to 20ms according to general NAND and
|
|
||||||
* SmartMedia specs.
|
|
||||||
*/
|
*/
|
||||||
static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
{
|
{
|
||||||
|
|
||||||
int status, state = chip->state;
|
int status;
|
||||||
unsigned long timeo = (state == FL_ERASING ? 400 : 20);
|
unsigned long timeo = 400;
|
||||||
|
|
||||||
led_trigger_event(nand_led_trigger, LED_FULL);
|
led_trigger_event(nand_led_trigger, LED_FULL);
|
||||||
|
|
||||||
|
@ -909,7 +916,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
panic_nand_wait(mtd, chip, timeo);
|
panic_nand_wait(mtd, chip, timeo);
|
||||||
else {
|
else {
|
||||||
timeo = jiffies + msecs_to_jiffies(timeo);
|
timeo = jiffies + msecs_to_jiffies(timeo);
|
||||||
while (time_before(jiffies, timeo)) {
|
do {
|
||||||
if (chip->dev_ready) {
|
if (chip->dev_ready) {
|
||||||
if (chip->dev_ready(mtd))
|
if (chip->dev_ready(mtd))
|
||||||
break;
|
break;
|
||||||
|
@ -918,7 +925,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cond_resched();
|
cond_resched();
|
||||||
}
|
} while (time_before(jiffies, timeo));
|
||||||
}
|
}
|
||||||
led_trigger_event(nand_led_trigger, LED_OFF);
|
led_trigger_event(nand_led_trigger, LED_OFF);
|
||||||
|
|
||||||
|
@ -1100,6 +1107,134 @@ out:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(nand_lock);
|
EXPORT_SYMBOL(nand_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
|
||||||
|
* @buf: buffer to test
|
||||||
|
* @len: buffer length
|
||||||
|
* @bitflips_threshold: maximum number of bitflips
|
||||||
|
*
|
||||||
|
* Check if a buffer contains only 0xff, which means the underlying region
|
||||||
|
* has been erased and is ready to be programmed.
|
||||||
|
* The bitflips_threshold specify the maximum number of bitflips before
|
||||||
|
* considering the region is not erased.
|
||||||
|
* Note: The logic of this function has been extracted from the memweight
|
||||||
|
* implementation, except that nand_check_erased_buf function exit before
|
||||||
|
* testing the whole buffer if the number of bitflips exceed the
|
||||||
|
* bitflips_threshold value.
|
||||||
|
*
|
||||||
|
* Returns a positive number of bitflips less than or equal to
|
||||||
|
* bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
|
||||||
|
* threshold.
|
||||||
|
*/
|
||||||
|
static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
|
||||||
|
{
|
||||||
|
const unsigned char *bitmap = buf;
|
||||||
|
int bitflips = 0;
|
||||||
|
int weight;
|
||||||
|
|
||||||
|
for (; len && ((uintptr_t)bitmap) % sizeof(long);
|
||||||
|
len--, bitmap++) {
|
||||||
|
weight = hweight8(*bitmap);
|
||||||
|
bitflips += BITS_PER_BYTE - weight;
|
||||||
|
if (unlikely(bitflips > bitflips_threshold))
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; len >= sizeof(long);
|
||||||
|
len -= sizeof(long), bitmap += sizeof(long)) {
|
||||||
|
weight = hweight_long(*((unsigned long *)bitmap));
|
||||||
|
bitflips += BITS_PER_LONG - weight;
|
||||||
|
if (unlikely(bitflips > bitflips_threshold))
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; len > 0; len--, bitmap++) {
|
||||||
|
weight = hweight8(*bitmap);
|
||||||
|
bitflips += BITS_PER_BYTE - weight;
|
||||||
|
if (unlikely(bitflips > bitflips_threshold))
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitflips;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
|
||||||
|
* 0xff data
|
||||||
|
* @data: data buffer to test
|
||||||
|
* @datalen: data length
|
||||||
|
* @ecc: ECC buffer
|
||||||
|
* @ecclen: ECC length
|
||||||
|
* @extraoob: extra OOB buffer
|
||||||
|
* @extraooblen: extra OOB length
|
||||||
|
* @bitflips_threshold: maximum number of bitflips
|
||||||
|
*
|
||||||
|
* Check if a data buffer and its associated ECC and OOB data contains only
|
||||||
|
* 0xff pattern, which means the underlying region has been erased and is
|
||||||
|
* ready to be programmed.
|
||||||
|
* The bitflips_threshold specify the maximum number of bitflips before
|
||||||
|
* considering the region as not erased.
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* 1/ ECC algorithms are working on pre-defined block sizes which are usually
|
||||||
|
* different from the NAND page size. When fixing bitflips, ECC engines will
|
||||||
|
* report the number of errors per chunk, and the NAND core infrastructure
|
||||||
|
* expect you to return the maximum number of bitflips for the whole page.
|
||||||
|
* This is why you should always use this function on a single chunk and
|
||||||
|
* not on the whole page. After checking each chunk you should update your
|
||||||
|
* max_bitflips value accordingly.
|
||||||
|
* 2/ When checking for bitflips in erased pages you should not only check
|
||||||
|
* the payload data but also their associated ECC data, because a user might
|
||||||
|
* have programmed almost all bits to 1 but a few. In this case, we
|
||||||
|
* shouldn't consider the chunk as erased, and checking ECC bytes prevent
|
||||||
|
* this case.
|
||||||
|
* 3/ The extraoob argument is optional, and should be used if some of your OOB
|
||||||
|
* data are protected by the ECC engine.
|
||||||
|
* It could also be used if you support subpages and want to attach some
|
||||||
|
* extra OOB data to an ECC chunk.
|
||||||
|
*
|
||||||
|
* Returns a positive number of bitflips less than or equal to
|
||||||
|
* bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
|
||||||
|
* threshold. In case of success, the passed buffers are filled with 0xff.
|
||||||
|
*/
|
||||||
|
int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||||
|
void *ecc, int ecclen,
|
||||||
|
void *extraoob, int extraooblen,
|
||||||
|
int bitflips_threshold)
|
||||||
|
{
|
||||||
|
int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
|
||||||
|
|
||||||
|
data_bitflips = nand_check_erased_buf(data, datalen,
|
||||||
|
bitflips_threshold);
|
||||||
|
if (data_bitflips < 0)
|
||||||
|
return data_bitflips;
|
||||||
|
|
||||||
|
bitflips_threshold -= data_bitflips;
|
||||||
|
|
||||||
|
ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
|
||||||
|
if (ecc_bitflips < 0)
|
||||||
|
return ecc_bitflips;
|
||||||
|
|
||||||
|
bitflips_threshold -= ecc_bitflips;
|
||||||
|
|
||||||
|
extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
|
||||||
|
bitflips_threshold);
|
||||||
|
if (extraoob_bitflips < 0)
|
||||||
|
return extraoob_bitflips;
|
||||||
|
|
||||||
|
if (data_bitflips)
|
||||||
|
memset(data, 0xff, datalen);
|
||||||
|
|
||||||
|
if (ecc_bitflips)
|
||||||
|
memset(ecc, 0xff, ecclen);
|
||||||
|
|
||||||
|
if (extraoob_bitflips)
|
||||||
|
memset(extraoob, 0xff, extraooblen);
|
||||||
|
|
||||||
|
return data_bitflips + ecc_bitflips + extraoob_bitflips;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_read_page_raw - [INTERN] read raw page data without ecc
|
* nand_read_page_raw - [INTERN] read raw page data without ecc
|
||||||
* @mtd: mtd info structure
|
* @mtd: mtd info structure
|
||||||
|
@ -2027,11 +2162,12 @@ out:
|
||||||
* @chip: nand chip info structure
|
* @chip: nand chip info structure
|
||||||
* @buf: data buffer
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
|
* @page: page number to write
|
||||||
*
|
*
|
||||||
* Not for syndrome calculating ECC controllers, which use a special oob layout.
|
* Not for syndrome calculating ECC controllers, which use a special oob layout.
|
||||||
*/
|
*/
|
||||||
static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
chip->write_buf(mtd, buf, mtd->writesize);
|
chip->write_buf(mtd, buf, mtd->writesize);
|
||||||
if (oob_required)
|
if (oob_required)
|
||||||
|
@ -2046,12 +2182,14 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* @chip: nand chip info structure
|
* @chip: nand chip info structure
|
||||||
* @buf: data buffer
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
|
* @page: page number to write
|
||||||
*
|
*
|
||||||
* We need a special oob layout and handling even when ECC isn't checked.
|
* We need a special oob layout and handling even when ECC isn't checked.
|
||||||
*/
|
*/
|
||||||
static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
int eccsize = chip->ecc.size;
|
int eccsize = chip->ecc.size;
|
||||||
int eccbytes = chip->ecc.bytes;
|
int eccbytes = chip->ecc.bytes;
|
||||||
|
@ -2088,9 +2226,11 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
||||||
* @chip: nand chip info structure
|
* @chip: nand chip info structure
|
||||||
* @buf: data buffer
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
|
* @page: page number to write
|
||||||
*/
|
*/
|
||||||
static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
int i, eccsize = chip->ecc.size;
|
int i, eccsize = chip->ecc.size;
|
||||||
int eccbytes = chip->ecc.bytes;
|
int eccbytes = chip->ecc.bytes;
|
||||||
|
@ -2106,7 +2246,7 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
for (i = 0; i < chip->ecc.total; i++)
|
for (i = 0; i < chip->ecc.total; i++)
|
||||||
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
||||||
|
|
||||||
return chip->ecc.write_page_raw(mtd, chip, buf, 1);
|
return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2115,9 +2255,11 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* @chip: nand chip info structure
|
* @chip: nand chip info structure
|
||||||
* @buf: data buffer
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
|
* @page: page number to write
|
||||||
*/
|
*/
|
||||||
static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
int i, eccsize = chip->ecc.size;
|
int i, eccsize = chip->ecc.size;
|
||||||
int eccbytes = chip->ecc.bytes;
|
int eccbytes = chip->ecc.bytes;
|
||||||
|
@ -2149,11 +2291,12 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
* @data_len: data length
|
* @data_len: data length
|
||||||
* @buf: data buffer
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
|
* @page: page number to write
|
||||||
*/
|
*/
|
||||||
static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, uint32_t offset,
|
struct nand_chip *chip, uint32_t offset,
|
||||||
uint32_t data_len, const uint8_t *buf,
|
uint32_t data_len, const uint8_t *buf,
|
||||||
int oob_required)
|
int oob_required, int page)
|
||||||
{
|
{
|
||||||
uint8_t *oob_buf = chip->oob_poi;
|
uint8_t *oob_buf = chip->oob_poi;
|
||||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||||
|
@ -2208,13 +2351,15 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
||||||
* @chip: nand chip info structure
|
* @chip: nand chip info structure
|
||||||
* @buf: data buffer
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
|
* @page: page number to write
|
||||||
*
|
*
|
||||||
* The hw generator calculates the error syndrome automatically. Therefore we
|
* The hw generator calculates the error syndrome automatically. Therefore we
|
||||||
* need a special oob layout and handling.
|
* need a special oob layout and handling.
|
||||||
*/
|
*/
|
||||||
static int nand_write_page_syndrome(struct mtd_info *mtd,
|
static int nand_write_page_syndrome(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
int i, eccsize = chip->ecc.size;
|
int i, eccsize = chip->ecc.size;
|
||||||
int eccbytes = chip->ecc.bytes;
|
int eccbytes = chip->ecc.bytes;
|
||||||
|
@ -2278,12 +2423,13 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
|
||||||
if (unlikely(raw))
|
if (unlikely(raw))
|
||||||
status = chip->ecc.write_page_raw(mtd, chip, buf,
|
status = chip->ecc.write_page_raw(mtd, chip, buf,
|
||||||
oob_required);
|
oob_required, page);
|
||||||
else if (subpage)
|
else if (subpage)
|
||||||
status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
|
status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
|
||||||
buf, oob_required);
|
buf, oob_required, page);
|
||||||
else
|
else
|
||||||
status = chip->ecc.write_page(mtd, chip, buf, oob_required);
|
status = chip->ecc.write_page(mtd, chip, buf, oob_required,
|
||||||
|
page);
|
||||||
|
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
|
@ -3708,10 +3854,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||||
|
|
||||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||||
|
|
||||||
if (!type->pagesize && chip->init_size) {
|
if (!type->pagesize) {
|
||||||
/* Set the pagesize, oobsize, erasesize by the driver */
|
|
||||||
busw = chip->init_size(mtd, chip, id_data);
|
|
||||||
} else if (!type->pagesize) {
|
|
||||||
/* Decode parameters from extended ID */
|
/* Decode parameters from extended ID */
|
||||||
nand_decode_ext_id(mtd, chip, id_data, &busw);
|
nand_decode_ext_id(mtd, chip, id_data, &busw);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3846,8 +3989,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||||
struct nand_flash_dev *type;
|
struct nand_flash_dev *type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (chip->dn) {
|
if (chip->flash_node) {
|
||||||
ret = nand_dt_init(mtd, chip, chip->dn);
|
ret = nand_dt_init(mtd, chip, chip->flash_node);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1080,7 +1080,7 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
struct nand_bbt_descr *td = this->bbt_td;
|
struct nand_bbt_descr *td = this->bbt_td;
|
||||||
struct nand_bbt_descr *md = this->bbt_md;
|
struct nand_bbt_descr *md = this->bbt_md;
|
||||||
|
|
||||||
len = mtd->size >> (this->bbt_erase_shift + 2);
|
len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1;
|
||||||
/*
|
/*
|
||||||
* Allocate memory (2bit per block) and clear the memory bad block
|
* Allocate memory (2bit per block) and clear the memory bad block
|
||||||
* table.
|
* table.
|
||||||
|
|
|
@ -649,8 +649,7 @@ static void free_device(struct nandsim *ns)
|
||||||
kmem_cache_free(ns->nand_pages_slab,
|
kmem_cache_free(ns->nand_pages_slab,
|
||||||
ns->pages[i].byte);
|
ns->pages[i].byte);
|
||||||
}
|
}
|
||||||
if (ns->nand_pages_slab)
|
kmem_cache_destroy(ns->nand_pages_slab);
|
||||||
kmem_cache_destroy(ns->nand_pages_slab);
|
|
||||||
vfree(ns->pages);
|
vfree(ns->pages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
|
||||||
chip->priv = ndfc;
|
chip->priv = ndfc;
|
||||||
|
|
||||||
ndfc->mtd.priv = chip;
|
ndfc->mtd.priv = chip;
|
||||||
ndfc->mtd.owner = THIS_MODULE;
|
ndfc->mtd.dev.parent = &ndfc->ofdev->dev;
|
||||||
|
|
||||||
flash_np = of_get_next_child(node, NULL);
|
flash_np = of_get_next_child(node, NULL);
|
||||||
if (!flash_np)
|
if (!flash_np)
|
||||||
|
|
|
@ -250,7 +250,7 @@ static int nuc900_nand_probe(struct platform_device *pdev)
|
||||||
chip = &(nuc900_nand->chip);
|
chip = &(nuc900_nand->chip);
|
||||||
|
|
||||||
nuc900_nand->mtd.priv = chip;
|
nuc900_nand->mtd.priv = chip;
|
||||||
nuc900_nand->mtd.owner = THIS_MODULE;
|
nuc900_nand->mtd.dev.parent = &pdev->dev;
|
||||||
spin_lock_init(&nuc900_nand->lock);
|
spin_lock_init(&nuc900_nand->lock);
|
||||||
|
|
||||||
nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL);
|
nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
|
|
@ -1500,11 +1500,12 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
||||||
* @chip: nand chip info structure
|
* @chip: nand chip info structure
|
||||||
* @buf: data buffer
|
* @buf: data buffer
|
||||||
* @oob_required: must write chip->oob_poi to OOB
|
* @oob_required: must write chip->oob_poi to OOB
|
||||||
|
* @page: page
|
||||||
*
|
*
|
||||||
* Custom write page method evolved to support multi sector writing in one shot
|
* Custom write page method evolved to support multi sector writing in one shot
|
||||||
*/
|
*/
|
||||||
static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
|
static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required, int page)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||||
|
@ -1684,8 +1685,7 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||||
info->ecc_opt = pdata->ecc_opt;
|
info->ecc_opt = pdata->ecc_opt;
|
||||||
mtd = &info->mtd;
|
mtd = &info->mtd;
|
||||||
mtd->priv = &info->nand;
|
mtd->priv = &info->nand;
|
||||||
mtd->name = dev_name(&pdev->dev);
|
mtd->dev.parent = &pdev->dev;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
nand_chip = &info->nand;
|
nand_chip = &info->nand;
|
||||||
nand_chip->ecc.priv = NULL;
|
nand_chip->ecc.priv = NULL;
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
mtd->priv = nc;
|
mtd->priv = nc;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
nc->priv = board;
|
nc->priv = board;
|
||||||
nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
|
nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
|
||||||
|
@ -201,6 +201,7 @@ static const struct of_device_id orion_nand_of_match_table[] = {
|
||||||
{ .compatible = "marvell,orion-nand", },
|
{ .compatible = "marvell,orion-nand", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, orion_nand_of_match_table);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct platform_driver orion_nand_driver = {
|
static struct platform_driver orion_nand_driver = {
|
||||||
|
|
|
@ -124,7 +124,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
|
||||||
|
|
||||||
/* Link the private data with the MTD structure */
|
/* Link the private data with the MTD structure */
|
||||||
pasemi_nand_mtd->priv = chip;
|
pasemi_nand_mtd->priv = chip;
|
||||||
pasemi_nand_mtd->owner = THIS_MODULE;
|
pasemi_nand_mtd->dev.parent = &ofdev->dev;
|
||||||
|
|
||||||
chip->IO_ADDR_R = of_iomap(np, 0);
|
chip->IO_ADDR_R = of_iomap(np, 0);
|
||||||
chip->IO_ADDR_W = chip->IO_ADDR_R;
|
chip->IO_ADDR_W = chip->IO_ADDR_R;
|
||||||
|
|
|
@ -59,8 +59,7 @@ static int plat_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
data->chip.priv = &data;
|
data->chip.priv = &data;
|
||||||
data->mtd.priv = &data->chip;
|
data->mtd.priv = &data->chip;
|
||||||
data->mtd.owner = THIS_MODULE;
|
data->mtd.dev.parent = &pdev->dev;
|
||||||
data->mtd.name = dev_name(&pdev->dev);
|
|
||||||
|
|
||||||
data->chip.IO_ADDR_R = data->io_base;
|
data->chip.IO_ADDR_R = data->io_base;
|
||||||
data->chip.IO_ADDR_W = data->io_base;
|
data->chip.IO_ADDR_W = data->io_base;
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dma/pxa-dma.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
|
@ -33,10 +35,6 @@
|
||||||
#define ARCH_HAS_DMA
|
#define ARCH_HAS_DMA
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARCH_HAS_DMA
|
|
||||||
#include <mach/dma.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
||||||
|
|
||||||
#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200)
|
#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200)
|
||||||
|
@ -78,7 +76,8 @@
|
||||||
#define NDCR_ND_MODE (0x3 << 21)
|
#define NDCR_ND_MODE (0x3 << 21)
|
||||||
#define NDCR_NAND_MODE (0x0)
|
#define NDCR_NAND_MODE (0x0)
|
||||||
#define NDCR_CLR_PG_CNT (0x1 << 20)
|
#define NDCR_CLR_PG_CNT (0x1 << 20)
|
||||||
#define NDCR_STOP_ON_UNCOR (0x1 << 19)
|
#define NFCV1_NDCR_ARB_CNTL (0x1 << 19)
|
||||||
|
#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19)
|
||||||
#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
|
#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
|
||||||
#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
|
#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
|
||||||
|
|
||||||
|
@ -201,6 +200,10 @@ struct pxa3xx_nand_info {
|
||||||
unsigned int oob_buff_pos;
|
unsigned int oob_buff_pos;
|
||||||
|
|
||||||
/* DMA information */
|
/* DMA information */
|
||||||
|
struct scatterlist sg;
|
||||||
|
enum dma_data_direction dma_dir;
|
||||||
|
struct dma_chan *dma_chan;
|
||||||
|
dma_cookie_t dma_cookie;
|
||||||
int drcmr_dat;
|
int drcmr_dat;
|
||||||
int drcmr_cmd;
|
int drcmr_cmd;
|
||||||
|
|
||||||
|
@ -208,8 +211,6 @@ struct pxa3xx_nand_info {
|
||||||
unsigned char *oob_buff;
|
unsigned char *oob_buff;
|
||||||
dma_addr_t data_buff_phys;
|
dma_addr_t data_buff_phys;
|
||||||
int data_dma_ch;
|
int data_dma_ch;
|
||||||
struct pxa_dma_desc *data_desc;
|
|
||||||
dma_addr_t data_desc_addr;
|
|
||||||
|
|
||||||
struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
|
struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
|
||||||
unsigned int state;
|
unsigned int state;
|
||||||
|
@ -252,6 +253,25 @@ static bool use_dma = 1;
|
||||||
module_param(use_dma, bool, 0444);
|
module_param(use_dma, bool, 0444);
|
||||||
MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
|
MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
|
||||||
|
|
||||||
|
struct pxa3xx_nand_timing {
|
||||||
|
unsigned int tCH; /* Enable signal hold time */
|
||||||
|
unsigned int tCS; /* Enable signal setup time */
|
||||||
|
unsigned int tWH; /* ND_nWE high duration */
|
||||||
|
unsigned int tWP; /* ND_nWE pulse time */
|
||||||
|
unsigned int tRH; /* ND_nRE high duration */
|
||||||
|
unsigned int tRP; /* ND_nRE pulse width */
|
||||||
|
unsigned int tR; /* ND_nWE high to ND_nRE low for read */
|
||||||
|
unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */
|
||||||
|
unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pxa3xx_nand_flash {
|
||||||
|
uint32_t chip_id;
|
||||||
|
unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
|
||||||
|
unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
|
||||||
|
struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
|
||||||
|
};
|
||||||
|
|
||||||
static struct pxa3xx_nand_timing timing[] = {
|
static struct pxa3xx_nand_timing timing[] = {
|
||||||
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
||||||
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
||||||
|
@ -260,15 +280,14 @@ static struct pxa3xx_nand_timing timing[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
||||||
{ "DEFAULT FLASH", 0, 0, 2048, 8, 8, 0, &timing[0] },
|
{ 0x46ec, 16, 16, &timing[1] },
|
||||||
{ "64MiB 16-bit", 0x46ec, 32, 512, 16, 16, 4096, &timing[1] },
|
{ 0xdaec, 8, 8, &timing[1] },
|
||||||
{ "256MiB 8-bit", 0xdaec, 64, 2048, 8, 8, 2048, &timing[1] },
|
{ 0xd7ec, 8, 8, &timing[1] },
|
||||||
{ "4GiB 8-bit", 0xd7ec, 128, 4096, 8, 8, 8192, &timing[1] },
|
{ 0xa12c, 8, 8, &timing[2] },
|
||||||
{ "128MiB 8-bit", 0xa12c, 64, 2048, 8, 8, 1024, &timing[2] },
|
{ 0xb12c, 16, 16, &timing[2] },
|
||||||
{ "128MiB 16-bit", 0xb12c, 64, 2048, 16, 16, 1024, &timing[2] },
|
{ 0xdc2c, 8, 8, &timing[2] },
|
||||||
{ "512MiB 8-bit", 0xdc2c, 64, 2048, 8, 8, 4096, &timing[2] },
|
{ 0xcc2c, 16, 16, &timing[2] },
|
||||||
{ "512MiB 16-bit", 0xcc2c, 64, 2048, 16, 16, 4096, &timing[2] },
|
{ 0xba20, 16, 16, &timing[3] },
|
||||||
{ "256MiB 16-bit", 0xba20, 64, 2048, 16, 16, 2048, &timing[3] },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
|
static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
|
||||||
|
@ -329,9 +348,6 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
|
||||||
.oobfree = { }
|
.oobfree = { }
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Define a default flash type setting serve as flash detecting only */
|
|
||||||
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
|
|
||||||
|
|
||||||
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
||||||
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
||||||
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
||||||
|
@ -393,6 +409,128 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
|
||||||
nand_writel(info, NDTR1CS0, ndtr1);
|
nand_writel(info, NDTR1CS0, ndtr1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host,
|
||||||
|
const struct nand_sdr_timings *t)
|
||||||
|
{
|
||||||
|
struct pxa3xx_nand_info *info = host->info_data;
|
||||||
|
struct nand_chip *chip = &host->chip;
|
||||||
|
unsigned long nand_clk = clk_get_rate(info->clk);
|
||||||
|
uint32_t ndtr0, ndtr1;
|
||||||
|
|
||||||
|
u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000);
|
||||||
|
u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000);
|
||||||
|
u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000);
|
||||||
|
u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000);
|
||||||
|
u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000);
|
||||||
|
u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000);
|
||||||
|
u32 tR = chip->chip_delay * 1000;
|
||||||
|
u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000);
|
||||||
|
u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);
|
||||||
|
|
||||||
|
/* fallback to a default value if tR = 0 */
|
||||||
|
if (!tR)
|
||||||
|
tR = 20000;
|
||||||
|
|
||||||
|
ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) |
|
||||||
|
NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) |
|
||||||
|
NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) |
|
||||||
|
NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) |
|
||||||
|
NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) |
|
||||||
|
NDTR0_tRP(ns2cycle(tRP_min, nand_clk));
|
||||||
|
|
||||||
|
ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) |
|
||||||
|
NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) |
|
||||||
|
NDTR1_tAR(ns2cycle(tAR_min, nand_clk));
|
||||||
|
|
||||||
|
info->ndtr0cs0 = ndtr0;
|
||||||
|
info->ndtr1cs0 = ndtr1;
|
||||||
|
nand_writel(info, NDTR0CS0, ndtr0);
|
||||||
|
nand_writel(info, NDTR1CS0, ndtr1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host,
|
||||||
|
unsigned int *flash_width,
|
||||||
|
unsigned int *dfc_width)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = &host->chip;
|
||||||
|
struct pxa3xx_nand_info *info = host->info_data;
|
||||||
|
const struct pxa3xx_nand_flash *f = NULL;
|
||||||
|
int i, id, ntypes;
|
||||||
|
|
||||||
|
ntypes = ARRAY_SIZE(builtin_flash_types);
|
||||||
|
|
||||||
|
chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1);
|
||||||
|
|
||||||
|
id = chip->read_byte(host->mtd);
|
||||||
|
id |= chip->read_byte(host->mtd) << 0x8;
|
||||||
|
|
||||||
|
for (i = 0; i < ntypes; i++) {
|
||||||
|
f = &builtin_flash_types[i];
|
||||||
|
|
||||||
|
if (f->chip_id == id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == ntypes) {
|
||||||
|
dev_err(&info->pdev->dev, "Error: timings not found\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxa3xx_nand_set_timing(host, f->timing);
|
||||||
|
|
||||||
|
*flash_width = f->flash_width;
|
||||||
|
*dfc_width = f->dfc_width;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host,
|
||||||
|
int mode)
|
||||||
|
{
|
||||||
|
const struct nand_sdr_timings *timings;
|
||||||
|
|
||||||
|
mode = fls(mode) - 1;
|
||||||
|
if (mode < 0)
|
||||||
|
mode = 0;
|
||||||
|
|
||||||
|
timings = onfi_async_timing_mode_to_sdr_timings(mode);
|
||||||
|
if (IS_ERR(timings))
|
||||||
|
return PTR_ERR(timings);
|
||||||
|
|
||||||
|
pxa3xx_nand_set_sdr_timing(host, timings);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pxa3xx_nand_init(struct pxa3xx_nand_host *host)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = &host->chip;
|
||||||
|
struct pxa3xx_nand_info *info = host->info_data;
|
||||||
|
unsigned int flash_width = 0, dfc_width = 0;
|
||||||
|
int mode, err;
|
||||||
|
|
||||||
|
mode = onfi_get_async_timing_mode(chip);
|
||||||
|
if (mode == ONFI_TIMING_MODE_UNKNOWN) {
|
||||||
|
err = pxa3xx_nand_init_timings_compat(host, &flash_width,
|
||||||
|
&dfc_width);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (flash_width == 16) {
|
||||||
|
info->reg_ndcr |= NDCR_DWIDTH_M;
|
||||||
|
chip->options |= NAND_BUSWIDTH_16;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0;
|
||||||
|
} else {
|
||||||
|
err = pxa3xx_nand_init_timings_onfi(host, mode);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the data and OOB size, depending on the selected
|
* Set the data and OOB size, depending on the selected
|
||||||
* spare and ECC configuration.
|
* spare and ECC configuration.
|
||||||
|
@ -468,6 +606,9 @@ static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
|
||||||
ndcr &= ~NDCR_ND_RUN;
|
ndcr &= ~NDCR_ND_RUN;
|
||||||
nand_writel(info, NDCR, ndcr);
|
nand_writel(info, NDCR, ndcr);
|
||||||
}
|
}
|
||||||
|
if (info->dma_chan)
|
||||||
|
dmaengine_terminate_all(info->dma_chan);
|
||||||
|
|
||||||
/* clear status bits */
|
/* clear status bits */
|
||||||
nand_writel(info, NDSR, NDSR_MASK);
|
nand_writel(info, NDSR, NDSR_MASK);
|
||||||
}
|
}
|
||||||
|
@ -504,7 +645,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
||||||
* the polling on the last read.
|
* the polling on the last read.
|
||||||
*/
|
*/
|
||||||
while (len > 8) {
|
while (len > 8) {
|
||||||
readsl(info->mmio_base + NDDB, data, 8);
|
ioread32_rep(info->mmio_base + NDDB, data, 8);
|
||||||
|
|
||||||
ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
|
ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
|
||||||
val & NDSR_RDDREQ, 1000, 5000);
|
val & NDSR_RDDREQ, 1000, 5000);
|
||||||
|
@ -519,7 +660,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readsl(info->mmio_base + NDDB, data, len);
|
ioread32_rep(info->mmio_base + NDDB, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_data_pio(struct pxa3xx_nand_info *info)
|
static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||||
|
@ -559,57 +700,61 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||||
info->data_size -= do_bytes;
|
info->data_size -= do_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARCH_HAS_DMA
|
static void pxa3xx_nand_data_dma_irq(void *data)
|
||||||
|
{
|
||||||
|
struct pxa3xx_nand_info *info = data;
|
||||||
|
struct dma_tx_state state;
|
||||||
|
enum dma_status status;
|
||||||
|
|
||||||
|
status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state);
|
||||||
|
if (likely(status == DMA_COMPLETE)) {
|
||||||
|
info->state = STATE_DMA_DONE;
|
||||||
|
} else {
|
||||||
|
dev_err(&info->pdev->dev, "DMA error on data channel\n");
|
||||||
|
info->retcode = ERR_DMABUSERR;
|
||||||
|
}
|
||||||
|
dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
|
||||||
|
|
||||||
|
nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
|
||||||
|
enable_int(info, NDCR_INT_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
static void start_data_dma(struct pxa3xx_nand_info *info)
|
static void start_data_dma(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct pxa_dma_desc *desc = info->data_desc;
|
enum dma_transfer_direction direction;
|
||||||
int dma_len = ALIGN(info->data_size + info->oob_size, 32);
|
struct dma_async_tx_descriptor *tx;
|
||||||
|
|
||||||
desc->ddadr = DDADR_STOP;
|
|
||||||
desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
|
|
||||||
|
|
||||||
switch (info->state) {
|
switch (info->state) {
|
||||||
case STATE_DMA_WRITING:
|
case STATE_DMA_WRITING:
|
||||||
desc->dsadr = info->data_buff_phys;
|
info->dma_dir = DMA_TO_DEVICE;
|
||||||
desc->dtadr = info->mmio_phys + NDDB;
|
direction = DMA_MEM_TO_DEV;
|
||||||
desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
|
|
||||||
break;
|
break;
|
||||||
case STATE_DMA_READING:
|
case STATE_DMA_READING:
|
||||||
desc->dtadr = info->data_buff_phys;
|
info->dma_dir = DMA_FROM_DEVICE;
|
||||||
desc->dsadr = info->mmio_phys + NDDB;
|
direction = DMA_DEV_TO_MEM;
|
||||||
desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
|
dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
|
||||||
info->state);
|
info->state);
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
|
info->sg.length = info->data_size +
|
||||||
|
(info->oob_size ? info->spare_size + info->ecc_size : 0);
|
||||||
|
dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
|
||||||
|
|
||||||
DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
|
tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction,
|
||||||
DDADR(info->data_dma_ch) = info->data_desc_addr;
|
DMA_PREP_INTERRUPT);
|
||||||
DCSR(info->data_dma_ch) |= DCSR_RUN;
|
if (!tx) {
|
||||||
}
|
dev_err(&info->pdev->dev, "prep_slave_sg() failed\n");
|
||||||
|
return;
|
||||||
static void pxa3xx_nand_data_dma_irq(int channel, void *data)
|
|
||||||
{
|
|
||||||
struct pxa3xx_nand_info *info = data;
|
|
||||||
uint32_t dcsr;
|
|
||||||
|
|
||||||
dcsr = DCSR(channel);
|
|
||||||
DCSR(channel) = dcsr;
|
|
||||||
|
|
||||||
if (dcsr & DCSR_BUSERR) {
|
|
||||||
info->retcode = ERR_DMABUSERR;
|
|
||||||
}
|
}
|
||||||
|
tx->callback = pxa3xx_nand_data_dma_irq;
|
||||||
info->state = STATE_DMA_DONE;
|
tx->callback_param = info;
|
||||||
enable_int(info, NDCR_INT_MASK);
|
info->dma_cookie = dmaengine_submit(tx);
|
||||||
nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
|
dma_async_issue_pending(info->dma_chan);
|
||||||
|
dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n",
|
||||||
|
__func__, direction, info->dma_cookie, info->sg.length);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
static void start_data_dma(struct pxa3xx_nand_info *info)
|
|
||||||
{}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data)
|
static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data)
|
||||||
{
|
{
|
||||||
|
@ -1128,7 +1273,8 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
|
static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
chip->write_buf(mtd, buf, mtd->writesize);
|
chip->write_buf(mtd, buf, mtd->writesize);
|
||||||
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
@ -1241,45 +1387,23 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
|
||||||
return NAND_STATUS_READY;
|
return NAND_STATUS_READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
|
static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info)
|
||||||
const struct pxa3xx_nand_flash *f)
|
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = info->pdev;
|
struct platform_device *pdev = info->pdev;
|
||||||
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct pxa3xx_nand_host *host = info->host[info->cs];
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
||||||
uint32_t ndcr = 0x0; /* enable all interrupts */
|
struct mtd_info *mtd = host->mtd;
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
|
||||||
if (f->page_size != 2048 && f->page_size != 512) {
|
/* configure default flash values */
|
||||||
dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
|
info->reg_ndcr = 0x0; /* enable all interrupts */
|
||||||
return -EINVAL;
|
info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
||||||
}
|
info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
|
||||||
|
info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
||||||
|
info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
|
||||||
|
info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0;
|
||||||
|
info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
|
||||||
|
|
||||||
if (f->flash_width != 16 && f->flash_width != 8) {
|
|
||||||
dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate addressing information */
|
|
||||||
host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
|
|
||||||
|
|
||||||
if (f->num_blocks * f->page_per_block > 65536)
|
|
||||||
host->row_addr_cycles = 3;
|
|
||||||
else
|
|
||||||
host->row_addr_cycles = 2;
|
|
||||||
|
|
||||||
ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
||||||
ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
|
|
||||||
ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0;
|
|
||||||
ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0;
|
|
||||||
ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0;
|
|
||||||
ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0;
|
|
||||||
|
|
||||||
ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
|
|
||||||
ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
|
||||||
|
|
||||||
info->reg_ndcr = ndcr;
|
|
||||||
|
|
||||||
pxa3xx_nand_set_timing(host, f->timing);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1289,42 +1413,57 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
||||||
|
|
||||||
/* Set an initial chunk size */
|
/* Set an initial chunk size */
|
||||||
info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
|
info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
|
||||||
info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
|
info->reg_ndcr = ndcr &
|
||||||
|
~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL);
|
||||||
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
||||||
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARCH_HAS_DMA
|
|
||||||
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = info->pdev;
|
struct platform_device *pdev = info->pdev;
|
||||||
int data_desc_offset = info->buf_size - sizeof(struct pxa_dma_desc);
|
struct dma_slave_config config;
|
||||||
|
dma_cap_mask_t mask;
|
||||||
|
struct pxad_param param;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (use_dma == 0) {
|
info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
|
||||||
info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
|
if (info->data_buff == NULL)
|
||||||
if (info->data_buff == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
info->data_buff = dma_alloc_coherent(&pdev->dev, info->buf_size,
|
|
||||||
&info->data_buff_phys, GFP_KERNEL);
|
|
||||||
if (info->data_buff == NULL) {
|
|
||||||
dev_err(&pdev->dev, "failed to allocate dma buffer\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
if (use_dma == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
sg_init_one(&info->sg, info->data_buff, info->buf_size);
|
||||||
|
dma_cap_zero(mask);
|
||||||
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
param.prio = PXAD_PRIO_LOWEST;
|
||||||
|
param.drcmr = info->drcmr_dat;
|
||||||
|
info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn,
|
||||||
|
¶m, &pdev->dev,
|
||||||
|
"data");
|
||||||
|
if (!info->dma_chan) {
|
||||||
|
dev_err(&pdev->dev, "unable to request data dma channel\n");
|
||||||
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->data_desc = (void *)info->data_buff + data_desc_offset;
|
memset(&config, 0, sizeof(config));
|
||||||
info->data_desc_addr = info->data_buff_phys + data_desc_offset;
|
config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||||
|
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||||
info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
|
config.src_addr = info->mmio_phys + NDDB;
|
||||||
pxa3xx_nand_data_dma_irq, info);
|
config.dst_addr = info->mmio_phys + NDDB;
|
||||||
if (info->data_dma_ch < 0) {
|
config.src_maxburst = 32;
|
||||||
dev_err(&pdev->dev, "failed to request data dma\n");
|
config.dst_maxburst = 32;
|
||||||
dma_free_coherent(&pdev->dev, info->buf_size,
|
ret = dmaengine_slave_config(info->dma_chan, &config);
|
||||||
info->data_buff, info->data_buff_phys);
|
if (ret < 0) {
|
||||||
return info->data_dma_ch;
|
dev_err(&info->pdev->dev,
|
||||||
|
"dma channel configuration failed: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1337,43 +1476,30 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
||||||
|
|
||||||
static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
|
static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = info->pdev;
|
|
||||||
if (info->use_dma) {
|
if (info->use_dma) {
|
||||||
pxa_free_dma(info->data_dma_ch);
|
dmaengine_terminate_all(info->dma_chan);
|
||||||
dma_free_coherent(&pdev->dev, info->buf_size,
|
dma_release_channel(info->dma_chan);
|
||||||
info->data_buff, info->data_buff_phys);
|
|
||||||
} else {
|
|
||||||
kfree(info->data_buff);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#else
|
|
||||||
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
|
||||||
{
|
|
||||||
info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
|
|
||||||
if (info->data_buff == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
|
|
||||||
{
|
|
||||||
kfree(info->data_buff);
|
kfree(info->data_buff);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
|
static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host)
|
||||||
{
|
{
|
||||||
|
struct pxa3xx_nand_info *info = host->info_data;
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
struct nand_chip *chip;
|
struct nand_chip *chip;
|
||||||
|
const struct nand_sdr_timings *timings;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mtd = info->host[info->cs]->mtd;
|
mtd = info->host[info->cs]->mtd;
|
||||||
chip = mtd->priv;
|
chip = mtd->priv;
|
||||||
|
|
||||||
/* use the common timing to make a try */
|
/* use the common timing to make a try */
|
||||||
ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
|
timings = onfi_async_timing_mode_to_sdr_timings(0);
|
||||||
if (ret)
|
if (IS_ERR(timings))
|
||||||
return ret;
|
return PTR_ERR(timings);
|
||||||
|
|
||||||
|
pxa3xx_nand_set_sdr_timing(host, timings);
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
|
chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
|
||||||
ret = chip->waitfunc(mtd, chip);
|
ret = chip->waitfunc(mtd, chip);
|
||||||
|
@ -1458,12 +1584,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
||||||
struct pxa3xx_nand_info *info = host->info_data;
|
struct pxa3xx_nand_info *info = host->info_data;
|
||||||
struct platform_device *pdev = info->pdev;
|
struct platform_device *pdev = info->pdev;
|
||||||
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
|
|
||||||
const struct pxa3xx_nand_flash *f = NULL;
|
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
uint32_t id = -1;
|
int ret;
|
||||||
uint64_t chipsize;
|
|
||||||
int i, ret, num;
|
|
||||||
uint16_t ecc_strength, ecc_step;
|
uint16_t ecc_strength, ecc_step;
|
||||||
|
|
||||||
if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
|
if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
|
||||||
|
@ -1472,7 +1594,11 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
||||||
/* Set a default chunk size */
|
/* Set a default chunk size */
|
||||||
info->chunk_size = 512;
|
info->chunk_size = 512;
|
||||||
|
|
||||||
ret = pxa3xx_nand_sensing(info);
|
ret = pxa3xx_nand_config_flash(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pxa3xx_nand_sensing(host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
|
dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
|
||||||
info->cs);
|
info->cs);
|
||||||
|
@ -1480,54 +1606,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
|
|
||||||
id = *((uint16_t *)(info->data_buff));
|
|
||||||
if (id != 0)
|
|
||||||
dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
|
|
||||||
else {
|
|
||||||
dev_warn(&info->pdev->dev,
|
|
||||||
"Read out ID 0, potential timing set wrong!!\n");
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
num = ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1;
|
|
||||||
for (i = 0; i < num; i++) {
|
|
||||||
if (i < pdata->num_flash)
|
|
||||||
f = pdata->flash + i;
|
|
||||||
else
|
|
||||||
f = &builtin_flash_types[i - pdata->num_flash + 1];
|
|
||||||
|
|
||||||
/* find the chip in default list */
|
|
||||||
if (f->chip_id == id)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
|
|
||||||
dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = pxa3xx_nand_config_flash(info, f);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(pxa3xx_flash_ids, 0, sizeof(pxa3xx_flash_ids));
|
|
||||||
|
|
||||||
pxa3xx_flash_ids[0].name = f->name;
|
|
||||||
pxa3xx_flash_ids[0].dev_id = (f->chip_id >> 8) & 0xffff;
|
|
||||||
pxa3xx_flash_ids[0].pagesize = f->page_size;
|
|
||||||
chipsize = (uint64_t)f->num_blocks * f->page_per_block * f->page_size;
|
|
||||||
pxa3xx_flash_ids[0].chipsize = chipsize >> 20;
|
|
||||||
pxa3xx_flash_ids[0].erasesize = f->page_size * f->page_per_block;
|
|
||||||
if (f->flash_width == 16)
|
|
||||||
pxa3xx_flash_ids[0].options = NAND_BUSWIDTH_16;
|
|
||||||
pxa3xx_flash_ids[1].name = NULL;
|
|
||||||
def = pxa3xx_flash_ids;
|
|
||||||
KEEP_CONFIG:
|
KEEP_CONFIG:
|
||||||
|
info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
||||||
if (info->reg_ndcr & NDCR_DWIDTH_M)
|
if (info->reg_ndcr & NDCR_DWIDTH_M)
|
||||||
chip->options |= NAND_BUSWIDTH_16;
|
chip->options |= NAND_BUSWIDTH_16;
|
||||||
|
|
||||||
|
@ -1535,9 +1615,18 @@ KEEP_CONFIG:
|
||||||
if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
|
if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
|
||||||
nand_writel(info, NDECCCTRL, 0x0);
|
nand_writel(info, NDECCCTRL, 0x0);
|
||||||
|
|
||||||
if (nand_scan_ident(mtd, 1, def))
|
if (nand_scan_ident(mtd, 1, NULL))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (!pdata->keep_config) {
|
||||||
|
ret = pxa3xx_nand_init(host);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&info->pdev->dev, "Failed to init nand: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pdata->flash_bbt) {
|
if (pdata->flash_bbt) {
|
||||||
/*
|
/*
|
||||||
* We'll use a bad block table stored in-flash and don't
|
* We'll use a bad block table stored in-flash and don't
|
||||||
|
@ -1635,7 +1724,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
host->cs = cs;
|
host->cs = cs;
|
||||||
host->info_data = info;
|
host->info_data = info;
|
||||||
mtd->priv = host;
|
mtd->priv = host;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
|
chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
|
||||||
chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
|
chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
|
||||||
|
@ -1662,34 +1751,23 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (use_dma) {
|
if (use_dma) {
|
||||||
/*
|
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||||
* This is a dirty hack to make this driver work from
|
if (r == NULL) {
|
||||||
* devicetree bindings. It can be removed once we have
|
dev_err(&pdev->dev,
|
||||||
* a prober DMA controller framework for DT.
|
"no resource defined for data DMA\n");
|
||||||
*/
|
ret = -ENXIO;
|
||||||
if (pdev->dev.of_node &&
|
goto fail_disable_clk;
|
||||||
of_machine_is_compatible("marvell,pxa3xx")) {
|
|
||||||
info->drcmr_dat = 97;
|
|
||||||
info->drcmr_cmd = 99;
|
|
||||||
} else {
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
|
||||||
if (r == NULL) {
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"no resource defined for data DMA\n");
|
|
||||||
ret = -ENXIO;
|
|
||||||
goto fail_disable_clk;
|
|
||||||
}
|
|
||||||
info->drcmr_dat = r->start;
|
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
|
||||||
if (r == NULL) {
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"no resource defined for cmd DMA\n");
|
|
||||||
ret = -ENXIO;
|
|
||||||
goto fail_disable_clk;
|
|
||||||
}
|
|
||||||
info->drcmr_cmd = r->start;
|
|
||||||
}
|
}
|
||||||
|
info->drcmr_dat = r->start;
|
||||||
|
|
||||||
|
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||||
|
if (r == NULL) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"no resource defined for cmd DMA\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto fail_disable_clk;
|
||||||
|
}
|
||||||
|
info->drcmr_cmd = r->start;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
@ -1754,6 +1832,16 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
|
||||||
free_irq(irq, info);
|
free_irq(irq, info);
|
||||||
pxa3xx_nand_free_buff(info);
|
pxa3xx_nand_free_buff(info);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In the pxa3xx case, the DFI bus is shared between the SMC and NFC.
|
||||||
|
* In order to prevent a lockup of the system bus, the DFI bus
|
||||||
|
* arbitration is granted to SMC upon driver removal. This is done by
|
||||||
|
* setting the x_ARB_CNTL bit, which also prevents the NAND to have
|
||||||
|
* access to the bus anymore.
|
||||||
|
*/
|
||||||
|
nand_writel(info, NDCR,
|
||||||
|
(nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) |
|
||||||
|
NFCV1_NDCR_ARB_CNTL);
|
||||||
clk_disable_unprepare(info->clk);
|
clk_disable_unprepare(info->clk);
|
||||||
|
|
||||||
for (cs = 0; cs < pdata->num_cs; cs++)
|
for (cs = 0; cs < pdata->num_cs; cs++)
|
||||||
|
@ -1800,15 +1888,16 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||||
struct pxa3xx_nand_platform_data *pdata;
|
struct pxa3xx_nand_platform_data *pdata;
|
||||||
struct mtd_part_parser_data ppdata = {};
|
struct mtd_part_parser_data ppdata = {};
|
||||||
struct pxa3xx_nand_info *info;
|
struct pxa3xx_nand_info *info;
|
||||||
int ret, cs, probe_success;
|
int ret, cs, probe_success, dma_available;
|
||||||
|
|
||||||
#ifndef ARCH_HAS_DMA
|
dma_available = IS_ENABLED(CONFIG_ARM) &&
|
||||||
if (use_dma) {
|
(IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP));
|
||||||
|
if (use_dma && !dma_available) {
|
||||||
use_dma = 0;
|
use_dma = 0;
|
||||||
dev_warn(&pdev->dev,
|
dev_warn(&pdev->dev,
|
||||||
"This platform can't do DMA on this device\n");
|
"This platform can't do DMA on this device\n");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
ret = pxa3xx_nand_probe_dt(pdev);
|
ret = pxa3xx_nand_probe_dt(pdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1861,35 +1950,22 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
|
static int pxa3xx_nand_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
|
struct pxa3xx_nand_info *info = dev_get_drvdata(dev);
|
||||||
struct pxa3xx_nand_platform_data *pdata;
|
|
||||||
struct mtd_info *mtd;
|
|
||||||
int cs;
|
|
||||||
|
|
||||||
pdata = dev_get_platdata(&pdev->dev);
|
|
||||||
if (info->state) {
|
if (info->state) {
|
||||||
dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
|
dev_err(dev, "driver busy, state = %d\n", info->state);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (cs = 0; cs < pdata->num_cs; cs++) {
|
|
||||||
mtd = info->host[cs]->mtd;
|
|
||||||
mtd_suspend(mtd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa3xx_nand_resume(struct platform_device *pdev)
|
static int pxa3xx_nand_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
|
struct pxa3xx_nand_info *info = dev_get_drvdata(dev);
|
||||||
struct pxa3xx_nand_platform_data *pdata;
|
|
||||||
struct mtd_info *mtd;
|
|
||||||
int cs;
|
|
||||||
|
|
||||||
pdata = dev_get_platdata(&pdev->dev);
|
|
||||||
/* We don't want to handle interrupt without calling mtd routine */
|
/* We don't want to handle interrupt without calling mtd routine */
|
||||||
disable_int(info, NDCR_INT_MASK);
|
disable_int(info, NDCR_INT_MASK);
|
||||||
|
|
||||||
|
@ -1907,10 +1983,6 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
|
||||||
* all status before resume
|
* all status before resume
|
||||||
*/
|
*/
|
||||||
nand_writel(info, NDSR, NDSR_MASK);
|
nand_writel(info, NDSR, NDSR_MASK);
|
||||||
for (cs = 0; cs < pdata->num_cs; cs++) {
|
|
||||||
mtd = info->host[cs]->mtd;
|
|
||||||
mtd_resume(mtd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1919,15 +1991,19 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
|
||||||
#define pxa3xx_nand_resume NULL
|
#define pxa3xx_nand_resume NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops pxa3xx_nand_pm_ops = {
|
||||||
|
.suspend = pxa3xx_nand_suspend,
|
||||||
|
.resume = pxa3xx_nand_resume,
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver pxa3xx_nand_driver = {
|
static struct platform_driver pxa3xx_nand_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "pxa3xx-nand",
|
.name = "pxa3xx-nand",
|
||||||
.of_match_table = pxa3xx_nand_dt_ids,
|
.of_match_table = pxa3xx_nand_dt_ids,
|
||||||
|
.pm = &pxa3xx_nand_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = pxa3xx_nand_probe,
|
.probe = pxa3xx_nand_probe,
|
||||||
.remove = pxa3xx_nand_remove,
|
.remove = pxa3xx_nand_remove,
|
||||||
.suspend = pxa3xx_nand_suspend,
|
|
||||||
.resume = pxa3xx_nand_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(pxa3xx_nand_driver);
|
module_platform_driver(pxa3xx_nand_driver);
|
||||||
|
|
|
@ -641,7 +641,6 @@ static int r852_register_nand_device(struct r852_device *dev)
|
||||||
|
|
||||||
WARN_ON(dev->card_registred);
|
WARN_ON(dev->card_registred);
|
||||||
|
|
||||||
dev->mtd->owner = THIS_MODULE;
|
|
||||||
dev->mtd->priv = dev->chip;
|
dev->mtd->priv = dev->chip;
|
||||||
dev->mtd->dev.parent = &dev->pci_dev->dev;
|
dev->mtd->dev.parent = &dev->pci_dev->dev;
|
||||||
|
|
||||||
|
|
|
@ -832,7 +832,6 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||||
|
|
||||||
nmtd->info = info;
|
nmtd->info = info;
|
||||||
nmtd->mtd.priv = chip;
|
nmtd->mtd.priv = chip;
|
||||||
nmtd->mtd.owner = THIS_MODULE;
|
|
||||||
nmtd->set = set;
|
nmtd->set = set;
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
|
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
|
||||||
|
@ -1016,6 +1015,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
pr_debug("initialising set %d (%p, info %p)\n",
|
pr_debug("initialising set %d (%p, info %p)\n",
|
||||||
setno, nmtd, info);
|
setno, nmtd, info);
|
||||||
|
|
||||||
|
nmtd->mtd.dev.parent = &pdev->dev;
|
||||||
s3c2410_nand_init_chip(info, nmtd, sets);
|
s3c2410_nand_init_chip(info, nmtd, sets);
|
||||||
|
|
||||||
nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
|
nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
|
||||||
|
|
|
@ -569,7 +569,8 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
chip->write_buf(mtd, buf, mtd->writesize);
|
chip->write_buf(mtd, buf, mtd->writesize);
|
||||||
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
@ -1123,6 +1124,7 @@ static int flctl_probe(struct platform_device *pdev)
|
||||||
flctl_mtd = &flctl->mtd;
|
flctl_mtd = &flctl->mtd;
|
||||||
nand = &flctl->chip;
|
nand = &flctl->chip;
|
||||||
flctl_mtd->priv = nand;
|
flctl_mtd->priv = nand;
|
||||||
|
flctl_mtd->dev.parent = &pdev->dev;
|
||||||
flctl->pdev = pdev;
|
flctl->pdev = pdev;
|
||||||
flctl->hwecc = pdata->has_hwecc;
|
flctl->hwecc = pdata->has_hwecc;
|
||||||
flctl->holden = pdata->use_holden;
|
flctl->holden = pdata->use_holden;
|
||||||
|
|
|
@ -144,7 +144,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* Link the private data with the MTD structure */
|
/* Link the private data with the MTD structure */
|
||||||
sharpsl->mtd.priv = this;
|
sharpsl->mtd.priv = this;
|
||||||
sharpsl->mtd.owner = THIS_MODULE;
|
sharpsl->mtd.dev.parent = &pdev->dev;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, sharpsl);
|
platform_set_drvdata(pdev, sharpsl);
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,6 @@ static int socrates_nand_probe(struct platform_device *ofdev)
|
||||||
nand_chip->priv = host; /* link the private data structures */
|
nand_chip->priv = host; /* link the private data structures */
|
||||||
mtd->priv = nand_chip;
|
mtd->priv = nand_chip;
|
||||||
mtd->name = "socrates_nand";
|
mtd->name = "socrates_nand";
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
mtd->dev.parent = &ofdev->dev;
|
mtd->dev.parent = &ofdev->dev;
|
||||||
ppdata.of_node = ofdev->dev.of_node;
|
ppdata.of_node = ofdev->dev.of_node;
|
||||||
|
|
||||||
|
|
|
@ -57,11 +57,8 @@
|
||||||
#define NFC_REG_ECC_CTL 0x0034
|
#define NFC_REG_ECC_CTL 0x0034
|
||||||
#define NFC_REG_ECC_ST 0x0038
|
#define NFC_REG_ECC_ST 0x0038
|
||||||
#define NFC_REG_DEBUG 0x003C
|
#define NFC_REG_DEBUG 0x003C
|
||||||
#define NFC_REG_ECC_CNT0 0x0040
|
#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
|
||||||
#define NFC_REG_ECC_CNT1 0x0044
|
#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
|
||||||
#define NFC_REG_ECC_CNT2 0x0048
|
|
||||||
#define NFC_REG_ECC_CNT3 0x004c
|
|
||||||
#define NFC_REG_USER_DATA_BASE 0x0050
|
|
||||||
#define NFC_REG_SPARE_AREA 0x00A0
|
#define NFC_REG_SPARE_AREA 0x00A0
|
||||||
#define NFC_RAM0_BASE 0x0400
|
#define NFC_RAM0_BASE 0x0400
|
||||||
#define NFC_RAM1_BASE 0x0800
|
#define NFC_RAM1_BASE 0x0800
|
||||||
|
@ -69,12 +66,16 @@
|
||||||
/* define bit use in NFC_CTL */
|
/* define bit use in NFC_CTL */
|
||||||
#define NFC_EN BIT(0)
|
#define NFC_EN BIT(0)
|
||||||
#define NFC_RESET BIT(1)
|
#define NFC_RESET BIT(1)
|
||||||
#define NFC_BUS_WIDYH BIT(2)
|
#define NFC_BUS_WIDTH_MSK BIT(2)
|
||||||
#define NFC_RB_SEL BIT(3)
|
#define NFC_BUS_WIDTH_8 (0 << 2)
|
||||||
#define NFC_CE_SEL GENMASK(26, 24)
|
#define NFC_BUS_WIDTH_16 (1 << 2)
|
||||||
|
#define NFC_RB_SEL_MSK BIT(3)
|
||||||
|
#define NFC_RB_SEL(x) ((x) << 3)
|
||||||
|
#define NFC_CE_SEL_MSK GENMASK(26, 24)
|
||||||
|
#define NFC_CE_SEL(x) ((x) << 24)
|
||||||
#define NFC_CE_CTL BIT(6)
|
#define NFC_CE_CTL BIT(6)
|
||||||
#define NFC_CE_CTL1 BIT(7)
|
#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8)
|
||||||
#define NFC_PAGE_SIZE GENMASK(11, 8)
|
#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8)
|
||||||
#define NFC_SAM BIT(12)
|
#define NFC_SAM BIT(12)
|
||||||
#define NFC_RAM_METHOD BIT(14)
|
#define NFC_RAM_METHOD BIT(14)
|
||||||
#define NFC_DEBUG_CTL BIT(31)
|
#define NFC_DEBUG_CTL BIT(31)
|
||||||
|
@ -86,10 +87,7 @@
|
||||||
#define NFC_CMD_FIFO_STATUS BIT(3)
|
#define NFC_CMD_FIFO_STATUS BIT(3)
|
||||||
#define NFC_STA BIT(4)
|
#define NFC_STA BIT(4)
|
||||||
#define NFC_NATCH_INT_FLAG BIT(5)
|
#define NFC_NATCH_INT_FLAG BIT(5)
|
||||||
#define NFC_RB_STATE0 BIT(8)
|
#define NFC_RB_STATE(x) BIT(x + 8)
|
||||||
#define NFC_RB_STATE1 BIT(9)
|
|
||||||
#define NFC_RB_STATE2 BIT(10)
|
|
||||||
#define NFC_RB_STATE3 BIT(11)
|
|
||||||
|
|
||||||
/* define bit use in NFC_INT */
|
/* define bit use in NFC_INT */
|
||||||
#define NFC_B2R_INT_ENABLE BIT(0)
|
#define NFC_B2R_INT_ENABLE BIT(0)
|
||||||
|
@ -109,9 +107,11 @@
|
||||||
(((tCAD) & 0x7) << 8))
|
(((tCAD) & 0x7) << 8))
|
||||||
|
|
||||||
/* define bit use in NFC_CMD */
|
/* define bit use in NFC_CMD */
|
||||||
#define NFC_CMD_LOW_BYTE GENMASK(7, 0)
|
#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0)
|
||||||
#define NFC_CMD_HIGH_BYTE GENMASK(15, 8)
|
#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8)
|
||||||
#define NFC_ADR_NUM GENMASK(18, 16)
|
#define NFC_CMD(x) (x)
|
||||||
|
#define NFC_ADR_NUM_MSK GENMASK(18, 16)
|
||||||
|
#define NFC_ADR_NUM(x) (((x) - 1) << 16)
|
||||||
#define NFC_SEND_ADR BIT(19)
|
#define NFC_SEND_ADR BIT(19)
|
||||||
#define NFC_ACCESS_DIR BIT(20)
|
#define NFC_ACCESS_DIR BIT(20)
|
||||||
#define NFC_DATA_TRANS BIT(21)
|
#define NFC_DATA_TRANS BIT(21)
|
||||||
|
@ -123,33 +123,38 @@
|
||||||
#define NFC_ROW_AUTO_INC BIT(27)
|
#define NFC_ROW_AUTO_INC BIT(27)
|
||||||
#define NFC_SEND_CMD3 BIT(28)
|
#define NFC_SEND_CMD3 BIT(28)
|
||||||
#define NFC_SEND_CMD4 BIT(29)
|
#define NFC_SEND_CMD4 BIT(29)
|
||||||
#define NFC_CMD_TYPE GENMASK(31, 30)
|
#define NFC_CMD_TYPE_MSK GENMASK(31, 30)
|
||||||
|
#define NFC_NORMAL_OP (0 << 30)
|
||||||
|
#define NFC_ECC_OP (1 << 30)
|
||||||
|
#define NFC_PAGE_OP (2 << 30)
|
||||||
|
|
||||||
/* define bit use in NFC_RCMD_SET */
|
/* define bit use in NFC_RCMD_SET */
|
||||||
#define NFC_READ_CMD GENMASK(7, 0)
|
#define NFC_READ_CMD_MSK GENMASK(7, 0)
|
||||||
#define NFC_RANDOM_READ_CMD0 GENMASK(15, 8)
|
#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8)
|
||||||
#define NFC_RANDOM_READ_CMD1 GENMASK(23, 16)
|
#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16)
|
||||||
|
|
||||||
/* define bit use in NFC_WCMD_SET */
|
/* define bit use in NFC_WCMD_SET */
|
||||||
#define NFC_PROGRAM_CMD GENMASK(7, 0)
|
#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0)
|
||||||
#define NFC_RANDOM_WRITE_CMD GENMASK(15, 8)
|
#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8)
|
||||||
#define NFC_READ_CMD0 GENMASK(23, 16)
|
#define NFC_READ_CMD0_MSK GENMASK(23, 16)
|
||||||
#define NFC_READ_CMD1 GENMASK(31, 24)
|
#define NFC_READ_CMD1_MSK GENMASK(31, 24)
|
||||||
|
|
||||||
/* define bit use in NFC_ECC_CTL */
|
/* define bit use in NFC_ECC_CTL */
|
||||||
#define NFC_ECC_EN BIT(0)
|
#define NFC_ECC_EN BIT(0)
|
||||||
#define NFC_ECC_PIPELINE BIT(3)
|
#define NFC_ECC_PIPELINE BIT(3)
|
||||||
#define NFC_ECC_EXCEPTION BIT(4)
|
#define NFC_ECC_EXCEPTION BIT(4)
|
||||||
#define NFC_ECC_BLOCK_SIZE BIT(5)
|
#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
|
||||||
#define NFC_RANDOM_EN BIT(9)
|
#define NFC_RANDOM_EN BIT(9)
|
||||||
#define NFC_RANDOM_DIRECTION BIT(10)
|
#define NFC_RANDOM_DIRECTION BIT(10)
|
||||||
#define NFC_ECC_MODE_SHIFT 12
|
#define NFC_ECC_MODE_MSK GENMASK(15, 12)
|
||||||
#define NFC_ECC_MODE GENMASK(15, 12)
|
#define NFC_ECC_MODE(x) ((x) << 12)
|
||||||
#define NFC_RANDOM_SEED GENMASK(30, 16)
|
#define NFC_RANDOM_SEED_MSK GENMASK(30, 16)
|
||||||
|
#define NFC_RANDOM_SEED(x) ((x) << 16)
|
||||||
|
|
||||||
/* NFC_USER_DATA helper macros */
|
/* define bit use in NFC_ECC_ST */
|
||||||
#define NFC_BUF_TO_USER_DATA(buf) ((buf)[0] | ((buf)[1] << 8) | \
|
#define NFC_ECC_ERR(x) BIT(x)
|
||||||
((buf)[2] << 16) | ((buf)[3] << 24))
|
#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
|
||||||
|
#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff)
|
||||||
|
|
||||||
#define NFC_DEFAULT_TIMEOUT_MS 1000
|
#define NFC_DEFAULT_TIMEOUT_MS 1000
|
||||||
|
|
||||||
|
@ -360,13 +365,13 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
|
||||||
switch (rb->type) {
|
switch (rb->type) {
|
||||||
case RB_NATIVE:
|
case RB_NATIVE:
|
||||||
ret = !!(readl(nfc->regs + NFC_REG_ST) &
|
ret = !!(readl(nfc->regs + NFC_REG_ST) &
|
||||||
(NFC_RB_STATE0 << rb->info.nativeid));
|
NFC_RB_STATE(rb->info.nativeid));
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
|
sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
|
||||||
ret = !!(readl(nfc->regs + NFC_REG_ST) &
|
ret = !!(readl(nfc->regs + NFC_REG_ST) &
|
||||||
(NFC_RB_STATE0 << rb->info.nativeid));
|
NFC_RB_STATE(rb->info.nativeid));
|
||||||
break;
|
break;
|
||||||
case RB_GPIO:
|
case RB_GPIO:
|
||||||
ret = gpio_get_value(rb->info.gpio);
|
ret = gpio_get_value(rb->info.gpio);
|
||||||
|
@ -396,19 +401,19 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ctl = readl(nfc->regs + NFC_REG_CTL) &
|
ctl = readl(nfc->regs + NFC_REG_CTL) &
|
||||||
~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN);
|
~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
|
||||||
|
|
||||||
if (chip >= 0) {
|
if (chip >= 0) {
|
||||||
sel = &sunxi_nand->sels[chip];
|
sel = &sunxi_nand->sels[chip];
|
||||||
|
|
||||||
ctl |= (sel->cs << 24) | NFC_EN |
|
ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
|
||||||
(((nand->page_shift - 10) & 0xf) << 8);
|
NFC_PAGE_SHIFT(nand->page_shift - 10);
|
||||||
if (sel->rb.type == RB_NONE) {
|
if (sel->rb.type == RB_NONE) {
|
||||||
nand->dev_ready = NULL;
|
nand->dev_ready = NULL;
|
||||||
} else {
|
} else {
|
||||||
nand->dev_ready = sunxi_nfc_dev_ready;
|
nand->dev_ready = sunxi_nfc_dev_ready;
|
||||||
if (sel->rb.type == RB_NATIVE)
|
if (sel->rb.type == RB_NATIVE)
|
||||||
ctl |= (sel->rb.info.nativeid << 3);
|
ctl |= NFC_RB_SEL(sel->rb.info.nativeid);
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
|
writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
|
||||||
|
@ -534,155 +539,244 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
|
||||||
sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd->priv;
|
||||||
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
|
struct sunxi_nand_hw_ecc *data = nand->ecc.priv;
|
||||||
|
u32 ecc_ctl;
|
||||||
|
|
||||||
|
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
|
||||||
|
ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
|
||||||
|
NFC_ECC_BLOCK_SIZE_MSK);
|
||||||
|
ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION;
|
||||||
|
|
||||||
|
writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd->priv;
|
||||||
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
|
|
||||||
|
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
|
||||||
|
nfc->regs + NFC_REG_ECC_CTL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
|
||||||
|
{
|
||||||
|
buf[0] = user_data;
|
||||||
|
buf[1] = user_data >> 8;
|
||||||
|
buf[2] = user_data >> 16;
|
||||||
|
buf[3] = user_data >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd->priv;
|
||||||
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
|
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (data_off + ecc->size != oob_off)
|
||||||
|
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
|
||||||
|
|
||||||
|
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
status = readl(nfc->regs + NFC_REG_ECC_ST);
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (status & NFC_ECC_ERR(0)) {
|
||||||
|
ret = nand_check_erased_ecc_chunk(data, ecc->size,
|
||||||
|
oob, ecc->bytes + 4,
|
||||||
|
NULL, 0, ecc->strength);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The engine protects 4 bytes of OOB data per chunk.
|
||||||
|
* Retrieve the corrected OOB bytes.
|
||||||
|
*/
|
||||||
|
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)),
|
||||||
|
oob);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
mtd->ecc_stats.failed++;
|
||||||
|
} else {
|
||||||
|
mtd->ecc_stats.corrected += ret;
|
||||||
|
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
*cur_off = oob_off + ecc->bytes + 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
|
||||||
|
u8 *oob, int *cur_off)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd->priv;
|
||||||
|
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||||
|
int offset = ((ecc->bytes + 4) * ecc->steps);
|
||||||
|
int len = mtd->oobsize - offset;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (*cur_off != offset)
|
||||||
|
nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
||||||
|
offset + mtd->writesize, -1);
|
||||||
|
|
||||||
|
sunxi_nfc_read_buf(mtd, oob + offset, len);
|
||||||
|
|
||||||
|
*cur_off = mtd->oobsize + mtd->writesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
|
||||||
|
{
|
||||||
|
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd->priv;
|
||||||
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
|
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (data_off != *cur_off)
|
||||||
|
nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1);
|
||||||
|
|
||||||
|
sunxi_nfc_write_buf(mtd, data, ecc->size);
|
||||||
|
|
||||||
|
/* Fill OOB data in */
|
||||||
|
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);
|
||||||
|
|
||||||
|
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
*cur_off = oob_off + ecc->bytes + 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
|
||||||
|
u8 *oob, int *cur_off)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd->priv;
|
||||||
|
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||||
|
int offset = ((ecc->bytes + 4) * ecc->steps);
|
||||||
|
int len = mtd->oobsize - offset;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (*cur_off != offset)
|
||||||
|
nand->cmdfunc(mtd, NAND_CMD_RNDIN,
|
||||||
|
offset + mtd->writesize, -1);
|
||||||
|
|
||||||
|
sunxi_nfc_write_buf(mtd, oob + offset, len);
|
||||||
|
|
||||||
|
*cur_off = mtd->oobsize + mtd->writesize;
|
||||||
|
}
|
||||||
|
|
||||||
static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
|
static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip, uint8_t *buf,
|
struct nand_chip *chip, uint8_t *buf,
|
||||||
int oob_required, int page)
|
int oob_required, int page)
|
||||||
{
|
{
|
||||||
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
|
|
||||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||||
struct nand_ecclayout *layout = ecc->layout;
|
|
||||||
struct sunxi_nand_hw_ecc *data = ecc->priv;
|
|
||||||
unsigned int max_bitflips = 0;
|
unsigned int max_bitflips = 0;
|
||||||
int offset;
|
int ret, i, cur_off = 0;
|
||||||
int ret;
|
|
||||||
u32 tmp;
|
|
||||||
int i;
|
|
||||||
int cnt;
|
|
||||||
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
|
sunxi_nfc_hw_ecc_enable(mtd);
|
||||||
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
|
|
||||||
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
|
|
||||||
NFC_ECC_EXCEPTION;
|
|
||||||
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
for (i = 0; i < ecc->steps; i++) {
|
for (i = 0; i < ecc->steps; i++) {
|
||||||
if (i)
|
int data_off = i * ecc->size;
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
|
int oob_off = i * (ecc->bytes + 4);
|
||||||
|
u8 *data = buf + data_off;
|
||||||
|
u8 *oob = chip->oob_poi + oob_off;
|
||||||
|
|
||||||
offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
|
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
|
||||||
|
oob_off + mtd->writesize,
|
||||||
chip->read_buf(mtd, NULL, ecc->size);
|
&cur_off, &max_bitflips);
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
|
|
||||||
|
|
||||||
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_CMD);
|
|
||||||
|
|
||||||
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
memcpy_fromio(buf + (i * ecc->size),
|
|
||||||
nfc->regs + NFC_RAM0_BASE, ecc->size);
|
|
||||||
|
|
||||||
if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
|
|
||||||
mtd->ecc_stats.failed++;
|
|
||||||
} else {
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
|
|
||||||
mtd->ecc_stats.corrected += tmp;
|
|
||||||
max_bitflips = max_t(unsigned int, max_bitflips, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oob_required) {
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
|
|
||||||
|
|
||||||
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
offset -= mtd->writesize;
|
|
||||||
chip->read_buf(mtd, chip->oob_poi + offset,
|
|
||||||
ecc->bytes + 4);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oob_required) {
|
if (oob_required)
|
||||||
cnt = ecc->layout->oobfree[ecc->steps].length;
|
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off);
|
||||||
if (cnt > 0) {
|
|
||||||
offset = mtd->writesize +
|
|
||||||
ecc->layout->oobfree[ecc->steps].offset;
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
|
|
||||||
offset -= mtd->writesize;
|
|
||||||
chip->read_buf(mtd, chip->oob_poi + offset, cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
|
sunxi_nfc_hw_ecc_disable(mtd);
|
||||||
tmp &= ~NFC_ECC_EN;
|
|
||||||
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
return max_bitflips;
|
return max_bitflips;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
|
static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required)
|
const uint8_t *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
|
|
||||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||||
struct nand_ecclayout *layout = ecc->layout;
|
int ret, i, cur_off = 0;
|
||||||
struct sunxi_nand_hw_ecc *data = ecc->priv;
|
|
||||||
int offset;
|
|
||||||
int ret;
|
|
||||||
u32 tmp;
|
|
||||||
int i;
|
|
||||||
int cnt;
|
|
||||||
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
|
sunxi_nfc_hw_ecc_enable(mtd);
|
||||||
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
|
|
||||||
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
|
|
||||||
NFC_ECC_EXCEPTION;
|
|
||||||
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
for (i = 0; i < ecc->steps; i++) {
|
for (i = 0; i < ecc->steps; i++) {
|
||||||
if (i)
|
int data_off = i * ecc->size;
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
|
int oob_off = i * (ecc->bytes + 4);
|
||||||
|
const u8 *data = buf + data_off;
|
||||||
|
const u8 *oob = chip->oob_poi + oob_off;
|
||||||
|
|
||||||
chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
|
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
|
||||||
|
oob_off + mtd->writesize,
|
||||||
offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
|
&cur_off);
|
||||||
|
|
||||||
/* Fill OOB data in */
|
|
||||||
writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
|
|
||||||
layout->oobfree[i].offset),
|
|
||||||
nfc->regs + NFC_REG_USER_DATA_BASE);
|
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
|
|
||||||
|
|
||||||
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
|
|
||||||
(1 << 30);
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_CMD);
|
|
||||||
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oob_required) {
|
if (oob_required)
|
||||||
cnt = ecc->layout->oobfree[i].length;
|
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off);
|
||||||
if (cnt > 0) {
|
|
||||||
offset = mtd->writesize +
|
|
||||||
ecc->layout->oobfree[i].offset;
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
|
|
||||||
offset -= mtd->writesize;
|
|
||||||
chip->write_buf(mtd, chip->oob_poi + offset, cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
|
sunxi_nfc_hw_ecc_disable(mtd);
|
||||||
tmp &= ~NFC_ECC_EN;
|
|
||||||
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -692,65 +786,29 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
|
||||||
uint8_t *buf, int oob_required,
|
uint8_t *buf, int oob_required,
|
||||||
int page)
|
int page)
|
||||||
{
|
{
|
||||||
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
|
|
||||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||||
struct sunxi_nand_hw_ecc *data = ecc->priv;
|
|
||||||
unsigned int max_bitflips = 0;
|
unsigned int max_bitflips = 0;
|
||||||
uint8_t *oob = chip->oob_poi;
|
int ret, i, cur_off = 0;
|
||||||
int offset = 0;
|
|
||||||
int ret;
|
|
||||||
int cnt;
|
|
||||||
u32 tmp;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
|
sunxi_nfc_hw_ecc_enable(mtd);
|
||||||
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
|
|
||||||
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
|
|
||||||
NFC_ECC_EXCEPTION;
|
|
||||||
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
for (i = 0; i < ecc->steps; i++) {
|
for (i = 0; i < ecc->steps; i++) {
|
||||||
chip->read_buf(mtd, NULL, ecc->size);
|
int data_off = i * (ecc->size + ecc->bytes + 4);
|
||||||
|
int oob_off = data_off + ecc->size;
|
||||||
|
u8 *data = buf + (i * ecc->size);
|
||||||
|
u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
|
||||||
|
|
||||||
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
|
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
|
||||||
writel(tmp, nfc->regs + NFC_REG_CMD);
|
oob_off, &cur_off,
|
||||||
|
&max_bitflips);
|
||||||
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
memcpy_fromio(buf, nfc->regs + NFC_RAM0_BASE, ecc->size);
|
|
||||||
buf += ecc->size;
|
|
||||||
offset += ecc->size;
|
|
||||||
|
|
||||||
if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
|
|
||||||
mtd->ecc_stats.failed++;
|
|
||||||
} else {
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
|
|
||||||
mtd->ecc_stats.corrected += tmp;
|
|
||||||
max_bitflips = max_t(unsigned int, max_bitflips, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oob_required) {
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
|
|
||||||
chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad);
|
|
||||||
oob += ecc->bytes + ecc->prepad;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += ecc->bytes + ecc->prepad;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oob_required) {
|
if (oob_required)
|
||||||
cnt = mtd->oobsize - (oob - chip->oob_poi);
|
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off);
|
||||||
if (cnt > 0) {
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
|
|
||||||
chip->read_buf(mtd, oob, cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
|
sunxi_nfc_hw_ecc_disable(mtd);
|
||||||
nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
return max_bitflips;
|
return max_bitflips;
|
||||||
}
|
}
|
||||||
|
@ -758,57 +816,29 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
|
||||||
static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
|
static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
int oob_required)
|
int oob_required, int page)
|
||||||
{
|
{
|
||||||
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
|
|
||||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||||
struct sunxi_nand_hw_ecc *data = ecc->priv;
|
int ret, i, cur_off = 0;
|
||||||
uint8_t *oob = chip->oob_poi;
|
|
||||||
int offset = 0;
|
|
||||||
int ret;
|
|
||||||
int cnt;
|
|
||||||
u32 tmp;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
|
sunxi_nfc_hw_ecc_enable(mtd);
|
||||||
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
|
|
||||||
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
|
|
||||||
NFC_ECC_EXCEPTION;
|
|
||||||
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
for (i = 0; i < ecc->steps; i++) {
|
for (i = 0; i < ecc->steps; i++) {
|
||||||
chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
|
int data_off = i * (ecc->size + ecc->bytes + 4);
|
||||||
offset += ecc->size;
|
int oob_off = data_off + ecc->size;
|
||||||
|
const u8 *data = buf + (i * ecc->size);
|
||||||
|
const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
|
||||||
|
|
||||||
/* Fill OOB data in */
|
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
|
||||||
writel(NFC_BUF_TO_USER_DATA(oob),
|
oob, oob_off, &cur_off);
|
||||||
nfc->regs + NFC_REG_USER_DATA_BASE);
|
|
||||||
|
|
||||||
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
|
|
||||||
(1 << 30);
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_CMD);
|
|
||||||
|
|
||||||
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
offset += ecc->bytes + ecc->prepad;
|
|
||||||
oob += ecc->bytes + ecc->prepad;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oob_required) {
|
if (oob_required)
|
||||||
cnt = mtd->oobsize - (oob - chip->oob_poi);
|
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off);
|
||||||
if (cnt > 0) {
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
|
|
||||||
chip->write_buf(mtd, oob, cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
|
sunxi_nfc_hw_ecc_disable(mtd);
|
||||||
tmp &= ~NFC_ECC_EN;
|
|
||||||
|
|
||||||
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -970,17 +1000,23 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
|
||||||
mode = chip->nand.onfi_timing_mode_default;
|
mode = chip->nand.onfi_timing_mode_default;
|
||||||
} else {
|
} else {
|
||||||
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
|
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
|
||||||
|
int i;
|
||||||
|
|
||||||
mode = fls(mode) - 1;
|
mode = fls(mode) - 1;
|
||||||
if (mode < 0)
|
if (mode < 0)
|
||||||
mode = 0;
|
mode = 0;
|
||||||
|
|
||||||
feature[0] = mode;
|
feature[0] = mode;
|
||||||
ret = chip->nand.onfi_set_features(&chip->mtd, &chip->nand,
|
for (i = 0; i < chip->nsels; i++) {
|
||||||
|
chip->nand.select_chip(&chip->mtd, i);
|
||||||
|
ret = chip->nand.onfi_set_features(&chip->mtd,
|
||||||
|
&chip->nand,
|
||||||
ONFI_FEATURE_ADDR_TIMING_MODE,
|
ONFI_FEATURE_ADDR_TIMING_MODE,
|
||||||
feature);
|
feature);
|
||||||
if (ret)
|
chip->nand.select_chip(&chip->mtd, -1);
|
||||||
return ret;
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timings = onfi_async_timing_mode_to_sdr_timings(mode);
|
timings = onfi_async_timing_mode_to_sdr_timings(mode);
|
||||||
|
@ -1154,16 +1190,9 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
|
||||||
struct device_node *np)
|
struct device_node *np)
|
||||||
{
|
{
|
||||||
struct nand_chip *nand = mtd->priv;
|
struct nand_chip *nand = mtd->priv;
|
||||||
int strength;
|
|
||||||
int blk_size;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
blk_size = of_get_nand_ecc_step_size(np);
|
if (!ecc->size) {
|
||||||
strength = of_get_nand_ecc_strength(np);
|
|
||||||
if (blk_size > 0 && strength > 0) {
|
|
||||||
ecc->size = blk_size;
|
|
||||||
ecc->strength = strength;
|
|
||||||
} else {
|
|
||||||
ecc->size = nand->ecc_step_ds;
|
ecc->size = nand->ecc_step_ds;
|
||||||
ecc->strength = nand->ecc_strength_ds;
|
ecc->strength = nand->ecc_strength_ds;
|
||||||
}
|
}
|
||||||
|
@ -1171,12 +1200,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
|
||||||
if (!ecc->size || !ecc->strength)
|
if (!ecc->size || !ecc->strength)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ecc->mode = NAND_ECC_HW;
|
|
||||||
|
|
||||||
ret = of_get_nand_ecc_mode(np);
|
|
||||||
if (ret >= 0)
|
|
||||||
ecc->mode = ret;
|
|
||||||
|
|
||||||
switch (ecc->mode) {
|
switch (ecc->mode) {
|
||||||
case NAND_ECC_SOFT_BCH:
|
case NAND_ECC_SOFT_BCH:
|
||||||
break;
|
break;
|
||||||
|
@ -1302,24 +1325,29 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
|
||||||
/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
|
/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
|
||||||
nand->chip_delay = 200;
|
nand->chip_delay = 200;
|
||||||
nand->controller = &nfc->controller;
|
nand->controller = &nfc->controller;
|
||||||
|
/*
|
||||||
|
* Set the ECC mode to the default value in case nothing is specified
|
||||||
|
* in the DT.
|
||||||
|
*/
|
||||||
|
nand->ecc.mode = NAND_ECC_HW;
|
||||||
|
nand->flash_node = np;
|
||||||
nand->select_chip = sunxi_nfc_select_chip;
|
nand->select_chip = sunxi_nfc_select_chip;
|
||||||
nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
|
nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
|
||||||
nand->read_buf = sunxi_nfc_read_buf;
|
nand->read_buf = sunxi_nfc_read_buf;
|
||||||
nand->write_buf = sunxi_nfc_write_buf;
|
nand->write_buf = sunxi_nfc_write_buf;
|
||||||
nand->read_byte = sunxi_nfc_read_byte;
|
nand->read_byte = sunxi_nfc_read_byte;
|
||||||
|
|
||||||
if (of_get_nand_on_flash_bbt(np))
|
|
||||||
nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
|
|
||||||
|
|
||||||
mtd = &chip->mtd;
|
mtd = &chip->mtd;
|
||||||
mtd->dev.parent = dev;
|
mtd->dev.parent = dev;
|
||||||
mtd->priv = nand;
|
mtd->priv = nand;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
|
|
||||||
ret = nand_scan_ident(mtd, nsels, NULL);
|
ret = nand_scan_ident(mtd, nsels, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (nand->bbt_options & NAND_BBT_USE_FLASH)
|
||||||
|
nand->bbt_options |= NAND_BBT_NO_OOB;
|
||||||
|
|
||||||
ret = sunxi_nand_chip_init_timings(chip, np);
|
ret = sunxi_nand_chip_init_timings(chip, np);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "could not configure chip timings: %d\n", ret);
|
dev_err(dev, "could not configure chip timings: %d\n", ret);
|
||||||
|
|
|
@ -382,6 +382,7 @@ static int tmio_probe(struct platform_device *dev)
|
||||||
nand_chip = &tmio->chip;
|
nand_chip = &tmio->chip;
|
||||||
mtd->priv = nand_chip;
|
mtd->priv = nand_chip;
|
||||||
mtd->name = "tmio-nand";
|
mtd->name = "tmio-nand";
|
||||||
|
mtd->dev.parent = &dev->dev;
|
||||||
|
|
||||||
tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr));
|
tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr));
|
||||||
if (!tmio->ccr)
|
if (!tmio->ccr)
|
||||||
|
|
|
@ -323,7 +323,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
|
||||||
continue;
|
continue;
|
||||||
chip = &txx9_priv->chip;
|
chip = &txx9_priv->chip;
|
||||||
mtd = &txx9_priv->mtd;
|
mtd = &txx9_priv->mtd;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->dev.parent = &dev->dev;
|
||||||
|
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
|
|
||||||
|
|
878
drivers/mtd/nand/vf610_nfc.c
Normal file
878
drivers/mtd/nand/vf610_nfc.c
Normal file
|
@ -0,0 +1,878 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009-2015 Freescale Semiconductor, Inc. and others
|
||||||
|
*
|
||||||
|
* Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver.
|
||||||
|
* Jason ported to M54418TWR and MVFA5 (VF610).
|
||||||
|
* Authors: Stefan Agner <stefan.agner@toradex.com>
|
||||||
|
* Bill Pringlemeir <bpringlemeir@nbsps.com>
|
||||||
|
* Shaohui Xie <b21989@freescale.com>
|
||||||
|
* Jason Jin <Jason.jin@freescale.com>
|
||||||
|
*
|
||||||
|
* Based on original driver mpc5121_nfc.c.
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Limitations:
|
||||||
|
* - Untested on MPC5125 and M54418.
|
||||||
|
* - DMA and pipelining not used.
|
||||||
|
* - 2K pages or less.
|
||||||
|
* - HW ECC: Only 2K page with 64+ OOB.
|
||||||
|
* - HW ECC: Only 24 and 32-bit error correction implemented.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/of_mtd.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define DRV_NAME "vf610_nfc"
|
||||||
|
|
||||||
|
/* Register Offsets */
|
||||||
|
#define NFC_FLASH_CMD1 0x3F00
|
||||||
|
#define NFC_FLASH_CMD2 0x3F04
|
||||||
|
#define NFC_COL_ADDR 0x3F08
|
||||||
|
#define NFC_ROW_ADDR 0x3F0c
|
||||||
|
#define NFC_ROW_ADDR_INC 0x3F14
|
||||||
|
#define NFC_FLASH_STATUS1 0x3F18
|
||||||
|
#define NFC_FLASH_STATUS2 0x3F1c
|
||||||
|
#define NFC_CACHE_SWAP 0x3F28
|
||||||
|
#define NFC_SECTOR_SIZE 0x3F2c
|
||||||
|
#define NFC_FLASH_CONFIG 0x3F30
|
||||||
|
#define NFC_IRQ_STATUS 0x3F38
|
||||||
|
|
||||||
|
/* Addresses for NFC MAIN RAM BUFFER areas */
|
||||||
|
#define NFC_MAIN_AREA(n) ((n) * 0x1000)
|
||||||
|
|
||||||
|
#define PAGE_2K 0x0800
|
||||||
|
#define OOB_64 0x0040
|
||||||
|
#define OOB_MAX 0x0100
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NFC_CMD2[CODE] values. See section:
|
||||||
|
* - 31.4.7 Flash Command Code Description, Vybrid manual
|
||||||
|
* - 23.8.6 Flash Command Sequencer, MPC5125 manual
|
||||||
|
*
|
||||||
|
* Briefly these are bitmasks of controller cycles.
|
||||||
|
*/
|
||||||
|
#define READ_PAGE_CMD_CODE 0x7EE0
|
||||||
|
#define READ_ONFI_PARAM_CMD_CODE 0x4860
|
||||||
|
#define PROGRAM_PAGE_CMD_CODE 0x7FC0
|
||||||
|
#define ERASE_CMD_CODE 0x4EC0
|
||||||
|
#define READ_ID_CMD_CODE 0x4804
|
||||||
|
#define RESET_CMD_CODE 0x4040
|
||||||
|
#define STATUS_READ_CMD_CODE 0x4068
|
||||||
|
|
||||||
|
/* NFC ECC mode define */
|
||||||
|
#define ECC_BYPASS 0
|
||||||
|
#define ECC_45_BYTE 6
|
||||||
|
#define ECC_60_BYTE 7
|
||||||
|
|
||||||
|
/*** Register Mask and bit definitions */
|
||||||
|
|
||||||
|
/* NFC_FLASH_CMD1 Field */
|
||||||
|
#define CMD_BYTE2_MASK 0xFF000000
|
||||||
|
#define CMD_BYTE2_SHIFT 24
|
||||||
|
|
||||||
|
/* NFC_FLASH_CM2 Field */
|
||||||
|
#define CMD_BYTE1_MASK 0xFF000000
|
||||||
|
#define CMD_BYTE1_SHIFT 24
|
||||||
|
#define CMD_CODE_MASK 0x00FFFF00
|
||||||
|
#define CMD_CODE_SHIFT 8
|
||||||
|
#define BUFNO_MASK 0x00000006
|
||||||
|
#define BUFNO_SHIFT 1
|
||||||
|
#define START_BIT BIT(0)
|
||||||
|
|
||||||
|
/* NFC_COL_ADDR Field */
|
||||||
|
#define COL_ADDR_MASK 0x0000FFFF
|
||||||
|
#define COL_ADDR_SHIFT 0
|
||||||
|
|
||||||
|
/* NFC_ROW_ADDR Field */
|
||||||
|
#define ROW_ADDR_MASK 0x00FFFFFF
|
||||||
|
#define ROW_ADDR_SHIFT 0
|
||||||
|
#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000
|
||||||
|
#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28
|
||||||
|
#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000
|
||||||
|
#define ROW_ADDR_CHIP_SEL_SHIFT 24
|
||||||
|
|
||||||
|
/* NFC_FLASH_STATUS2 Field */
|
||||||
|
#define STATUS_BYTE1_MASK 0x000000FF
|
||||||
|
|
||||||
|
/* NFC_FLASH_CONFIG Field */
|
||||||
|
#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000
|
||||||
|
#define CONFIG_ECC_SRAM_ADDR_SHIFT 22
|
||||||
|
#define CONFIG_ECC_SRAM_REQ_BIT BIT(21)
|
||||||
|
#define CONFIG_DMA_REQ_BIT BIT(20)
|
||||||
|
#define CONFIG_ECC_MODE_MASK 0x000E0000
|
||||||
|
#define CONFIG_ECC_MODE_SHIFT 17
|
||||||
|
#define CONFIG_FAST_FLASH_BIT BIT(16)
|
||||||
|
#define CONFIG_16BIT BIT(7)
|
||||||
|
#define CONFIG_BOOT_MODE_BIT BIT(6)
|
||||||
|
#define CONFIG_ADDR_AUTO_INCR_BIT BIT(5)
|
||||||
|
#define CONFIG_BUFNO_AUTO_INCR_BIT BIT(4)
|
||||||
|
#define CONFIG_PAGE_CNT_MASK 0xF
|
||||||
|
#define CONFIG_PAGE_CNT_SHIFT 0
|
||||||
|
|
||||||
|
/* NFC_IRQ_STATUS Field */
|
||||||
|
#define IDLE_IRQ_BIT BIT(29)
|
||||||
|
#define IDLE_EN_BIT BIT(20)
|
||||||
|
#define CMD_DONE_CLEAR_BIT BIT(18)
|
||||||
|
#define IDLE_CLEAR_BIT BIT(17)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ECC status - seems to consume 8 bytes (double word). The documented
|
||||||
|
* status byte is located in the lowest byte of the second word (which is
|
||||||
|
* the 4th or 7th byte depending on endianness).
|
||||||
|
* Calculate an offset to store the ECC status at the end of the buffer.
|
||||||
|
*/
|
||||||
|
#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8)
|
||||||
|
|
||||||
|
#define ECC_STATUS 0x4
|
||||||
|
#define ECC_STATUS_MASK 0x80
|
||||||
|
#define ECC_STATUS_ERR_COUNT 0x3F
|
||||||
|
|
||||||
|
enum vf610_nfc_alt_buf {
|
||||||
|
ALT_BUF_DATA = 0,
|
||||||
|
ALT_BUF_ID = 1,
|
||||||
|
ALT_BUF_STAT = 2,
|
||||||
|
ALT_BUF_ONFI = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum vf610_nfc_variant {
|
||||||
|
NFC_VFC610 = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vf610_nfc {
|
||||||
|
struct mtd_info mtd;
|
||||||
|
struct nand_chip chip;
|
||||||
|
struct device *dev;
|
||||||
|
void __iomem *regs;
|
||||||
|
struct completion cmd_done;
|
||||||
|
uint buf_offset;
|
||||||
|
int write_sz;
|
||||||
|
/* Status and ID are in alternate locations. */
|
||||||
|
enum vf610_nfc_alt_buf alt_buf;
|
||||||
|
enum vf610_nfc_variant variant;
|
||||||
|
struct clk *clk;
|
||||||
|
bool use_hw_ecc;
|
||||||
|
u32 ecc_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define mtd_to_nfc(_mtd) container_of(_mtd, struct vf610_nfc, mtd)
|
||||||
|
|
||||||
|
static struct nand_ecclayout vf610_nfc_ecc45 = {
|
||||||
|
.eccbytes = 45,
|
||||||
|
.eccpos = {19, 20, 21, 22, 23,
|
||||||
|
24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39,
|
||||||
|
40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55,
|
||||||
|
56, 57, 58, 59, 60, 61, 62, 63},
|
||||||
|
.oobfree = {
|
||||||
|
{.offset = 2,
|
||||||
|
.length = 17} }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nand_ecclayout vf610_nfc_ecc60 = {
|
||||||
|
.eccbytes = 60,
|
||||||
|
.eccpos = { 4, 5, 6, 7, 8, 9, 10, 11,
|
||||||
|
12, 13, 14, 15, 16, 17, 18, 19,
|
||||||
|
20, 21, 22, 23, 24, 25, 26, 27,
|
||||||
|
28, 29, 30, 31, 32, 33, 34, 35,
|
||||||
|
36, 37, 38, 39, 40, 41, 42, 43,
|
||||||
|
44, 45, 46, 47, 48, 49, 50, 51,
|
||||||
|
52, 53, 54, 55, 56, 57, 58, 59,
|
||||||
|
60, 61, 62, 63 },
|
||||||
|
.oobfree = {
|
||||||
|
{.offset = 2,
|
||||||
|
.length = 2} }
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg)
|
||||||
|
{
|
||||||
|
return readl(nfc->regs + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vf610_nfc_write(struct vf610_nfc *nfc, uint reg, u32 val)
|
||||||
|
{
|
||||||
|
writel(val, nfc->regs + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vf610_nfc_set(struct vf610_nfc *nfc, uint reg, u32 bits)
|
||||||
|
{
|
||||||
|
vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) | bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vf610_nfc_clear(struct vf610_nfc *nfc, uint reg, u32 bits)
|
||||||
|
{
|
||||||
|
vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) & ~bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg,
|
||||||
|
u32 mask, u32 shift, u32 val)
|
||||||
|
{
|
||||||
|
vf610_nfc_write(nfc, reg,
|
||||||
|
(vf610_nfc_read(nfc, reg) & (~mask)) | val << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src,
|
||||||
|
size_t n)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Use this accessor for the internal SRAM buffers. On the ARM
|
||||||
|
* Freescale Vybrid SoC it's known that the driver can treat
|
||||||
|
* the SRAM buffer as if it's memory. Other platform might need
|
||||||
|
* to treat the buffers differently.
|
||||||
|
*
|
||||||
|
* For the time being, use memcpy
|
||||||
|
*/
|
||||||
|
memcpy(dst, src, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear flags for upcoming command */
|
||||||
|
static inline void vf610_nfc_clear_status(struct vf610_nfc *nfc)
|
||||||
|
{
|
||||||
|
u32 tmp = vf610_nfc_read(nfc, NFC_IRQ_STATUS);
|
||||||
|
|
||||||
|
tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT;
|
||||||
|
vf610_nfc_write(nfc, NFC_IRQ_STATUS, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_done(struct vf610_nfc *nfc)
|
||||||
|
{
|
||||||
|
unsigned long timeout = msecs_to_jiffies(100);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Barrier is needed after this write. This write need
|
||||||
|
* to be done before reading the next register the first
|
||||||
|
* time.
|
||||||
|
* vf610_nfc_set implicates such a barrier by using writel
|
||||||
|
* to write to the register.
|
||||||
|
*/
|
||||||
|
vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
|
||||||
|
vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT);
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(&nfc->cmd_done, timeout))
|
||||||
|
dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n");
|
||||||
|
|
||||||
|
vf610_nfc_clear_status(nfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col)
|
||||||
|
{
|
||||||
|
u32 flash_id;
|
||||||
|
|
||||||
|
if (col < 4) {
|
||||||
|
flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1);
|
||||||
|
flash_id >>= (3 - col) * 8;
|
||||||
|
} else {
|
||||||
|
flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2);
|
||||||
|
flash_id >>= 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flash_id & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 vf610_nfc_get_status(struct vf610_nfc *nfc)
|
||||||
|
{
|
||||||
|
return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1,
|
||||||
|
u32 cmd_code)
|
||||||
|
{
|
||||||
|
u32 tmp;
|
||||||
|
|
||||||
|
vf610_nfc_clear_status(nfc);
|
||||||
|
|
||||||
|
tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2);
|
||||||
|
tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK);
|
||||||
|
tmp |= cmd_byte1 << CMD_BYTE1_SHIFT;
|
||||||
|
tmp |= cmd_code << CMD_CODE_SHIFT;
|
||||||
|
vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1,
|
||||||
|
u32 cmd_byte2, u32 cmd_code)
|
||||||
|
{
|
||||||
|
u32 tmp;
|
||||||
|
|
||||||
|
vf610_nfc_send_command(nfc, cmd_byte1, cmd_code);
|
||||||
|
|
||||||
|
tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1);
|
||||||
|
tmp &= ~CMD_BYTE2_MASK;
|
||||||
|
tmp |= cmd_byte2 << CMD_BYTE2_SHIFT;
|
||||||
|
vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t vf610_nfc_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = data;
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
|
||||||
|
vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
|
||||||
|
complete(&nfc->cmd_done);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page)
|
||||||
|
{
|
||||||
|
if (column != -1) {
|
||||||
|
if (nfc->chip.options & NAND_BUSWIDTH_16)
|
||||||
|
column = column / 2;
|
||||||
|
vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK,
|
||||||
|
COL_ADDR_SHIFT, column);
|
||||||
|
}
|
||||||
|
if (page != -1)
|
||||||
|
vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK,
|
||||||
|
ROW_ADDR_SHIFT, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode)
|
||||||
|
{
|
||||||
|
vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
|
||||||
|
CONFIG_ECC_MODE_MASK,
|
||||||
|
CONFIG_ECC_MODE_SHIFT, ecc_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size)
|
||||||
|
{
|
||||||
|
vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
|
||||||
|
int column, int page)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0;
|
||||||
|
|
||||||
|
nfc->buf_offset = max(column, 0);
|
||||||
|
nfc->alt_buf = ALT_BUF_DATA;
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case NAND_CMD_SEQIN:
|
||||||
|
/* Use valid column/page from preread... */
|
||||||
|
vf610_nfc_addr_cycle(nfc, column, page);
|
||||||
|
nfc->buf_offset = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SEQIN => data => PAGEPROG sequence is done by the controller
|
||||||
|
* hence we do not need to issue the command here...
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
case NAND_CMD_PAGEPROG:
|
||||||
|
trfr_sz += nfc->write_sz;
|
||||||
|
vf610_nfc_transfer_size(nfc, trfr_sz);
|
||||||
|
vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN,
|
||||||
|
command, PROGRAM_PAGE_CMD_CODE);
|
||||||
|
if (nfc->use_hw_ecc)
|
||||||
|
vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
|
||||||
|
else
|
||||||
|
vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_RESET:
|
||||||
|
vf610_nfc_transfer_size(nfc, 0);
|
||||||
|
vf610_nfc_send_command(nfc, command, RESET_CMD_CODE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_READOOB:
|
||||||
|
trfr_sz += mtd->oobsize;
|
||||||
|
column = mtd->writesize;
|
||||||
|
vf610_nfc_transfer_size(nfc, trfr_sz);
|
||||||
|
vf610_nfc_send_commands(nfc, NAND_CMD_READ0,
|
||||||
|
NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
|
||||||
|
vf610_nfc_addr_cycle(nfc, column, page);
|
||||||
|
vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_READ0:
|
||||||
|
trfr_sz += mtd->writesize + mtd->oobsize;
|
||||||
|
vf610_nfc_transfer_size(nfc, trfr_sz);
|
||||||
|
vf610_nfc_send_commands(nfc, NAND_CMD_READ0,
|
||||||
|
NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
|
||||||
|
vf610_nfc_addr_cycle(nfc, column, page);
|
||||||
|
vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_PARAM:
|
||||||
|
nfc->alt_buf = ALT_BUF_ONFI;
|
||||||
|
trfr_sz = 3 * sizeof(struct nand_onfi_params);
|
||||||
|
vf610_nfc_transfer_size(nfc, trfr_sz);
|
||||||
|
vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE);
|
||||||
|
vf610_nfc_addr_cycle(nfc, -1, column);
|
||||||
|
vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_ERASE1:
|
||||||
|
vf610_nfc_transfer_size(nfc, 0);
|
||||||
|
vf610_nfc_send_commands(nfc, command,
|
||||||
|
NAND_CMD_ERASE2, ERASE_CMD_CODE);
|
||||||
|
vf610_nfc_addr_cycle(nfc, column, page);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_READID:
|
||||||
|
nfc->alt_buf = ALT_BUF_ID;
|
||||||
|
nfc->buf_offset = 0;
|
||||||
|
vf610_nfc_transfer_size(nfc, 0);
|
||||||
|
vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE);
|
||||||
|
vf610_nfc_addr_cycle(nfc, -1, column);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_CMD_STATUS:
|
||||||
|
nfc->alt_buf = ALT_BUF_STAT;
|
||||||
|
vf610_nfc_transfer_size(nfc, 0);
|
||||||
|
vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vf610_nfc_done(nfc);
|
||||||
|
|
||||||
|
nfc->use_hw_ecc = false;
|
||||||
|
nfc->write_sz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
uint c = nfc->buf_offset;
|
||||||
|
|
||||||
|
/* Alternate buffers are only supported through read_byte */
|
||||||
|
WARN_ON(nfc->alt_buf);
|
||||||
|
|
||||||
|
vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len);
|
||||||
|
|
||||||
|
nfc->buf_offset += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
||||||
|
int len)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
uint c = nfc->buf_offset;
|
||||||
|
uint l;
|
||||||
|
|
||||||
|
l = min_t(uint, len, mtd->writesize + mtd->oobsize - c);
|
||||||
|
vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l);
|
||||||
|
|
||||||
|
nfc->write_sz += l;
|
||||||
|
nfc->buf_offset += l;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
u8 tmp;
|
||||||
|
uint c = nfc->buf_offset;
|
||||||
|
|
||||||
|
switch (nfc->alt_buf) {
|
||||||
|
case ALT_BUF_ID:
|
||||||
|
tmp = vf610_nfc_get_id(nfc, c);
|
||||||
|
break;
|
||||||
|
case ALT_BUF_STAT:
|
||||||
|
tmp = vf610_nfc_get_status(nfc);
|
||||||
|
break;
|
||||||
|
#ifdef __LITTLE_ENDIAN
|
||||||
|
case ALT_BUF_ONFI:
|
||||||
|
/* Reverse byte since the controller uses big endianness */
|
||||||
|
c = nfc->buf_offset ^ 0x3;
|
||||||
|
/* fall-through */
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nfc->buf_offset++;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16 vf610_nfc_read_word(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
u16 tmp;
|
||||||
|
|
||||||
|
vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If not provided, upper layers apply a fixed delay. */
|
||||||
|
static int vf610_nfc_dev_ready(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
/* NFC handles R/B internally; always ready. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function supports Vybrid only (MPC5125 would have full RB and four CS)
|
||||||
|
*/
|
||||||
|
static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR);
|
||||||
|
|
||||||
|
/* Vybrid only (MPC5125 would have full RB and four CS) */
|
||||||
|
if (nfc->variant != NFC_VFC610)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK);
|
||||||
|
|
||||||
|
if (chip >= 0) {
|
||||||
|
tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT;
|
||||||
|
tmp |= BIT(chip) << ROW_ADDR_CHIP_SEL_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Count the number of 0's in buff up to max_bits */
|
||||||
|
static inline int count_written_bits(uint8_t *buff, int size, int max_bits)
|
||||||
|
{
|
||||||
|
uint32_t *buff32 = (uint32_t *)buff;
|
||||||
|
int k, written_bits = 0;
|
||||||
|
|
||||||
|
for (k = 0; k < (size / 4); k++) {
|
||||||
|
written_bits += hweight32(~buff32[k]);
|
||||||
|
if (unlikely(written_bits > max_bits))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return written_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
|
||||||
|
uint8_t *oob, int page)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS;
|
||||||
|
u8 ecc_status;
|
||||||
|
u8 ecc_count;
|
||||||
|
int flips_threshold = nfc->chip.ecc.strength / 2;
|
||||||
|
|
||||||
|
ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff;
|
||||||
|
ecc_count = ecc_status & ECC_STATUS_ERR_COUNT;
|
||||||
|
|
||||||
|
if (!(ecc_status & ECC_STATUS_MASK))
|
||||||
|
return ecc_count;
|
||||||
|
|
||||||
|
/* Read OOB without ECC unit enabled */
|
||||||
|
vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page);
|
||||||
|
vf610_nfc_read_buf(mtd, oob, mtd->oobsize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On an erased page, bit count (including OOB) should be zero or
|
||||||
|
* at least less then half of the ECC strength.
|
||||||
|
*/
|
||||||
|
return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob,
|
||||||
|
mtd->oobsize, NULL, 0,
|
||||||
|
flips_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
uint8_t *buf, int oob_required, int page)
|
||||||
|
{
|
||||||
|
int eccsize = chip->ecc.size;
|
||||||
|
int stat;
|
||||||
|
|
||||||
|
vf610_nfc_read_buf(mtd, buf, eccsize);
|
||||||
|
if (oob_required)
|
||||||
|
vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
|
||||||
|
stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page);
|
||||||
|
|
||||||
|
if (stat < 0) {
|
||||||
|
mtd->ecc_stats.failed++;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
mtd->ecc_stats.corrected += stat;
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
const uint8_t *buf, int oob_required, int page)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
|
||||||
|
vf610_nfc_write_buf(mtd, buf, mtd->writesize);
|
||||||
|
if (oob_required)
|
||||||
|
vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
|
||||||
|
/* Always write whole page including OOB due to HW ECC */
|
||||||
|
nfc->use_hw_ecc = true;
|
||||||
|
nfc->write_sz = mtd->writesize + mtd->oobsize;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id vf610_nfc_dt_ids[] = {
|
||||||
|
{ .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, vf610_nfc_dt_ids);
|
||||||
|
|
||||||
|
static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc)
|
||||||
|
{
|
||||||
|
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
|
||||||
|
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT);
|
||||||
|
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT);
|
||||||
|
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT);
|
||||||
|
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT);
|
||||||
|
vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT);
|
||||||
|
|
||||||
|
/* Disable virtual pages, only one elementary transfer unit */
|
||||||
|
vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK,
|
||||||
|
CONFIG_PAGE_CNT_SHIFT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vf610_nfc_init_controller(struct vf610_nfc *nfc)
|
||||||
|
{
|
||||||
|
if (nfc->chip.options & NAND_BUSWIDTH_16)
|
||||||
|
vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
|
||||||
|
else
|
||||||
|
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
|
||||||
|
|
||||||
|
if (nfc->chip.ecc.mode == NAND_ECC_HW) {
|
||||||
|
/* Set ECC status offset in SRAM */
|
||||||
|
vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
|
||||||
|
CONFIG_ECC_SRAM_ADDR_MASK,
|
||||||
|
CONFIG_ECC_SRAM_ADDR_SHIFT,
|
||||||
|
ECC_SRAM_ADDR >> 3);
|
||||||
|
|
||||||
|
/* Enable ECC status in SRAM */
|
||||||
|
vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vf610_nfc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct vf610_nfc *nfc;
|
||||||
|
struct resource *res;
|
||||||
|
struct mtd_info *mtd;
|
||||||
|
struct nand_chip *chip;
|
||||||
|
struct device_node *child;
|
||||||
|
const struct of_device_id *of_id;
|
||||||
|
int err;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
|
||||||
|
if (!nfc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
nfc->dev = &pdev->dev;
|
||||||
|
mtd = &nfc->mtd;
|
||||||
|
chip = &nfc->chip;
|
||||||
|
|
||||||
|
mtd->priv = chip;
|
||||||
|
mtd->owner = THIS_MODULE;
|
||||||
|
mtd->dev.parent = nfc->dev;
|
||||||
|
mtd->name = DRV_NAME;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq <= 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
nfc->regs = devm_ioremap_resource(nfc->dev, res);
|
||||||
|
if (IS_ERR(nfc->regs))
|
||||||
|
return PTR_ERR(nfc->regs);
|
||||||
|
|
||||||
|
nfc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(nfc->clk))
|
||||||
|
return PTR_ERR(nfc->clk);
|
||||||
|
|
||||||
|
err = clk_prepare_enable(nfc->clk);
|
||||||
|
if (err) {
|
||||||
|
dev_err(nfc->dev, "Unable to enable clock!\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev);
|
||||||
|
nfc->variant = (enum vf610_nfc_variant)of_id->data;
|
||||||
|
|
||||||
|
for_each_available_child_of_node(nfc->dev->of_node, child) {
|
||||||
|
if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) {
|
||||||
|
|
||||||
|
if (chip->flash_node) {
|
||||||
|
dev_err(nfc->dev,
|
||||||
|
"Only one NAND chip supported!\n");
|
||||||
|
err = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->flash_node = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chip->flash_node) {
|
||||||
|
dev_err(nfc->dev, "NAND chip sub-node missing!\n");
|
||||||
|
err = -ENODEV;
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->dev_ready = vf610_nfc_dev_ready;
|
||||||
|
chip->cmdfunc = vf610_nfc_command;
|
||||||
|
chip->read_byte = vf610_nfc_read_byte;
|
||||||
|
chip->read_word = vf610_nfc_read_word;
|
||||||
|
chip->read_buf = vf610_nfc_read_buf;
|
||||||
|
chip->write_buf = vf610_nfc_write_buf;
|
||||||
|
chip->select_chip = vf610_nfc_select_chip;
|
||||||
|
|
||||||
|
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||||
|
|
||||||
|
init_completion(&nfc->cmd_done);
|
||||||
|
|
||||||
|
err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd);
|
||||||
|
if (err) {
|
||||||
|
dev_err(nfc->dev, "Error requesting IRQ!\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
vf610_nfc_preinit_controller(nfc);
|
||||||
|
|
||||||
|
/* first scan to find the device and get the page size */
|
||||||
|
if (nand_scan_ident(mtd, 1, NULL)) {
|
||||||
|
err = -ENXIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
vf610_nfc_init_controller(nfc);
|
||||||
|
|
||||||
|
/* Bad block options. */
|
||||||
|
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||||
|
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||||
|
|
||||||
|
/* Single buffer only, max 256 OOB minus ECC status */
|
||||||
|
if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
|
||||||
|
dev_err(nfc->dev, "Unsupported flash page size\n");
|
||||||
|
err = -ENXIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip->ecc.mode == NAND_ECC_HW) {
|
||||||
|
if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
|
||||||
|
dev_err(nfc->dev, "Unsupported flash with hwecc\n");
|
||||||
|
err = -ENXIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip->ecc.size != mtd->writesize) {
|
||||||
|
dev_err(nfc->dev, "Step size needs to be page size\n");
|
||||||
|
err = -ENXIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only 64 byte ECC layouts known */
|
||||||
|
if (mtd->oobsize > 64)
|
||||||
|
mtd->oobsize = 64;
|
||||||
|
|
||||||
|
if (chip->ecc.strength == 32) {
|
||||||
|
nfc->ecc_mode = ECC_60_BYTE;
|
||||||
|
chip->ecc.bytes = 60;
|
||||||
|
chip->ecc.layout = &vf610_nfc_ecc60;
|
||||||
|
} else if (chip->ecc.strength == 24) {
|
||||||
|
nfc->ecc_mode = ECC_45_BYTE;
|
||||||
|
chip->ecc.bytes = 45;
|
||||||
|
chip->ecc.layout = &vf610_nfc_ecc45;
|
||||||
|
} else {
|
||||||
|
dev_err(nfc->dev, "Unsupported ECC strength\n");
|
||||||
|
err = -ENXIO;
|
||||||
|
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;
|
||||||
|
|
||||||
|
chip->ecc.size = PAGE_2K;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* second phase scan */
|
||||||
|
if (nand_scan_tail(mtd)) {
|
||||||
|
err = -ENXIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, mtd);
|
||||||
|
|
||||||
|
/* Register device in MTD */
|
||||||
|
return mtd_device_parse_register(mtd, NULL,
|
||||||
|
&(struct mtd_part_parser_data){
|
||||||
|
.of_node = chip->flash_node,
|
||||||
|
},
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
|
error:
|
||||||
|
of_node_put(chip->flash_node);
|
||||||
|
err_clk:
|
||||||
|
clk_disable_unprepare(nfc->clk);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vf610_nfc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
|
||||||
|
nand_release(mtd);
|
||||||
|
clk_disable_unprepare(nfc->clk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int vf610_nfc_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
|
||||||
|
clk_disable_unprepare(nfc->clk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vf610_nfc_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||||
|
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||||
|
|
||||||
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
|
||||||
|
clk_prepare_enable(nfc->clk);
|
||||||
|
|
||||||
|
vf610_nfc_preinit_controller(nfc);
|
||||||
|
vf610_nfc_init_controller(nfc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(vf610_nfc_pm_ops, vf610_nfc_suspend, vf610_nfc_resume);
|
||||||
|
|
||||||
|
static struct platform_driver vf610_nfc_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.of_match_table = vf610_nfc_dt_ids,
|
||||||
|
.pm = &vf610_nfc_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = vf610_nfc_probe,
|
||||||
|
.remove = vf610_nfc_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(vf610_nfc_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
|
||||||
|
MODULE_DESCRIPTION("Freescale VF610/MPC5125 NFC MTD NAND driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -29,23 +29,33 @@ static int parse_ofpart_partitions(struct mtd_info *master,
|
||||||
struct mtd_partition **pparts,
|
struct mtd_partition **pparts,
|
||||||
struct mtd_part_parser_data *data)
|
struct mtd_part_parser_data *data)
|
||||||
{
|
{
|
||||||
struct device_node *node;
|
struct device_node *mtd_node;
|
||||||
|
struct device_node *ofpart_node;
|
||||||
const char *partname;
|
const char *partname;
|
||||||
struct device_node *pp;
|
struct device_node *pp;
|
||||||
int nr_parts, i;
|
int nr_parts, i, ret = 0;
|
||||||
|
bool dedicated = true;
|
||||||
|
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
node = data->of_node;
|
mtd_node = data->of_node;
|
||||||
if (!node)
|
if (!mtd_node)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
ofpart_node = of_get_child_by_name(mtd_node, "partitions");
|
||||||
|
if (!ofpart_node) {
|
||||||
|
pr_warn("%s: 'partitions' subnode not found on %s. Trying to parse direct subnodes as partitions.\n",
|
||||||
|
master->name, mtd_node->full_name);
|
||||||
|
ofpart_node = mtd_node;
|
||||||
|
dedicated = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* First count the subnodes */
|
/* First count the subnodes */
|
||||||
nr_parts = 0;
|
nr_parts = 0;
|
||||||
for_each_child_of_node(node, pp) {
|
for_each_child_of_node(ofpart_node, pp) {
|
||||||
if (node_has_compatible(pp))
|
if (!dedicated && node_has_compatible(pp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nr_parts++;
|
nr_parts++;
|
||||||
|
@ -59,22 +69,36 @@ static int parse_ofpart_partitions(struct mtd_info *master,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
for_each_child_of_node(node, pp) {
|
for_each_child_of_node(ofpart_node, pp) {
|
||||||
const __be32 *reg;
|
const __be32 *reg;
|
||||||
int len;
|
int len;
|
||||||
int a_cells, s_cells;
|
int a_cells, s_cells;
|
||||||
|
|
||||||
if (node_has_compatible(pp))
|
if (!dedicated && node_has_compatible(pp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
reg = of_get_property(pp, "reg", &len);
|
reg = of_get_property(pp, "reg", &len);
|
||||||
if (!reg) {
|
if (!reg) {
|
||||||
nr_parts--;
|
if (dedicated) {
|
||||||
continue;
|
pr_debug("%s: ofpart partition %s (%s) missing reg property.\n",
|
||||||
|
master->name, pp->full_name,
|
||||||
|
mtd_node->full_name);
|
||||||
|
goto ofpart_fail;
|
||||||
|
} else {
|
||||||
|
nr_parts--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a_cells = of_n_addr_cells(pp);
|
a_cells = of_n_addr_cells(pp);
|
||||||
s_cells = of_n_size_cells(pp);
|
s_cells = of_n_size_cells(pp);
|
||||||
|
if (len / 4 != a_cells + s_cells) {
|
||||||
|
pr_debug("%s: ofpart partition %s (%s) error parsing reg property.\n",
|
||||||
|
master->name, pp->full_name,
|
||||||
|
mtd_node->full_name);
|
||||||
|
goto ofpart_fail;
|
||||||
|
}
|
||||||
|
|
||||||
(*pparts)[i].offset = of_read_number(reg, a_cells);
|
(*pparts)[i].offset = of_read_number(reg, a_cells);
|
||||||
(*pparts)[i].size = of_read_number(reg + a_cells, s_cells);
|
(*pparts)[i].size = of_read_number(reg + a_cells, s_cells);
|
||||||
|
|
||||||
|
@ -92,15 +116,20 @@ static int parse_ofpart_partitions(struct mtd_info *master,
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!i) {
|
if (!nr_parts)
|
||||||
of_node_put(pp);
|
goto ofpart_none;
|
||||||
pr_err("No valid partition found on %s\n", node->full_name);
|
|
||||||
kfree(*pparts);
|
|
||||||
*pparts = NULL;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nr_parts;
|
return nr_parts;
|
||||||
|
|
||||||
|
ofpart_fail:
|
||||||
|
pr_err("%s: error parsing ofpart partition %s (%s)\n",
|
||||||
|
master->name, pp->full_name, mtd_node->full_name);
|
||||||
|
ret = -EINVAL;
|
||||||
|
ofpart_none:
|
||||||
|
of_node_put(pp);
|
||||||
|
kfree(*pparts);
|
||||||
|
*pparts = NULL;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct mtd_part_parser ofpart_parser = {
|
static struct mtd_part_parser ofpart_parser = {
|
||||||
|
|
|
@ -60,9 +60,8 @@ static int generic_onenand_probe(struct platform_device *pdev)
|
||||||
info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL;
|
info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL;
|
||||||
info->onenand.irq = platform_get_irq(pdev, 0);
|
info->onenand.irq = platform_get_irq(pdev, 0);
|
||||||
|
|
||||||
info->mtd.name = dev_name(&pdev->dev);
|
info->mtd.dev.parent = &pdev->dev;
|
||||||
info->mtd.priv = &info->onenand;
|
info->mtd.priv = &info->onenand;
|
||||||
info->mtd.owner = THIS_MODULE;
|
|
||||||
|
|
||||||
if (onenand_scan(&info->mtd, 1)) {
|
if (onenand_scan(&info->mtd, 1)) {
|
||||||
err = -ENXIO;
|
err = -ENXIO;
|
||||||
|
|
|
@ -710,9 +710,7 @@ static int omap2_onenand_probe(struct platform_device *pdev)
|
||||||
c->onenand.base, c->freq);
|
c->onenand.base, c->freq);
|
||||||
|
|
||||||
c->pdev = pdev;
|
c->pdev = pdev;
|
||||||
c->mtd.name = dev_name(&pdev->dev);
|
|
||||||
c->mtd.priv = &c->onenand;
|
c->mtd.priv = &c->onenand;
|
||||||
c->mtd.owner = THIS_MODULE;
|
|
||||||
|
|
||||||
c->mtd.dev.parent = &pdev->dev;
|
c->mtd.dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
|
|
@ -864,7 +864,6 @@ static int s3c_onenand_probe(struct platform_device *pdev)
|
||||||
this = (struct onenand_chip *) &mtd[1];
|
this = (struct onenand_chip *) &mtd[1];
|
||||||
mtd->priv = this;
|
mtd->priv = this;
|
||||||
mtd->dev.parent = &pdev->dev;
|
mtd->dev.parent = &pdev->dev;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
onenand->pdev = pdev;
|
onenand->pdev = pdev;
|
||||||
onenand->type = platform_get_device_id(pdev)->driver_data;
|
onenand->type = platform_get_device_id(pdev)->driver_data;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ config MTD_SPI_NOR_USE_4K_SECTORS
|
||||||
|
|
||||||
config SPI_FSL_QUADSPI
|
config SPI_FSL_QUADSPI
|
||||||
tristate "Freescale Quad SPI controller"
|
tristate "Freescale Quad SPI controller"
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC || COMPILE_TEST
|
||||||
|
depends on HAS_IOMEM
|
||||||
help
|
help
|
||||||
This enables support for the Quad SPI controller in master mode.
|
This enables support for the Quad SPI controller in master mode.
|
||||||
This controller does not support generic SPI. It only supports
|
This controller does not support generic SPI. It only supports
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/mtd/spi-nor.h>
|
#include <linux/mtd/spi-nor.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/pm_qos.h>
|
#include <linux/pm_qos.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
/* Controller needs driver to swap endian */
|
/* Controller needs driver to swap endian */
|
||||||
#define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0)
|
#define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0)
|
||||||
|
@ -154,15 +155,15 @@
|
||||||
#define LUT_MODE 4
|
#define LUT_MODE 4
|
||||||
#define LUT_MODE2 5
|
#define LUT_MODE2 5
|
||||||
#define LUT_MODE4 6
|
#define LUT_MODE4 6
|
||||||
#define LUT_READ 7
|
#define LUT_FSL_READ 7
|
||||||
#define LUT_WRITE 8
|
#define LUT_FSL_WRITE 8
|
||||||
#define LUT_JMP_ON_CS 9
|
#define LUT_JMP_ON_CS 9
|
||||||
#define LUT_ADDR_DDR 10
|
#define LUT_ADDR_DDR 10
|
||||||
#define LUT_MODE_DDR 11
|
#define LUT_MODE_DDR 11
|
||||||
#define LUT_MODE2_DDR 12
|
#define LUT_MODE2_DDR 12
|
||||||
#define LUT_MODE4_DDR 13
|
#define LUT_MODE4_DDR 13
|
||||||
#define LUT_READ_DDR 14
|
#define LUT_FSL_READ_DDR 14
|
||||||
#define LUT_WRITE_DDR 15
|
#define LUT_FSL_WRITE_DDR 15
|
||||||
#define LUT_DATA_LEARN 16
|
#define LUT_DATA_LEARN 16
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -259,7 +260,6 @@ static struct fsl_qspi_devtype_data imx6ul_data = {
|
||||||
|
|
||||||
#define FSL_QSPI_MAX_CHIP 4
|
#define FSL_QSPI_MAX_CHIP 4
|
||||||
struct fsl_qspi {
|
struct fsl_qspi {
|
||||||
struct mtd_info mtd[FSL_QSPI_MAX_CHIP];
|
|
||||||
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
|
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
|
||||||
void __iomem *iobase;
|
void __iomem *iobase;
|
||||||
void __iomem *ahb_addr;
|
void __iomem *ahb_addr;
|
||||||
|
@ -366,7 +366,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
||||||
|
|
||||||
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
||||||
base + QUADSPI_LUT(lut_base));
|
base + QUADSPI_LUT(lut_base));
|
||||||
writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo),
|
writel(LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
|
||||||
base + QUADSPI_LUT(lut_base + 1));
|
base + QUADSPI_LUT(lut_base + 1));
|
||||||
|
|
||||||
/* Write enable */
|
/* Write enable */
|
||||||
|
@ -387,11 +387,11 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
||||||
|
|
||||||
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
||||||
base + QUADSPI_LUT(lut_base));
|
base + QUADSPI_LUT(lut_base));
|
||||||
writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
|
writel(LUT0(FSL_WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
|
||||||
|
|
||||||
/* Read Status */
|
/* Read Status */
|
||||||
lut_base = SEQID_RDSR * 4;
|
lut_base = SEQID_RDSR * 4;
|
||||||
writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1),
|
writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(FSL_READ, PAD1, 0x1),
|
||||||
base + QUADSPI_LUT(lut_base));
|
base + QUADSPI_LUT(lut_base));
|
||||||
|
|
||||||
/* Erase a sector */
|
/* Erase a sector */
|
||||||
|
@ -410,17 +410,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
||||||
|
|
||||||
/* READ ID */
|
/* READ ID */
|
||||||
lut_base = SEQID_RDID * 4;
|
lut_base = SEQID_RDID * 4;
|
||||||
writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8),
|
writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(FSL_READ, PAD1, 0x8),
|
||||||
base + QUADSPI_LUT(lut_base));
|
base + QUADSPI_LUT(lut_base));
|
||||||
|
|
||||||
/* Write Register */
|
/* Write Register */
|
||||||
lut_base = SEQID_WRSR * 4;
|
lut_base = SEQID_WRSR * 4;
|
||||||
writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2),
|
writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(FSL_WRITE, PAD1, 0x2),
|
||||||
base + QUADSPI_LUT(lut_base));
|
base + QUADSPI_LUT(lut_base));
|
||||||
|
|
||||||
/* Read Configuration Register */
|
/* Read Configuration Register */
|
||||||
lut_base = SEQID_RDCR * 4;
|
lut_base = SEQID_RDCR * 4;
|
||||||
writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1),
|
writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(FSL_READ, PAD1, 0x1),
|
||||||
base + QUADSPI_LUT(lut_base));
|
base + QUADSPI_LUT(lut_base));
|
||||||
|
|
||||||
/* Write disable */
|
/* Write disable */
|
||||||
|
@ -798,8 +798,7 @@ static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
|
static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
||||||
int write_enable)
|
|
||||||
{
|
{
|
||||||
struct fsl_qspi *q = nor->priv;
|
struct fsl_qspi *q = nor->priv;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -870,7 +869,7 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(q->dev, "cmd [%x],read from 0x%p, len:%d\n",
|
dev_dbg(q->dev, "cmd [%x],read from %p, len:%zd\n",
|
||||||
cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
|
cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
|
||||||
len);
|
len);
|
||||||
|
|
||||||
|
@ -888,7 +887,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
|
dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
|
||||||
nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);
|
nor->mtd.erasesize / 1024, q->chip_base_addr, (u32)offs);
|
||||||
|
|
||||||
ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
|
ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1006,19 +1005,16 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* iterate the subnodes. */
|
/* iterate the subnodes. */
|
||||||
for_each_available_child_of_node(dev->of_node, np) {
|
for_each_available_child_of_node(dev->of_node, np) {
|
||||||
char modalias[40];
|
|
||||||
|
|
||||||
/* skip the holes */
|
/* skip the holes */
|
||||||
if (!q->has_second_chip)
|
if (!q->has_second_chip)
|
||||||
i *= 2;
|
i *= 2;
|
||||||
|
|
||||||
nor = &q->nor[i];
|
nor = &q->nor[i];
|
||||||
mtd = &q->mtd[i];
|
mtd = &nor->mtd;
|
||||||
|
|
||||||
nor->mtd = mtd;
|
|
||||||
nor->dev = dev;
|
nor->dev = dev;
|
||||||
|
nor->flash_node = np;
|
||||||
nor->priv = q;
|
nor->priv = q;
|
||||||
mtd->priv = nor;
|
|
||||||
|
|
||||||
/* fill the hooks */
|
/* fill the hooks */
|
||||||
nor->read_reg = fsl_qspi_read_reg;
|
nor->read_reg = fsl_qspi_read_reg;
|
||||||
|
@ -1030,10 +1026,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||||
nor->prepare = fsl_qspi_prep;
|
nor->prepare = fsl_qspi_prep;
|
||||||
nor->unprepare = fsl_qspi_unprep;
|
nor->unprepare = fsl_qspi_unprep;
|
||||||
|
|
||||||
ret = of_modalias_node(np, modalias, sizeof(modalias));
|
|
||||||
if (ret < 0)
|
|
||||||
goto mutex_failed;
|
|
||||||
|
|
||||||
ret = of_property_read_u32(np, "spi-max-frequency",
|
ret = of_property_read_u32(np, "spi-max-frequency",
|
||||||
&q->clk_rate);
|
&q->clk_rate);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -1042,7 +1034,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||||
/* set the chip address for READID */
|
/* set the chip address for READID */
|
||||||
fsl_qspi_set_base_addr(q, nor);
|
fsl_qspi_set_base_addr(q, nor);
|
||||||
|
|
||||||
ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
|
ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto mutex_failed;
|
goto mutex_failed;
|
||||||
|
|
||||||
|
@ -1087,7 +1079,7 @@ last_init_failed:
|
||||||
/* skip the holes */
|
/* skip the holes */
|
||||||
if (!q->has_second_chip)
|
if (!q->has_second_chip)
|
||||||
i *= 2;
|
i *= 2;
|
||||||
mtd_device_unregister(&q->mtd[i]);
|
mtd_device_unregister(&q->nor[i].mtd);
|
||||||
}
|
}
|
||||||
mutex_failed:
|
mutex_failed:
|
||||||
mutex_destroy(&q->lock);
|
mutex_destroy(&q->lock);
|
||||||
|
@ -1107,7 +1099,7 @@ static int fsl_qspi_remove(struct platform_device *pdev)
|
||||||
/* skip the holes */
|
/* skip the holes */
|
||||||
if (!q->has_second_chip)
|
if (!q->has_second_chip)
|
||||||
i *= 2;
|
i *= 2;
|
||||||
mtd_device_unregister(&q->mtd[i]);
|
mtd_device_unregister(&q->nor[i].mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* disable the hardware */
|
/* disable the hardware */
|
||||||
|
|
|
@ -60,7 +60,6 @@ struct nxp_spifi {
|
||||||
struct clk *clk_reg;
|
struct clk *clk_reg;
|
||||||
void __iomem *io_base;
|
void __iomem *io_base;
|
||||||
void __iomem *flash_base;
|
void __iomem *flash_base;
|
||||||
struct mtd_info mtd;
|
|
||||||
struct spi_nor nor;
|
struct spi_nor nor;
|
||||||
bool memory_mode;
|
bool memory_mode;
|
||||||
u32 mcmd;
|
u32 mcmd;
|
||||||
|
@ -150,8 +149,7 @@ static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
||||||
return nxp_spifi_wait_for_cmd(spifi);
|
return nxp_spifi_wait_for_cmd(spifi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
||||||
int len, int write_enable)
|
|
||||||
{
|
{
|
||||||
struct nxp_spifi *spifi = nor->priv;
|
struct nxp_spifi *spifi = nor->priv;
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
|
@ -331,9 +329,8 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
|
||||||
|
|
||||||
writel(ctrl, spifi->io_base + SPIFI_CTRL);
|
writel(ctrl, spifi->io_base + SPIFI_CTRL);
|
||||||
|
|
||||||
spifi->mtd.priv = &spifi->nor;
|
|
||||||
spifi->nor.mtd = &spifi->mtd;
|
|
||||||
spifi->nor.dev = spifi->dev;
|
spifi->nor.dev = spifi->dev;
|
||||||
|
spifi->nor.flash_node = np;
|
||||||
spifi->nor.priv = spifi;
|
spifi->nor.priv = spifi;
|
||||||
spifi->nor.read = nxp_spifi_read;
|
spifi->nor.read = nxp_spifi_read;
|
||||||
spifi->nor.write = nxp_spifi_write;
|
spifi->nor.write = nxp_spifi_write;
|
||||||
|
@ -365,7 +362,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
|
||||||
}
|
}
|
||||||
|
|
||||||
ppdata.of_node = np;
|
ppdata.of_node = np;
|
||||||
ret = mtd_device_parse_register(&spifi->mtd, NULL, &ppdata, NULL, 0);
|
ret = mtd_device_parse_register(&spifi->nor.mtd, NULL, &ppdata, NULL, 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(spifi->dev, "mtd device parse failed\n");
|
dev_err(spifi->dev, "mtd device parse failed\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -454,7 +451,7 @@ static int nxp_spifi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct nxp_spifi *spifi = platform_get_drvdata(pdev);
|
struct nxp_spifi *spifi = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
mtd_device_unregister(&spifi->mtd);
|
mtd_device_unregister(&spifi->nor.mtd);
|
||||||
clk_disable_unprepare(spifi->clk_spifi);
|
clk_disable_unprepare(spifi->clk_spifi);
|
||||||
clk_disable_unprepare(spifi->clk_reg);
|
clk_disable_unprepare(spifi->clk_reg);
|
||||||
|
|
||||||
|
|
|
@ -16,15 +16,26 @@
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
#include <linux/mtd/cfi.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/spi/flash.h>
|
#include <linux/spi/flash.h>
|
||||||
#include <linux/mtd/spi-nor.h>
|
#include <linux/mtd/spi-nor.h>
|
||||||
|
|
||||||
/* Define max times to check status register before we give up. */
|
/* Define max times to check status register before we give up. */
|
||||||
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
|
|
||||||
|
/*
|
||||||
|
* For everything but full-chip erase; probably could be much smaller, but kept
|
||||||
|
* around for safety for now
|
||||||
|
*/
|
||||||
|
#define DEFAULT_READY_WAIT_JIFFIES (40UL * HZ)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For full-chip erase, calibrated to a 2MB flash (M25P16); should be scaled up
|
||||||
|
* for larger flash
|
||||||
|
*/
|
||||||
|
#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ)
|
||||||
|
|
||||||
#define SPI_NOR_MAX_ID_LEN 6
|
#define SPI_NOR_MAX_ID_LEN 6
|
||||||
|
|
||||||
|
@ -145,7 +156,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
|
||||||
static inline int write_sr(struct spi_nor *nor, u8 val)
|
static inline int write_sr(struct spi_nor *nor, u8 val)
|
||||||
{
|
{
|
||||||
nor->cmd_buf[0] = val;
|
nor->cmd_buf[0] = val;
|
||||||
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
|
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -154,7 +165,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val)
|
||||||
*/
|
*/
|
||||||
static inline int write_enable(struct spi_nor *nor)
|
static inline int write_enable(struct spi_nor *nor)
|
||||||
{
|
{
|
||||||
return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
|
return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -162,7 +173,7 @@ static inline int write_enable(struct spi_nor *nor)
|
||||||
*/
|
*/
|
||||||
static inline int write_disable(struct spi_nor *nor)
|
static inline int write_disable(struct spi_nor *nor)
|
||||||
{
|
{
|
||||||
return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
|
return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||||
|
@ -179,16 +190,16 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
|
||||||
u8 cmd;
|
u8 cmd;
|
||||||
|
|
||||||
switch (JEDEC_MFR(info)) {
|
switch (JEDEC_MFR(info)) {
|
||||||
case CFI_MFR_ST: /* Micron, actually */
|
case SNOR_MFR_MICRON:
|
||||||
/* Some Micron need WREN command; all will accept it */
|
/* Some Micron need WREN command; all will accept it */
|
||||||
need_wren = true;
|
need_wren = true;
|
||||||
case CFI_MFR_MACRONIX:
|
case SNOR_MFR_MACRONIX:
|
||||||
case 0xEF /* winbond */:
|
case SNOR_MFR_WINBOND:
|
||||||
if (need_wren)
|
if (need_wren)
|
||||||
write_enable(nor);
|
write_enable(nor);
|
||||||
|
|
||||||
cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
|
cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
|
||||||
status = nor->write_reg(nor, cmd, NULL, 0, 0);
|
status = nor->write_reg(nor, cmd, NULL, 0);
|
||||||
if (need_wren)
|
if (need_wren)
|
||||||
write_disable(nor);
|
write_disable(nor);
|
||||||
|
|
||||||
|
@ -196,7 +207,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
|
||||||
default:
|
default:
|
||||||
/* Spansion style */
|
/* Spansion style */
|
||||||
nor->cmd_buf[0] = enable << 7;
|
nor->cmd_buf[0] = enable << 7;
|
||||||
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
|
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static inline int spi_nor_sr_ready(struct spi_nor *nor)
|
static inline int spi_nor_sr_ready(struct spi_nor *nor)
|
||||||
|
@ -233,12 +244,13 @@ static int spi_nor_ready(struct spi_nor *nor)
|
||||||
* Service routine to read status register until ready, or timeout occurs.
|
* Service routine to read status register until ready, or timeout occurs.
|
||||||
* Returns non-zero if error.
|
* Returns non-zero if error.
|
||||||
*/
|
*/
|
||||||
static int spi_nor_wait_till_ready(struct spi_nor *nor)
|
static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,
|
||||||
|
unsigned long timeout_jiffies)
|
||||||
{
|
{
|
||||||
unsigned long deadline;
|
unsigned long deadline;
|
||||||
int timeout = 0, ret;
|
int timeout = 0, ret;
|
||||||
|
|
||||||
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
|
deadline = jiffies + timeout_jiffies;
|
||||||
|
|
||||||
while (!timeout) {
|
while (!timeout) {
|
||||||
if (time_after_eq(jiffies, deadline))
|
if (time_after_eq(jiffies, deadline))
|
||||||
|
@ -258,6 +270,12 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spi_nor_wait_till_ready(struct spi_nor *nor)
|
||||||
|
{
|
||||||
|
return spi_nor_wait_till_ready_with_timeout(nor,
|
||||||
|
DEFAULT_READY_WAIT_JIFFIES);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Erase the whole flash memory
|
* Erase the whole flash memory
|
||||||
*
|
*
|
||||||
|
@ -265,9 +283,9 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)
|
||||||
*/
|
*/
|
||||||
static int erase_chip(struct spi_nor *nor)
|
static int erase_chip(struct spi_nor *nor)
|
||||||
{
|
{
|
||||||
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
|
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));
|
||||||
|
|
||||||
return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
|
return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
|
static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
|
||||||
|
@ -321,6 +339,8 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
|
|
||||||
/* whole-chip erase? */
|
/* whole-chip erase? */
|
||||||
if (len == mtd->size) {
|
if (len == mtd->size) {
|
||||||
|
unsigned long timeout;
|
||||||
|
|
||||||
write_enable(nor);
|
write_enable(nor);
|
||||||
|
|
||||||
if (erase_chip(nor)) {
|
if (erase_chip(nor)) {
|
||||||
|
@ -328,7 +348,16 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
goto erase_err;
|
goto erase_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = spi_nor_wait_till_ready(nor);
|
/*
|
||||||
|
* Scale the timeout linearly with the size of the flash, with
|
||||||
|
* a minimum calibrated to an old 2MB flash. We could try to
|
||||||
|
* pull these from CFI/SFDP, but these values should be good
|
||||||
|
* enough for now.
|
||||||
|
*/
|
||||||
|
timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
|
||||||
|
CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
|
||||||
|
(unsigned long)(mtd->size / SZ_2M));
|
||||||
|
ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto erase_err;
|
goto erase_err;
|
||||||
|
|
||||||
|
@ -371,72 +400,171 @@ erase_err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
|
||||||
|
uint64_t *len)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = nor->mtd;
|
struct mtd_info *mtd = &nor->mtd;
|
||||||
uint32_t offset = ofs;
|
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
||||||
uint8_t status_old, status_new;
|
int shift = ffs(mask) - 1;
|
||||||
int ret = 0;
|
int pow;
|
||||||
|
|
||||||
status_old = read_sr(nor);
|
if (!(sr & mask)) {
|
||||||
|
/* No protection */
|
||||||
if (offset < mtd->size - (mtd->size / 2))
|
*ofs = 0;
|
||||||
status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
|
*len = 0;
|
||||||
else if (offset < mtd->size - (mtd->size / 4))
|
} else {
|
||||||
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
|
pow = ((sr & mask) ^ mask) >> shift;
|
||||||
else if (offset < mtd->size - (mtd->size / 8))
|
*len = mtd->size >> pow;
|
||||||
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
|
*ofs = mtd->size - *len;
|
||||||
else if (offset < mtd->size - (mtd->size / 16))
|
|
||||||
status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
|
|
||||||
else if (offset < mtd->size - (mtd->size / 32))
|
|
||||||
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
|
|
||||||
else if (offset < mtd->size - (mtd->size / 64))
|
|
||||||
status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
|
|
||||||
else
|
|
||||||
status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
|
|
||||||
|
|
||||||
/* Only modify protection if it will not unlock other areas */
|
|
||||||
if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) >
|
|
||||||
(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
|
|
||||||
write_enable(nor);
|
|
||||||
ret = write_sr(nor, status_new);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
/*
|
||||||
|
* Return 1 if the entire region is locked, 0 otherwise
|
||||||
|
*/
|
||||||
|
static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
|
||||||
|
u8 sr)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = nor->mtd;
|
loff_t lock_offs;
|
||||||
uint32_t offset = ofs;
|
uint64_t lock_len;
|
||||||
uint8_t status_old, status_new;
|
|
||||||
int ret = 0;
|
stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
|
||||||
|
|
||||||
|
return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* (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)
|
||||||
|
*
|
||||||
|
* Sample table portion for 8MB flash (Winbond w25q64fw):
|
||||||
|
*
|
||||||
|
* SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* X | X | 0 | 0 | 0 | NONE | NONE
|
||||||
|
* 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64
|
||||||
|
* 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32
|
||||||
|
* 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16
|
||||||
|
* 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* Returns negative on errors, 0 on success.
|
||||||
|
*/
|
||||||
|
static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = &nor->mtd;
|
||||||
|
u8 status_old, status_new;
|
||||||
|
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
||||||
|
u8 shift = ffs(mask) - 1, pow, val;
|
||||||
|
|
||||||
status_old = read_sr(nor);
|
status_old = read_sr(nor);
|
||||||
|
|
||||||
if (offset+len > mtd->size - (mtd->size / 64))
|
/* SPI NOR always locks to the end */
|
||||||
status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0);
|
if (ofs + len != mtd->size) {
|
||||||
else if (offset+len > mtd->size - (mtd->size / 32))
|
/* Does combined region extend to end? */
|
||||||
status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
|
if (!stm_is_locked_sr(nor, ofs + len, mtd->size - ofs - len,
|
||||||
else if (offset+len > mtd->size - (mtd->size / 16))
|
status_old))
|
||||||
status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
|
return -EINVAL;
|
||||||
else if (offset+len > mtd->size - (mtd->size / 8))
|
len = mtd->size - ofs;
|
||||||
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
|
|
||||||
else if (offset+len > mtd->size - (mtd->size / 4))
|
|
||||||
status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
|
|
||||||
else if (offset+len > mtd->size - (mtd->size / 2))
|
|
||||||
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
|
|
||||||
else
|
|
||||||
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
|
|
||||||
|
|
||||||
/* Only modify protection if it will not lock other areas */
|
|
||||||
if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) <
|
|
||||||
(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
|
|
||||||
write_enable(nor);
|
|
||||||
ret = write_sr(nor, status_new);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
/*
|
||||||
|
* Need smallest pow such that:
|
||||||
|
*
|
||||||
|
* 1 / (2^pow) <= (len / size)
|
||||||
|
*
|
||||||
|
* so (assuming power-of-2 size) we do:
|
||||||
|
*
|
||||||
|
* pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
|
||||||
|
*/
|
||||||
|
pow = ilog2(mtd->size) - ilog2(len);
|
||||||
|
val = mask - (pow << shift);
|
||||||
|
if (val & ~mask)
|
||||||
|
return -EINVAL;
|
||||||
|
/* Don't "lock" with no region! */
|
||||||
|
if (!(val & mask))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
status_new = (status_old & ~mask) | val;
|
||||||
|
|
||||||
|
/* Only modify protection if it will not unlock other areas */
|
||||||
|
if ((status_new & mask) <= (status_old & mask))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
write_enable(nor);
|
||||||
|
return write_sr(nor, status_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlock a region of the flash. See stm_lock() for more info
|
||||||
|
*
|
||||||
|
* Returns negative on errors, 0 on success.
|
||||||
|
*/
|
||||||
|
static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = &nor->mtd;
|
||||||
|
uint8_t status_old, status_new;
|
||||||
|
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
||||||
|
u8 shift = ffs(mask) - 1, pow, val;
|
||||||
|
|
||||||
|
status_old = read_sr(nor);
|
||||||
|
|
||||||
|
/* Cannot unlock; would unlock larger region than requested */
|
||||||
|
if (stm_is_locked_sr(nor, status_old, ofs - mtd->erasesize,
|
||||||
|
mtd->erasesize))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Need largest pow such that:
|
||||||
|
*
|
||||||
|
* 1 / (2^pow) >= (len / size)
|
||||||
|
*
|
||||||
|
* so (assuming power-of-2 size) we do:
|
||||||
|
*
|
||||||
|
* 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) {
|
||||||
|
val = 0; /* fully unlocked */
|
||||||
|
} else {
|
||||||
|
val = mask - (pow << shift);
|
||||||
|
/* Some power-of-two sizes are not supported */
|
||||||
|
if (val & ~mask)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_new = (status_old & ~mask) | val;
|
||||||
|
|
||||||
|
/* Only modify protection if it will not lock other areas */
|
||||||
|
if ((status_new & mask) >= (status_old & mask))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
write_enable(nor);
|
||||||
|
return write_sr(nor, status_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a region of the flash is (completely) locked. See stm_lock() for
|
||||||
|
* more info.
|
||||||
|
*
|
||||||
|
* Returns 1 if entire region is locked, 0 if any portion is unlocked, and
|
||||||
|
* negative on errors.
|
||||||
|
*/
|
||||||
|
static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = read_sr(nor);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
return stm_is_locked_sr(nor, ofs, len, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
|
@ -469,6 +597,21 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
|
{
|
||||||
|
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = nor->flash_is_locked(nor, ofs, len);
|
||||||
|
|
||||||
|
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Used when the "_ext_id" is two bytes at most */
|
/* Used when the "_ext_id" is two bytes at most */
|
||||||
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||||
.id = { \
|
.id = { \
|
||||||
|
@ -585,6 +728,7 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
|
|
||||||
/* Micron */
|
/* Micron */
|
||||||
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
|
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
|
||||||
|
{ "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
|
||||||
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
|
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||||
{ "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
|
{ "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||||
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
|
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
|
||||||
|
@ -618,12 +762,13 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
|
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
|
||||||
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
|
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
|
||||||
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
|
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
|
||||||
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
{ "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
|
{ "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) },
|
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
|
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
|
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
{ "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K) },
|
{ "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) },
|
||||||
|
|
||||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
||||||
|
@ -635,6 +780,7 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
|
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
|
||||||
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
|
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
|
||||||
{ "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) },
|
{ "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) },
|
||||||
|
{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
||||||
{ "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
|
{ "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
|
||||||
|
|
||||||
|
@ -683,10 +829,11 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
|
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
|
||||||
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
|
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
|
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) },
|
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
{ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K) },
|
{ "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) },
|
||||||
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
||||||
|
@ -868,8 +1015,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
|
||||||
val = read_sr(nor);
|
val = read_sr(nor);
|
||||||
write_enable(nor);
|
write_enable(nor);
|
||||||
|
|
||||||
nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
|
write_sr(nor, val | SR_QUAD_EN_MX);
|
||||||
nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
|
|
||||||
|
|
||||||
if (spi_nor_wait_till_ready(nor))
|
if (spi_nor_wait_till_ready(nor))
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -894,7 +1040,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val)
|
||||||
nor->cmd_buf[0] = val & 0xff;
|
nor->cmd_buf[0] = val & 0xff;
|
||||||
nor->cmd_buf[1] = (val >> 8);
|
nor->cmd_buf[1] = (val >> 8);
|
||||||
|
|
||||||
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0);
|
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spansion_quad_enable(struct spi_nor *nor)
|
static int spansion_quad_enable(struct spi_nor *nor)
|
||||||
|
@ -936,7 +1082,7 @@ static int micron_quad_enable(struct spi_nor *nor)
|
||||||
|
|
||||||
/* set EVCR, enable quad I/O */
|
/* set EVCR, enable quad I/O */
|
||||||
nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
|
nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
|
||||||
ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0);
|
ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(nor->dev, "error while writing EVCR register\n");
|
dev_err(nor->dev, "error while writing EVCR register\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -965,14 +1111,14 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
switch (JEDEC_MFR(info)) {
|
switch (JEDEC_MFR(info)) {
|
||||||
case CFI_MFR_MACRONIX:
|
case SNOR_MFR_MACRONIX:
|
||||||
status = macronix_quad_enable(nor);
|
status = macronix_quad_enable(nor);
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(nor->dev, "Macronix quad-read not enabled\n");
|
dev_err(nor->dev, "Macronix quad-read not enabled\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
case CFI_MFR_ST:
|
case SNOR_MFR_MICRON:
|
||||||
status = micron_quad_enable(nor);
|
status = micron_quad_enable(nor);
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(nor->dev, "Micron quad-read not enabled\n");
|
dev_err(nor->dev, "Micron quad-read not enabled\n");
|
||||||
|
@ -1004,8 +1150,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||||
{
|
{
|
||||||
const struct flash_info *info = NULL;
|
const struct flash_info *info = NULL;
|
||||||
struct device *dev = nor->dev;
|
struct device *dev = nor->dev;
|
||||||
struct mtd_info *mtd = nor->mtd;
|
struct mtd_info *mtd = &nor->mtd;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = nor->flash_node;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -1048,19 +1194,21 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||||
mutex_init(&nor->lock);
|
mutex_init(&nor->lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Atmel, SST and Intel/Numonyx serial nor tend to power
|
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
|
||||||
* up with the software protection bits set
|
* with the software protection bits set
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (JEDEC_MFR(info) == CFI_MFR_ATMEL ||
|
if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
|
||||||
JEDEC_MFR(info) == CFI_MFR_INTEL ||
|
JEDEC_MFR(info) == SNOR_MFR_INTEL ||
|
||||||
JEDEC_MFR(info) == CFI_MFR_SST) {
|
JEDEC_MFR(info) == SNOR_MFR_SST ||
|
||||||
|
JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
|
||||||
write_enable(nor);
|
write_enable(nor);
|
||||||
write_sr(nor, 0);
|
write_sr(nor, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mtd->name)
|
if (!mtd->name)
|
||||||
mtd->name = dev_name(dev);
|
mtd->name = dev_name(dev);
|
||||||
|
mtd->priv = nor;
|
||||||
mtd->type = MTD_NORFLASH;
|
mtd->type = MTD_NORFLASH;
|
||||||
mtd->writesize = 1;
|
mtd->writesize = 1;
|
||||||
mtd->flags = MTD_CAP_NORFLASH;
|
mtd->flags = MTD_CAP_NORFLASH;
|
||||||
|
@ -1068,15 +1216,18 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||||
mtd->_erase = spi_nor_erase;
|
mtd->_erase = spi_nor_erase;
|
||||||
mtd->_read = spi_nor_read;
|
mtd->_read = spi_nor_read;
|
||||||
|
|
||||||
/* nor protection support for STmicro chips */
|
/* NOR protection support for STmicro/Micron chips and similar */
|
||||||
if (JEDEC_MFR(info) == CFI_MFR_ST) {
|
if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
|
||||||
|
JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
|
||||||
nor->flash_lock = stm_lock;
|
nor->flash_lock = stm_lock;
|
||||||
nor->flash_unlock = stm_unlock;
|
nor->flash_unlock = stm_unlock;
|
||||||
|
nor->flash_is_locked = stm_is_locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nor->flash_lock && nor->flash_unlock) {
|
if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) {
|
||||||
mtd->_lock = spi_nor_lock;
|
mtd->_lock = spi_nor_lock;
|
||||||
mtd->_unlock = spi_nor_unlock;
|
mtd->_unlock = spi_nor_unlock;
|
||||||
|
mtd->_is_locked = spi_nor_is_locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sst nor chips use AAI word program */
|
/* sst nor chips use AAI word program */
|
||||||
|
@ -1163,7 +1314,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||||
else if (mtd->size > 0x1000000) {
|
else if (mtd->size > 0x1000000) {
|
||||||
/* enable 4-byte addressing if the device exceeds 16MiB */
|
/* enable 4-byte addressing if the device exceeds 16MiB */
|
||||||
nor->addr_width = 4;
|
nor->addr_width = 4;
|
||||||
if (JEDEC_MFR(info) == CFI_MFR_AMD) {
|
if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
|
||||||
/* Dedicated 4-byte command set */
|
/* Dedicated 4-byte command set */
|
||||||
switch (nor->flash_read) {
|
switch (nor->flash_read) {
|
||||||
case SPI_NOR_QUAD:
|
case SPI_NOR_QUAD:
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -49,7 +50,7 @@ static int pgsize;
|
||||||
static int ebcnt;
|
static int ebcnt;
|
||||||
static int pgcnt;
|
static int pgcnt;
|
||||||
static int goodebcnt;
|
static int goodebcnt;
|
||||||
static struct timeval start, finish;
|
static ktime_t start, finish;
|
||||||
|
|
||||||
static int multiblock_erase(int ebnum, int blocks)
|
static int multiblock_erase(int ebnum, int blocks)
|
||||||
{
|
{
|
||||||
|
@ -168,12 +169,12 @@ static int read_eraseblock_by_2pages(int ebnum)
|
||||||
|
|
||||||
static inline void start_timing(void)
|
static inline void start_timing(void)
|
||||||
{
|
{
|
||||||
do_gettimeofday(&start);
|
start = ktime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void stop_timing(void)
|
static inline void stop_timing(void)
|
||||||
{
|
{
|
||||||
do_gettimeofday(&finish);
|
finish = ktime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
static long calc_speed(void)
|
static long calc_speed(void)
|
||||||
|
@ -181,8 +182,7 @@ static long calc_speed(void)
|
||||||
uint64_t k;
|
uint64_t k;
|
||||||
long ms;
|
long ms;
|
||||||
|
|
||||||
ms = (finish.tv_sec - start.tv_sec) * 1000 +
|
ms = ktime_ms_delta(finish, start);
|
||||||
(finish.tv_usec - start.tv_usec) / 1000;
|
|
||||||
if (ms == 0)
|
if (ms == 0)
|
||||||
return 0;
|
return 0;
|
||||||
k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000;
|
k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -79,18 +80,18 @@ static unsigned char *check_buf;
|
||||||
static unsigned int erase_cycles;
|
static unsigned int erase_cycles;
|
||||||
|
|
||||||
static int pgsize;
|
static int pgsize;
|
||||||
static struct timeval start, finish;
|
static ktime_t start, finish;
|
||||||
|
|
||||||
static void report_corrupt(unsigned char *read, unsigned char *written);
|
static void report_corrupt(unsigned char *read, unsigned char *written);
|
||||||
|
|
||||||
static inline void start_timing(void)
|
static inline void start_timing(void)
|
||||||
{
|
{
|
||||||
do_gettimeofday(&start);
|
start = ktime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void stop_timing(void)
|
static inline void stop_timing(void)
|
||||||
{
|
{
|
||||||
do_gettimeofday(&finish);
|
finish = ktime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -333,8 +334,7 @@ static int __init tort_init(void)
|
||||||
long ms;
|
long ms;
|
||||||
|
|
||||||
stop_timing();
|
stop_timing();
|
||||||
ms = (finish.tv_sec - start.tv_sec) * 1000 +
|
ms = ktime_ms_delta(finish, start);
|
||||||
(finish.tv_usec - start.tv_usec) / 1000;
|
|
||||||
pr_info("%08u erase cycles done, took %lu "
|
pr_info("%08u erase cycles done, took %lu "
|
||||||
"milliseconds (%lu seconds)\n",
|
"milliseconds (%lu seconds)\n",
|
||||||
erase_cycles, ms, ms / 1000);
|
erase_cycles, ms, ms / 1000);
|
||||||
|
|
|
@ -613,7 +613,8 @@ static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id)
|
||||||
#ifdef CONFIG_MTD_SPINAND_ONDIEECC
|
#ifdef CONFIG_MTD_SPINAND_ONDIEECC
|
||||||
static int spinand_write_page_hwecc(struct mtd_info *mtd,
|
static int spinand_write_page_hwecc(struct mtd_info *mtd,
|
||||||
struct nand_chip *chip,
|
struct nand_chip *chip,
|
||||||
const u8 *buf, int oob_required)
|
const u8 *buf, int oob_required,
|
||||||
|
int page)
|
||||||
{
|
{
|
||||||
const u8 *p = buf;
|
const u8 *p = buf;
|
||||||
int eccsize = chip->ecc.size;
|
int eccsize = chip->ecc.size;
|
||||||
|
@ -909,8 +910,7 @@ static int spinand_probe(struct spi_device *spi_nand)
|
||||||
dev_set_drvdata(&spi_nand->dev, mtd);
|
dev_set_drvdata(&spi_nand->dev, mtd);
|
||||||
|
|
||||||
mtd->priv = chip;
|
mtd->priv = chip;
|
||||||
mtd->name = dev_name(&spi_nand->dev);
|
mtd->dev.parent = &spi_nand->dev;
|
||||||
mtd->owner = THIS_MODULE;
|
|
||||||
mtd->oobsize = 64;
|
mtd->oobsize = 64;
|
||||||
|
|
||||||
if (nand_scan(mtd, 1))
|
if (nand_scan(mtd, 1))
|
||||||
|
|
|
@ -621,9 +621,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode
|
||||||
uint32_t alloclen;
|
uint32_t alloclen;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!new_valid_dev(rdev))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
ri = jffs2_alloc_raw_inode();
|
ri = jffs2_alloc_raw_inode();
|
||||||
if (!ri)
|
if (!ri)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
@ -97,25 +97,16 @@ int __init jffs2_create_slab_caches(void)
|
||||||
|
|
||||||
void jffs2_destroy_slab_caches(void)
|
void jffs2_destroy_slab_caches(void)
|
||||||
{
|
{
|
||||||
if(full_dnode_slab)
|
kmem_cache_destroy(full_dnode_slab);
|
||||||
kmem_cache_destroy(full_dnode_slab);
|
kmem_cache_destroy(raw_dirent_slab);
|
||||||
if(raw_dirent_slab)
|
kmem_cache_destroy(raw_inode_slab);
|
||||||
kmem_cache_destroy(raw_dirent_slab);
|
kmem_cache_destroy(tmp_dnode_info_slab);
|
||||||
if(raw_inode_slab)
|
kmem_cache_destroy(raw_node_ref_slab);
|
||||||
kmem_cache_destroy(raw_inode_slab);
|
kmem_cache_destroy(node_frag_slab);
|
||||||
if(tmp_dnode_info_slab)
|
kmem_cache_destroy(inode_cache_slab);
|
||||||
kmem_cache_destroy(tmp_dnode_info_slab);
|
|
||||||
if(raw_node_ref_slab)
|
|
||||||
kmem_cache_destroy(raw_node_ref_slab);
|
|
||||||
if(node_frag_slab)
|
|
||||||
kmem_cache_destroy(node_frag_slab);
|
|
||||||
if(inode_cache_slab)
|
|
||||||
kmem_cache_destroy(inode_cache_slab);
|
|
||||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||||
if (xattr_datum_cache)
|
kmem_cache_destroy(xattr_datum_cache);
|
||||||
kmem_cache_destroy(xattr_datum_cache);
|
kmem_cache_destroy(xattr_ref_cache);
|
||||||
if (xattr_ref_cache)
|
|
||||||
kmem_cache_destroy(xattr_ref_cache);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -660,8 +660,12 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r
|
||||||
|
|
||||||
err = jffs2_flash_read(c, (ref_offset(ref)) + read,
|
err = jffs2_flash_read(c, (ref_offset(ref)) + read,
|
||||||
rd->nsize - already, &read, &fd->name[already]);
|
rd->nsize - already, &read, &fd->name[already]);
|
||||||
if (unlikely(read != rd->nsize - already) && likely(!err))
|
if (unlikely(read != rd->nsize - already) && likely(!err)) {
|
||||||
|
jffs2_free_full_dirent(fd);
|
||||||
|
JFFS2_ERROR("short read: wanted %d bytes, got %zd\n",
|
||||||
|
rd->nsize - already, read);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (unlikely(err)) {
|
if (unlikely(err)) {
|
||||||
JFFS2_ERROR("read remainder of name: error %d\n", err);
|
JFFS2_ERROR("read remainder of name: error %d\n", err);
|
||||||
|
|
|
@ -1274,7 +1274,6 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
|
||||||
#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
|
#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
|
||||||
c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
|
c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
|
||||||
if (!c->wbuf_verify) {
|
if (!c->wbuf_verify) {
|
||||||
kfree(c->oobbuf);
|
|
||||||
kfree(c->wbuf);
|
kfree(c->wbuf);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
21
include/linux/blkpg.h
Normal file
21
include/linux/blkpg.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef _LINUX_BLKPG_H
|
||||||
|
#define _LINUX_BLKPG_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Partition table and disk geometry handling
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/compat.h>
|
||||||
|
#include <uapi/linux/blkpg.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
/* For 32-bit/64-bit compatibility of struct blkpg_ioctl_arg */
|
||||||
|
struct blkpg_compat_ioctl_arg {
|
||||||
|
compat_int_t op;
|
||||||
|
compat_int_t flags;
|
||||||
|
compat_int_t datalen;
|
||||||
|
compat_uptr_t data;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _LINUX_BLKPG_H */
|
|
@ -504,16 +504,16 @@ struct nand_ecc_ctrl {
|
||||||
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
uint8_t *buf, int oob_required, int page);
|
uint8_t *buf, int oob_required, int page);
|
||||||
int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required);
|
const uint8_t *buf, int oob_required, int page);
|
||||||
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
uint8_t *buf, int oob_required, int page);
|
uint8_t *buf, int oob_required, int page);
|
||||||
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
uint32_t offs, uint32_t len, uint8_t *buf, int page);
|
uint32_t offs, uint32_t len, uint8_t *buf, int page);
|
||||||
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
uint32_t offset, uint32_t data_len,
|
uint32_t offset, uint32_t data_len,
|
||||||
const uint8_t *data_buf, int oob_required);
|
const uint8_t *data_buf, int oob_required, int page);
|
||||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
const uint8_t *buf, int oob_required);
|
const uint8_t *buf, int oob_required, int page);
|
||||||
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
int page);
|
int page);
|
||||||
int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
@ -544,7 +544,7 @@ struct nand_buffers {
|
||||||
* flash device
|
* flash device
|
||||||
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
|
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
|
||||||
* flash device.
|
* flash device.
|
||||||
* @dn: [BOARDSPECIFIC] device node describing this instance
|
* @flash_node: [BOARDSPECIFIC] device node describing this instance
|
||||||
* @read_byte: [REPLACEABLE] read one byte from the chip
|
* @read_byte: [REPLACEABLE] read one byte from the chip
|
||||||
* @read_word: [REPLACEABLE] read one word from the chip
|
* @read_word: [REPLACEABLE] read one word from the chip
|
||||||
* @write_byte: [REPLACEABLE] write a single byte to the chip on the
|
* @write_byte: [REPLACEABLE] write a single byte to the chip on the
|
||||||
|
@ -556,10 +556,6 @@ struct nand_buffers {
|
||||||
* @block_markbad: [REPLACEABLE] mark a block bad
|
* @block_markbad: [REPLACEABLE] mark a block bad
|
||||||
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
||||||
* ALE/CLE/nCE. Also used to write command and address
|
* ALE/CLE/nCE. Also used to write command and address
|
||||||
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
|
||||||
* mtd->oobsize, mtd->writesize and so on.
|
|
||||||
* @id_data contains the 8 bytes values of NAND_CMD_READID.
|
|
||||||
* Return with the bus width.
|
|
||||||
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accessing
|
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accessing
|
||||||
* device ready/busy line. If set to NULL no access to
|
* device ready/busy line. If set to NULL no access to
|
||||||
* ready/busy is available and the ready/busy information
|
* ready/busy is available and the ready/busy information
|
||||||
|
@ -647,7 +643,7 @@ struct nand_chip {
|
||||||
void __iomem *IO_ADDR_R;
|
void __iomem *IO_ADDR_R;
|
||||||
void __iomem *IO_ADDR_W;
|
void __iomem *IO_ADDR_W;
|
||||||
|
|
||||||
struct device_node *dn;
|
struct device_node *flash_node;
|
||||||
|
|
||||||
uint8_t (*read_byte)(struct mtd_info *mtd);
|
uint8_t (*read_byte)(struct mtd_info *mtd);
|
||||||
u16 (*read_word)(struct mtd_info *mtd);
|
u16 (*read_word)(struct mtd_info *mtd);
|
||||||
|
@ -658,8 +654,6 @@ struct nand_chip {
|
||||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||||
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
|
|
||||||
u8 *id_data);
|
|
||||||
int (*dev_ready)(struct mtd_info *mtd);
|
int (*dev_ready)(struct mtd_info *mtd);
|
||||||
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
|
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
|
||||||
int page_addr);
|
int page_addr);
|
||||||
|
@ -1030,4 +1024,9 @@ struct nand_sdr_timings {
|
||||||
|
|
||||||
/* get timing characteristics from ONFI timing mode. */
|
/* get timing characteristics from ONFI timing mode. */
|
||||||
const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
|
const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
|
||||||
|
|
||||||
|
int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||||
|
void *ecc, int ecclen,
|
||||||
|
void *extraoob, int extraooblen,
|
||||||
|
int threshold);
|
||||||
#endif /* __LINUX_MTD_NAND_H */
|
#endif /* __LINUX_MTD_NAND_H */
|
||||||
|
|
|
@ -10,6 +10,23 @@
|
||||||
#ifndef __LINUX_MTD_SPI_NOR_H
|
#ifndef __LINUX_MTD_SPI_NOR_H
|
||||||
#define __LINUX_MTD_SPI_NOR_H
|
#define __LINUX_MTD_SPI_NOR_H
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/mtd/cfi.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manufacturer IDs
|
||||||
|
*
|
||||||
|
* The first byte returned from the flash after sending opcode SPINOR_OP_RDID.
|
||||||
|
* Sometimes these are the same as CFI IDs, but sometimes they aren't.
|
||||||
|
*/
|
||||||
|
#define SNOR_MFR_ATMEL CFI_MFR_ATMEL
|
||||||
|
#define SNOR_MFR_INTEL CFI_MFR_INTEL
|
||||||
|
#define SNOR_MFR_MICRON CFI_MFR_ST /* ST Micro <--> Micron */
|
||||||
|
#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX
|
||||||
|
#define SNOR_MFR_SPANSION CFI_MFR_AMD
|
||||||
|
#define SNOR_MFR_SST CFI_MFR_SST
|
||||||
|
#define SNOR_MFR_WINBOND 0xef
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note on opcode nomenclature: some opcodes have a format like
|
* Note on opcode nomenclature: some opcodes have a format like
|
||||||
* SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
|
* SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
|
||||||
|
@ -61,24 +78,24 @@
|
||||||
#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
|
#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
|
||||||
|
|
||||||
/* Status Register bits. */
|
/* Status Register bits. */
|
||||||
#define SR_WIP 1 /* Write in progress */
|
#define SR_WIP BIT(0) /* Write in progress */
|
||||||
#define SR_WEL 2 /* Write enable latch */
|
#define SR_WEL BIT(1) /* Write enable latch */
|
||||||
/* meaning of other SR_* bits may differ between vendors */
|
/* meaning of other SR_* bits may differ between vendors */
|
||||||
#define SR_BP0 4 /* Block protect 0 */
|
#define SR_BP0 BIT(2) /* Block protect 0 */
|
||||||
#define SR_BP1 8 /* Block protect 1 */
|
#define SR_BP1 BIT(3) /* Block protect 1 */
|
||||||
#define SR_BP2 0x10 /* Block protect 2 */
|
#define SR_BP2 BIT(4) /* Block protect 2 */
|
||||||
#define SR_SRWD 0x80 /* SR write protect */
|
#define SR_SRWD BIT(7) /* SR write protect */
|
||||||
|
|
||||||
#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */
|
#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
|
||||||
|
|
||||||
/* Enhanced Volatile Configuration Register bits */
|
/* Enhanced Volatile Configuration Register bits */
|
||||||
#define EVCR_QUAD_EN_MICRON 0x80 /* Micron Quad I/O */
|
#define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */
|
||||||
|
|
||||||
/* Flag Status Register bits */
|
/* Flag Status Register bits */
|
||||||
#define FSR_READY 0x80
|
#define FSR_READY BIT(7)
|
||||||
|
|
||||||
/* Configuration Register bits. */
|
/* Configuration Register bits. */
|
||||||
#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
|
#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */
|
||||||
|
|
||||||
enum read_mode {
|
enum read_mode {
|
||||||
SPI_NOR_NORMAL = 0,
|
SPI_NOR_NORMAL = 0,
|
||||||
|
@ -87,33 +104,6 @@ enum read_mode {
|
||||||
SPI_NOR_QUAD,
|
SPI_NOR_QUAD,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
|
|
||||||
* @wren: command for "Write Enable", or 0x00 for not required
|
|
||||||
* @cmd: command for operation
|
|
||||||
* @cmd_pins: number of pins to send @cmd (1, 2, 4)
|
|
||||||
* @addr: address for operation
|
|
||||||
* @addr_pins: number of pins to send @addr (1, 2, 4)
|
|
||||||
* @addr_width: number of address bytes
|
|
||||||
* (3,4, or 0 for address not required)
|
|
||||||
* @mode: mode data
|
|
||||||
* @mode_pins: number of pins to send @mode (1, 2, 4)
|
|
||||||
* @mode_cycles: number of mode cycles (0 for mode not required)
|
|
||||||
* @dummy_cycles: number of dummy cycles (0 for dummy not required)
|
|
||||||
*/
|
|
||||||
struct spi_nor_xfer_cfg {
|
|
||||||
u8 wren;
|
|
||||||
u8 cmd;
|
|
||||||
u8 cmd_pins;
|
|
||||||
u32 addr;
|
|
||||||
u8 addr_pins;
|
|
||||||
u8 addr_width;
|
|
||||||
u8 mode;
|
|
||||||
u8 mode_pins;
|
|
||||||
u8 mode_cycles;
|
|
||||||
u8 dummy_cycles;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SPI_NOR_MAX_CMD_SIZE 8
|
#define SPI_NOR_MAX_CMD_SIZE 8
|
||||||
enum spi_nor_ops {
|
enum spi_nor_ops {
|
||||||
SPI_NOR_OPS_READ = 0,
|
SPI_NOR_OPS_READ = 0,
|
||||||
|
@ -127,11 +117,14 @@ enum spi_nor_option_flags {
|
||||||
SNOR_F_USE_FSR = BIT(0),
|
SNOR_F_USE_FSR = BIT(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mtd_info;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct spi_nor - Structure for defining a the SPI NOR layer
|
* struct spi_nor - Structure for defining a the SPI NOR layer
|
||||||
* @mtd: point to a mtd_info structure
|
* @mtd: point to a mtd_info structure
|
||||||
* @lock: the lock for the read/write/erase/lock/unlock operations
|
* @lock: the lock for the read/write/erase/lock/unlock operations
|
||||||
* @dev: point to a spi device, or a spi nor controller device.
|
* @dev: point to a spi device, or a spi nor controller device.
|
||||||
|
* @flash_node: point to a device node describing this flash instance.
|
||||||
* @page_size: the page size of the SPI NOR
|
* @page_size: the page size of the SPI NOR
|
||||||
* @addr_width: number of address bytes
|
* @addr_width: number of address bytes
|
||||||
* @erase_opcode: the opcode for erasing a sector
|
* @erase_opcode: the opcode for erasing a sector
|
||||||
|
@ -141,28 +134,28 @@ enum spi_nor_option_flags {
|
||||||
* @flash_read: the mode of the read
|
* @flash_read: the mode of the read
|
||||||
* @sst_write_second: used by the SST write operation
|
* @sst_write_second: used by the SST write operation
|
||||||
* @flags: flag options for the current SPI-NOR (SNOR_F_*)
|
* @flags: flag options for the current SPI-NOR (SNOR_F_*)
|
||||||
* @cfg: used by the read_xfer/write_xfer
|
|
||||||
* @cmd_buf: used by the write_reg
|
* @cmd_buf: used by the write_reg
|
||||||
* @prepare: [OPTIONAL] do some preparations for the
|
* @prepare: [OPTIONAL] do some preparations for the
|
||||||
* read/write/erase/lock/unlock operations
|
* read/write/erase/lock/unlock operations
|
||||||
* @unprepare: [OPTIONAL] do some post work after the
|
* @unprepare: [OPTIONAL] do some post work after the
|
||||||
* read/write/erase/lock/unlock operations
|
* read/write/erase/lock/unlock operations
|
||||||
* @read_xfer: [OPTIONAL] the read fundamental primitive
|
|
||||||
* @write_xfer: [OPTIONAL] the writefundamental primitive
|
|
||||||
* @read_reg: [DRIVER-SPECIFIC] read out the register
|
* @read_reg: [DRIVER-SPECIFIC] read out the register
|
||||||
* @write_reg: [DRIVER-SPECIFIC] write data to the register
|
* @write_reg: [DRIVER-SPECIFIC] write data to the register
|
||||||
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
|
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
|
||||||
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
|
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
|
||||||
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
|
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
|
||||||
* at the offset @offs
|
* at the offset @offs
|
||||||
* @lock: [FLASH-SPECIFIC] lock a region of the SPI NOR
|
* @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR
|
||||||
* @unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
|
* @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
|
||||||
|
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
|
||||||
|
* completely locked
|
||||||
* @priv: the private data
|
* @priv: the private data
|
||||||
*/
|
*/
|
||||||
struct spi_nor {
|
struct spi_nor {
|
||||||
struct mtd_info *mtd;
|
struct mtd_info mtd;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
struct device_node *flash_node;
|
||||||
u32 page_size;
|
u32 page_size;
|
||||||
u8 addr_width;
|
u8 addr_width;
|
||||||
u8 erase_opcode;
|
u8 erase_opcode;
|
||||||
|
@ -172,18 +165,12 @@ struct spi_nor {
|
||||||
enum read_mode flash_read;
|
enum read_mode flash_read;
|
||||||
bool sst_write_second;
|
bool sst_write_second;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
struct spi_nor_xfer_cfg cfg;
|
|
||||||
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
||||||
|
|
||||||
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||||
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||||
int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
|
|
||||||
u8 *buf, size_t len);
|
|
||||||
int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
|
|
||||||
u8 *buf, size_t len);
|
|
||||||
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||||
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
|
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||||
int write_enable);
|
|
||||||
|
|
||||||
int (*read)(struct spi_nor *nor, loff_t from,
|
int (*read)(struct spi_nor *nor, loff_t from,
|
||||||
size_t len, size_t *retlen, u_char *read_buf);
|
size_t len, size_t *retlen, u_char *read_buf);
|
||||||
|
@ -193,6 +180,7 @@ struct spi_nor {
|
||||||
|
|
||||||
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||||
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||||
|
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||||
|
|
||||||
void *priv;
|
void *priv;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,30 +4,6 @@
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
struct pxa3xx_nand_timing {
|
|
||||||
unsigned int tCH; /* Enable signal hold time */
|
|
||||||
unsigned int tCS; /* Enable signal setup time */
|
|
||||||
unsigned int tWH; /* ND_nWE high duration */
|
|
||||||
unsigned int tWP; /* ND_nWE pulse time */
|
|
||||||
unsigned int tRH; /* ND_nRE high duration */
|
|
||||||
unsigned int tRP; /* ND_nRE pulse width */
|
|
||||||
unsigned int tR; /* ND_nWE high to ND_nRE low for read */
|
|
||||||
unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */
|
|
||||||
unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pxa3xx_nand_flash {
|
|
||||||
char *name;
|
|
||||||
uint32_t chip_id;
|
|
||||||
unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */
|
|
||||||
unsigned int page_size; /* Page size in bytes (PAGE_SZ) */
|
|
||||||
unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
|
|
||||||
unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
|
|
||||||
unsigned int num_blocks; /* Number of physical blocks in Flash */
|
|
||||||
|
|
||||||
struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Current pxa3xx_nand controller has two chip select which
|
* Current pxa3xx_nand controller has two chip select which
|
||||||
* both be workable.
|
* both be workable.
|
||||||
|
@ -63,9 +39,6 @@ struct pxa3xx_nand_platform_data {
|
||||||
|
|
||||||
const struct mtd_partition *parts[NUM_CHIP_SELECT];
|
const struct mtd_partition *parts[NUM_CHIP_SELECT];
|
||||||
unsigned int nr_parts[NUM_CHIP_SELECT];
|
unsigned int nr_parts[NUM_CHIP_SELECT];
|
||||||
|
|
||||||
const struct pxa3xx_nand_flash * flash;
|
|
||||||
size_t num_flash;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info);
|
extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef _LINUX_BLKPG_H
|
#ifndef _UAPI__LINUX_BLKPG_H
|
||||||
#define _LINUX_BLKPG_H
|
#define _UAPI__LINUX_BLKPG_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Partition table and disk geometry handling
|
* Partition table and disk geometry handling
|
||||||
|
@ -56,4 +56,4 @@ struct blkpg_partition {
|
||||||
char volname[BLKPG_VOLNAMELTH]; /* volume label */
|
char volname[BLKPG_VOLNAMELTH]; /* volume label */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _LINUX_BLKPG_H */
|
#endif /* _UAPI__LINUX_BLKPG_H */
|
||||||
|
|
|
@ -20,8 +20,6 @@
|
||||||
#ifndef __MTD_USER_H__
|
#ifndef __MTD_USER_H__
|
||||||
#define __MTD_USER_H__
|
#define __MTD_USER_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* This file is blessed for inclusion by userspace */
|
/* This file is blessed for inclusion by userspace */
|
||||||
#include <mtd/mtd-abi.h>
|
#include <mtd/mtd-abi.h>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue