mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 22:54:01 +00:00
MTD fixes for 4.2
JFFS2 * fix a theoretical unbalanced locking issue; the lock handling was a bit unclean, but AFAICT, it didn't actually lead to real deadlocks NAND * brcmnand driver: new driver supporting NAND controller found originally on Broadcom STB SoCs (BCM7xxx), but now also found on BCM63xxx, iProc (e.g., Cygnus, BCM5301x), BCM3xxx, and more * Begin factoring out BBT code so it can be shared between traditional (parallel) NAND drivers and upcoming SPI NAND drivers (WIP) * Add common DT-based init support, so nand_base can pick up some flash properties automatically, using established common NAND DT properties * mxc_nand: support 8-bit ECC * pxa3xx_nand: - fix build for ARM64 - use a jiffies-based timeout SPI NOR * Add a few new IDs * Clear out some unnecessary entries * Make sure SECT_4K flags are correct for all (?) entries Core * Fix mtd->usecount race conditions (BUG_ON()) * Switch to modern PM ops Other * CFI: save code space by de-inlining large functions * Clean up some partition parser selection code across several drivers * Various miscellaneous changes, mostly minor -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJViZdKAAoJEFySrpd9RFgtaqoQAINkYUdxa8mOlmXQSIhWz19K 5FJqJN+/lgrYmIGI1SzVy/TTB/V4hWH+9h1snU98ToaHFACqbKKeo6FLs6GRd+WJ adR9H4PUjtAZOt8trKzzygJnqvRPDWnn6H+0urxFv7kpyPTEorifiiI6+o3AM995 vh+YsLJNFYHucIzi4TVkgwssLYi8I9TOb35pDnW2uT/ikDi+4Uu+Ocq6u7SuMPXV Zch6DcmBhtTQ9ghBwF/dMMAhyyMLyY4q0+CEzmJGxNU+g2o1njOTUDBv+lLqxxMB fUXMAPXIT3ndhWjMmzklG7AjOorbg44aG2UlqJWXx00VYJKNf+pljpgdHOXKiuWo xYkqOYnEM8gZ4qYNInKbbv34Q2+EjCxg+aAWxh+yRCJrfnF3p5/QSMVL2P2JNrc3 Kg8W7pW42xOeGxTYpydBtvpq+41qRtdWMgNp47PHvKF0mhpeSCCvRf/YfU4cNcfv WfQzBLy/d7RMVFyvACdvzerF/fh9YX3yxV0B0LstytCSZO13617F6HHpm1pYfP0v d9GpLpdiFktoG/AXpPzlSl992ALf0SQaedamuxApfvLVymDkG9xoO0P5p6SbOiJ0 QnBvEMkswEf7ExPzr5SYufSE93wikAEsAfjsLOHo+FQQ57eaRgpCOZHHgfuwcTra xUr6u2Rq9iDenhVYDqJU =JN5N -----END PGP SIGNATURE----- Merge tag 'for-linus-20150623' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: "JFFS2: - fix a theoretical unbalanced locking issue; the lock handling was a bit unclean, but AFAICT, it didn't actually lead to real deadlocks NAND: - brcmnand driver: new driver supporting NAND controller found originally on Broadcom STB SoCs (BCM7xxx), but now also found on BCM63xxx, iProc (e.g., Cygnus, BCM5301x), BCM3xxx, and more - begin factoring out BBT code so it can be shared between traditional (parallel) NAND drivers and upcoming SPI NAND drivers (WIP) - add common DT-based init support, so nand_base can pick up some flash properties automatically, using established common NAND DT properties - mxc_nand: support 8-bit ECC - pxa3xx_nand: * fix build for ARM64 * use a jiffies-based timeout SPI NOR: - add a few new IDs - clear out some unnecessary entries - make sure SECT_4K flags are correct for all (?) entries Core: - fix mtd->usecount race conditions (BUG_ON()) - switch to modern PM ops Other: - CFI: save code space by de-inlining large functions - clean up some partition parser selection code across several drivers - various miscellaneous changes, mostly minor" * tag 'for-linus-20150623' of git://git.infradead.org/linux-mtd: (57 commits) mtd: docg3: Fix kasprintf() usage mtd: docg3: Don't leak docg3->bbt in error path mtd: nandsim: Fix kasprintf() usage mtd: cs553x_nand: Fix kasprintf() usage mtd: r852: Fix device_create_file() usage mtd: brcmnand: drop unnecessary initialization mtd: propagate error codes from add_mtd_device() mtd: diskonchip: remove two-phase partitioning / registration mtd: dc21285: use raw spinlock functions for nw_gpio_lock mtd: chips: fixup dependencies, to prevent build error mtd: cfi_cmdset_0002: Initialize datum before calling map_word_load_partial mtd: cfi: deinline large functions mtd: lantiq-flash: use default partition parsers mtd: plat_nand: use default partition probe mtd: nand: correct indentation within conditional mtd: remove incorrect file name mtd: blktrans: use better error code for unimplemented ioctl() mtd: maps: Spelling s/reseved/reserved/ mtd: blktrans: change blktrans_getgeo return value mtd: mxc_nand: generate nand_ecclayout for 8 bit ECC ...
This commit is contained in:
commit
54245ed870
47 changed files with 3329 additions and 394 deletions
150
Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt
Normal file
150
Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
* Broadcom STB NAND Controller
|
||||||
|
|
||||||
|
The Broadcom Set-Top Box NAND controller supports low-level access to raw NAND
|
||||||
|
flash chips. It has a memory-mapped register interface for both control
|
||||||
|
registers and for its data input/output buffer. On some SoCs, this controller is
|
||||||
|
paired with a custom DMA engine (inventively named "Flash DMA") which supports
|
||||||
|
basic PROGRAM and READ functions, among other features.
|
||||||
|
|
||||||
|
This controller was originally designed for STB SoCs (BCM7xxx) but is now
|
||||||
|
available on a variety of Broadcom SoCs, including some BCM3xxx, BCM63xx, and
|
||||||
|
iProc/Cygnus. Its history includes several similar (but not fully register
|
||||||
|
compatible) versions.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : May contain an SoC-specific compatibility string (see below)
|
||||||
|
to account for any SoC-specific hardware bits that may be
|
||||||
|
added on top of the base core controller.
|
||||||
|
In addition, must contain compatibility information about
|
||||||
|
the core NAND controller, of the following form:
|
||||||
|
"brcm,brcmnand" and an appropriate version compatibility
|
||||||
|
string, like "brcm,brcmnand-v7.0"
|
||||||
|
Possible values:
|
||||||
|
brcm,brcmnand-v4.0
|
||||||
|
brcm,brcmnand-v5.0
|
||||||
|
brcm,brcmnand-v6.0
|
||||||
|
brcm,brcmnand-v6.1
|
||||||
|
brcm,brcmnand-v7.0
|
||||||
|
brcm,brcmnand-v7.1
|
||||||
|
brcm,brcmnand
|
||||||
|
- reg : the register start and length for NAND register region.
|
||||||
|
(optional) Flash DMA register range (if present)
|
||||||
|
(optional) NAND flash cache range (if at non-standard offset)
|
||||||
|
- reg-names : a list of the names corresponding to the previous register
|
||||||
|
ranges. Should contain "nand" and (optionally)
|
||||||
|
"flash-dma" and/or "nand-cache".
|
||||||
|
- interrupts : The NAND CTLRDY interrupt and (if Flash DMA is available)
|
||||||
|
FLASH_DMA_DONE
|
||||||
|
- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done", if broken out as
|
||||||
|
individual interrupts.
|
||||||
|
May be "nand", if the SoC has the individual NAND
|
||||||
|
interrupts multiplexed behind another custom piece of
|
||||||
|
hardware
|
||||||
|
- interrupt-parent : See standard interrupt bindings
|
||||||
|
- #address-cells : <1> - subnodes give the chip-select number
|
||||||
|
- #size-cells : <0>
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- brcm,nand-has-wp : Some versions of this IP include a write-protect
|
||||||
|
(WP) control bit. It is always available on >=
|
||||||
|
v7.0. Use this property to describe the rare
|
||||||
|
earlier versions of this core that include WP
|
||||||
|
|
||||||
|
-- Additonal SoC-specific NAND controller properties --
|
||||||
|
|
||||||
|
The NAND controller is integrated differently on the variety of SoCs on which it
|
||||||
|
is found. Part of this integration involves providing status and enable bits
|
||||||
|
with which to control the 8 exposed NAND interrupts, as well as hardware for
|
||||||
|
configuring the endianness of the data bus. On some SoCs, these features are
|
||||||
|
handled via standard, modular components (e.g., their interrupts look like a
|
||||||
|
normal IRQ chip), but on others, they are controlled in unique and interesting
|
||||||
|
ways, sometimes with registers that lump multiple NAND-related functions
|
||||||
|
together. The former case can be described simply by the standard interrupts
|
||||||
|
properties in the main controller node. But for the latter exceptional cases,
|
||||||
|
we define additional 'compatible' properties and associated register resources within the NAND controller node above.
|
||||||
|
|
||||||
|
- compatible: Can be one of several SoC-specific strings. Each SoC may have
|
||||||
|
different requirements for its additional properties, as described below each
|
||||||
|
bullet point below.
|
||||||
|
|
||||||
|
* "brcm,nand-bcm63138"
|
||||||
|
- reg: (required) the 'NAND_INT_BASE' register range, with separate status
|
||||||
|
and enable registers
|
||||||
|
- reg-names: (required) "nand-int-base"
|
||||||
|
|
||||||
|
* "brcm,nand-iproc"
|
||||||
|
- reg: (required) the "IDM" register range, for interrupt enable and APB
|
||||||
|
bus access endianness configuration, and the "EXT" register range,
|
||||||
|
for interrupt status/ack.
|
||||||
|
- reg-names: (required) a list of the names corresponding to the previous
|
||||||
|
register ranges. Should contain "iproc-idm" and "iproc-ext".
|
||||||
|
|
||||||
|
|
||||||
|
* NAND chip-select
|
||||||
|
|
||||||
|
Each controller (compatible: "brcm,brcmnand") may contain one or more subnodes
|
||||||
|
to represent enabled chip-selects which (may) contain NAND flash chips. Their
|
||||||
|
properties are as follows.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : should contain "brcm,nandcs"
|
||||||
|
- reg : a single integer representing the chip-select
|
||||||
|
number (e.g., 0, 1, 2, etc.)
|
||||||
|
- #address-cells : see partition.txt
|
||||||
|
- #size-cells : see partition.txt
|
||||||
|
- nand-ecc-strength : see nand.txt
|
||||||
|
- nand-ecc-step-size : must be 512 or 1024. See nand.txt
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- nand-on-flash-bbt : boolean, to enable the on-flash BBT for this
|
||||||
|
chip-select. See nand.txt
|
||||||
|
- brcm,nand-oob-sector-size : integer, to denote the spare area sector size
|
||||||
|
expected for the ECC layout in use. This size, in
|
||||||
|
addition to the strength and step-size,
|
||||||
|
determines how the hardware BCH engine will lay
|
||||||
|
out the parity bytes it stores on the flash.
|
||||||
|
This property can be automatically determined by
|
||||||
|
the flash geometry (particularly the NAND page
|
||||||
|
and OOB size) in many cases, but when booting
|
||||||
|
from NAND, the boot controller has only a limited
|
||||||
|
number of available options for its default ECC
|
||||||
|
layout.
|
||||||
|
|
||||||
|
Each nandcs device node may optionally contain sub-nodes describing the flash
|
||||||
|
partition mapping. See partition.txt for more detail.
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
nand@f0442800 {
|
||||||
|
compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
|
||||||
|
reg = <0xF0442800 0x600>,
|
||||||
|
<0xF0443000 0x100>;
|
||||||
|
reg-names = "nand", "flash-dma";
|
||||||
|
interrupt-parent = <&hif_intr2_intc>;
|
||||||
|
interrupts = <24>, <4>;
|
||||||
|
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
nandcs@1 {
|
||||||
|
compatible = "brcm,nandcs";
|
||||||
|
reg = <1>; // Chip select 1
|
||||||
|
nand-on-flash-bbt;
|
||||||
|
nand-ecc-strength = <12>;
|
||||||
|
nand-ecc-step-size = <512>;
|
||||||
|
|
||||||
|
// Partitions
|
||||||
|
#address-cells = <1>; // <2>, for 64-bit offset
|
||||||
|
#size-cells = <1>; // <2>, for 64-bit length
|
||||||
|
flash0.rootfs@0 {
|
||||||
|
reg = <0 0x10000000>;
|
||||||
|
};
|
||||||
|
flash0@0 {
|
||||||
|
reg = <0 0>; // MTDPART_SIZ_FULL
|
||||||
|
};
|
||||||
|
flash0.kernel@10000000 {
|
||||||
|
reg = <0x10000000 0x400000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -2267,6 +2267,12 @@ S: Supported
|
||||||
F: drivers/gpio/gpio-bcm-kona.c
|
F: drivers/gpio/gpio-bcm-kona.c
|
||||||
F: Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt
|
F: Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt
|
||||||
|
|
||||||
|
BROADCOM STB NAND FLASH DRIVER
|
||||||
|
M: Brian Norris <computersforpeace@gmail.com>
|
||||||
|
L: linux-mtd@lists.infradead.org
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/mtd/nand/brcmnand/
|
||||||
|
|
||||||
BROADCOM SPECIFIC AMBA DRIVER (BCMA)
|
BROADCOM SPECIFIC AMBA DRIVER (BCMA)
|
||||||
M: Rafał Miłecki <zajec5@gmail.com>
|
M: Rafał Miłecki <zajec5@gmail.com>
|
||||||
L: linux-wireless@vger.kernel.org
|
L: linux-wireless@vger.kernel.org
|
||||||
|
|
|
@ -16,6 +16,7 @@ config MTD_CFI
|
||||||
config MTD_JEDECPROBE
|
config MTD_JEDECPROBE
|
||||||
tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
|
tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
|
||||||
select MTD_GEN_PROBE
|
select MTD_GEN_PROBE
|
||||||
|
select MTD_CFI_UTIL
|
||||||
help
|
help
|
||||||
This option enables JEDEC-style probing of flash chips which are not
|
This option enables JEDEC-style probing of flash chips which are not
|
||||||
compatible with the Common Flash Interface, but will use the common
|
compatible with the Common Flash Interface, but will use the common
|
||||||
|
|
|
@ -1295,7 +1295,7 @@ static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
|
||||||
unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1);
|
unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1);
|
||||||
int gap = adr - bus_ofs;
|
int gap = adr - bus_ofs;
|
||||||
int n = min_t(int, len, map_bankwidth(map) - gap);
|
int n = min_t(int, len, map_bankwidth(map) - gap);
|
||||||
map_word datum;
|
map_word datum = map_word_ff(map);
|
||||||
|
|
||||||
if (n != map_bankwidth(map)) {
|
if (n != map_bankwidth(map)) {
|
||||||
/* partial write of a word, load old contents */
|
/* partial write of a word, load old contents */
|
||||||
|
|
|
@ -23,6 +23,194 @@
|
||||||
#include <linux/mtd/map.h>
|
#include <linux/mtd/map.h>
|
||||||
#include <linux/mtd/cfi.h>
|
#include <linux/mtd/cfi.h>
|
||||||
|
|
||||||
|
void cfi_udelay(int us)
|
||||||
|
{
|
||||||
|
if (us >= 1000) {
|
||||||
|
msleep((us+999)/1000);
|
||||||
|
} else {
|
||||||
|
udelay(us);
|
||||||
|
cond_resched();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(cfi_udelay);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the command address according to the given geometry.
|
||||||
|
*/
|
||||||
|
uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
|
||||||
|
struct map_info *map, struct cfi_private *cfi)
|
||||||
|
{
|
||||||
|
unsigned bankwidth = map_bankwidth(map);
|
||||||
|
unsigned interleave = cfi_interleave(cfi);
|
||||||
|
unsigned type = cfi->device_type;
|
||||||
|
uint32_t addr;
|
||||||
|
|
||||||
|
addr = (cmd_ofs * type) * interleave;
|
||||||
|
|
||||||
|
/* Modify the unlock address if we are in compatibility mode.
|
||||||
|
* For 16bit devices on 8 bit busses
|
||||||
|
* and 32bit devices on 16 bit busses
|
||||||
|
* set the low bit of the alternating bit sequence of the address.
|
||||||
|
*/
|
||||||
|
if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa))
|
||||||
|
addr |= (type >> 1)*interleave;
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(cfi_build_cmd_addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transforms the CFI command for the given geometry (bus width & interleave).
|
||||||
|
* It looks too long to be inline, but in the common case it should almost all
|
||||||
|
* get optimised away.
|
||||||
|
*/
|
||||||
|
map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
|
||||||
|
{
|
||||||
|
map_word val = { {0} };
|
||||||
|
int wordwidth, words_per_bus, chip_mode, chips_per_word;
|
||||||
|
unsigned long onecmd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* We do it this way to give the compiler a fighting chance
|
||||||
|
of optimising away all the crap for 'bankwidth' larger than
|
||||||
|
an unsigned long, in the common case where that support is
|
||||||
|
disabled */
|
||||||
|
if (map_bankwidth_is_large(map)) {
|
||||||
|
wordwidth = sizeof(unsigned long);
|
||||||
|
words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
|
||||||
|
} else {
|
||||||
|
wordwidth = map_bankwidth(map);
|
||||||
|
words_per_bus = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
|
||||||
|
chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
|
||||||
|
|
||||||
|
/* First, determine what the bit-pattern should be for a single
|
||||||
|
device, according to chip mode and endianness... */
|
||||||
|
switch (chip_mode) {
|
||||||
|
default: BUG();
|
||||||
|
case 1:
|
||||||
|
onecmd = cmd;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
onecmd = cpu_to_cfi16(map, cmd);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
onecmd = cpu_to_cfi32(map, cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now replicate it across the size of an unsigned long, or
|
||||||
|
just to the bus width as appropriate */
|
||||||
|
switch (chips_per_word) {
|
||||||
|
default: BUG();
|
||||||
|
#if BITS_PER_LONG >= 64
|
||||||
|
case 8:
|
||||||
|
onecmd |= (onecmd << (chip_mode * 32));
|
||||||
|
#endif
|
||||||
|
case 4:
|
||||||
|
onecmd |= (onecmd << (chip_mode * 16));
|
||||||
|
case 2:
|
||||||
|
onecmd |= (onecmd << (chip_mode * 8));
|
||||||
|
case 1:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And finally, for the multi-word case, replicate it
|
||||||
|
in all words in the structure */
|
||||||
|
for (i=0; i < words_per_bus; i++) {
|
||||||
|
val.x[i] = onecmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(cfi_build_cmd);
|
||||||
|
|
||||||
|
unsigned long cfi_merge_status(map_word val, struct map_info *map,
|
||||||
|
struct cfi_private *cfi)
|
||||||
|
{
|
||||||
|
int wordwidth, words_per_bus, chip_mode, chips_per_word;
|
||||||
|
unsigned long onestat, res = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* We do it this way to give the compiler a fighting chance
|
||||||
|
of optimising away all the crap for 'bankwidth' larger than
|
||||||
|
an unsigned long, in the common case where that support is
|
||||||
|
disabled */
|
||||||
|
if (map_bankwidth_is_large(map)) {
|
||||||
|
wordwidth = sizeof(unsigned long);
|
||||||
|
words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
|
||||||
|
} else {
|
||||||
|
wordwidth = map_bankwidth(map);
|
||||||
|
words_per_bus = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
|
||||||
|
chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
|
||||||
|
|
||||||
|
onestat = val.x[0];
|
||||||
|
/* Or all status words together */
|
||||||
|
for (i=1; i < words_per_bus; i++) {
|
||||||
|
onestat |= val.x[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
res = onestat;
|
||||||
|
switch(chips_per_word) {
|
||||||
|
default: BUG();
|
||||||
|
#if BITS_PER_LONG >= 64
|
||||||
|
case 8:
|
||||||
|
res |= (onestat >> (chip_mode * 32));
|
||||||
|
#endif
|
||||||
|
case 4:
|
||||||
|
res |= (onestat >> (chip_mode * 16));
|
||||||
|
case 2:
|
||||||
|
res |= (onestat >> (chip_mode * 8));
|
||||||
|
case 1:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last, determine what the bit-pattern should be for a single
|
||||||
|
device, according to chip mode and endianness... */
|
||||||
|
switch (chip_mode) {
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
res = cfi16_to_cpu(map, res);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
res = cfi32_to_cpu(map, res);
|
||||||
|
break;
|
||||||
|
default: BUG();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(cfi_merge_status);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a CFI command to a bank of flash for the given geometry.
|
||||||
|
*
|
||||||
|
* Returns the offset in flash where the command was written.
|
||||||
|
* If prev_val is non-null, it will be set to the value at the command address,
|
||||||
|
* before the command was written.
|
||||||
|
*/
|
||||||
|
uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
|
||||||
|
struct map_info *map, struct cfi_private *cfi,
|
||||||
|
int type, map_word *prev_val)
|
||||||
|
{
|
||||||
|
map_word val;
|
||||||
|
uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi);
|
||||||
|
val = cfi_build_cmd(cmd, map, cfi);
|
||||||
|
|
||||||
|
if (prev_val)
|
||||||
|
*prev_val = map_read(map, addr);
|
||||||
|
|
||||||
|
map_write(map, val, addr);
|
||||||
|
|
||||||
|
return addr - base;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(cfi_send_gen_cmd);
|
||||||
|
|
||||||
int __xipram cfi_qry_present(struct map_info *map, __u32 base,
|
int __xipram cfi_qry_present(struct map_info *map, __u32 base,
|
||||||
struct cfi_private *cfi)
|
struct cfi_private *cfi)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1815,7 +1815,7 @@ static void doc_dbg_unregister(struct docg3 *docg3)
|
||||||
* @chip_id: The chip ID of the supported chip
|
* @chip_id: The chip ID of the supported chip
|
||||||
* @mtd: The structure to fill
|
* @mtd: The structure to fill
|
||||||
*/
|
*/
|
||||||
static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct docg3 *docg3 = mtd->priv;
|
struct docg3 *docg3 = mtd->priv;
|
||||||
int cfg;
|
int cfg;
|
||||||
|
@ -1828,6 +1828,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||||
case DOC_CHIPID_G3:
|
case DOC_CHIPID_G3:
|
||||||
mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
|
mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
|
||||||
docg3->device_id);
|
docg3->device_id);
|
||||||
|
if (!mtd->name)
|
||||||
|
return -ENOMEM;
|
||||||
docg3->max_block = 2047;
|
docg3->max_block = 2047;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1850,6 +1852,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||||
mtd->_block_isbad = doc_block_isbad;
|
mtd->_block_isbad = doc_block_isbad;
|
||||||
mtd->ecclayout = &docg3_oobinfo;
|
mtd->ecclayout = &docg3_oobinfo;
|
||||||
mtd->ecc_strength = DOC_ECC_BCH_T;
|
mtd->ecc_strength = DOC_ECC_BCH_T;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1900,7 +1904,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if (chip_id != (u16)(~chip_id_inv)) {
|
if (chip_id != (u16)(~chip_id_inv)) {
|
||||||
goto nomem3;
|
goto nomem4;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (chip_id) {
|
switch (chip_id) {
|
||||||
|
@ -1910,15 +1914,19 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
|
doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
|
||||||
goto nomem3;
|
goto nomem4;
|
||||||
}
|
}
|
||||||
|
|
||||||
doc_set_driver_info(chip_id, mtd);
|
ret = doc_set_driver_info(chip_id, mtd);
|
||||||
|
if (ret)
|
||||||
|
goto nomem4;
|
||||||
|
|
||||||
doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ);
|
doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ);
|
||||||
doc_reload_bbt(docg3);
|
doc_reload_bbt(docg3);
|
||||||
return mtd;
|
return mtd;
|
||||||
|
|
||||||
|
nomem4:
|
||||||
|
kfree(docg3->bbt);
|
||||||
nomem3:
|
nomem3:
|
||||||
kfree(mtd);
|
kfree(mtd);
|
||||||
nomem2:
|
nomem2:
|
||||||
|
@ -2117,7 +2125,7 @@ static int docg3_release(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static struct of_device_id docg3_dt_ids[] = {
|
static const struct of_device_id docg3_dt_ids[] = {
|
||||||
{ .compatible = "m-systems,diskonchip-g3" },
|
{ .compatible = "m-systems,diskonchip-g3" },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -261,45 +261,33 @@ static int m25p_remove(struct spi_device *spi)
|
||||||
* keep them available as module aliases for existing platforms.
|
* keep them available as module aliases for existing platforms.
|
||||||
*/
|
*/
|
||||||
static const struct spi_device_id m25p_ids[] = {
|
static const struct spi_device_id m25p_ids[] = {
|
||||||
{"at25fs010"}, {"at25fs040"}, {"at25df041a"}, {"at25df321a"},
|
/*
|
||||||
{"at25df641"}, {"at26f004"}, {"at26df081a"}, {"at26df161a"},
|
* Entries not used in DTs that should be safe to drop after replacing
|
||||||
{"at26df321"}, {"at45db081d"},
|
* them with "nor-jedec" in platform data.
|
||||||
{"en25f32"}, {"en25p32"}, {"en25q32b"}, {"en25p64"},
|
*/
|
||||||
{"en25q64"}, {"en25qh128"}, {"en25qh256"},
|
{"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
|
||||||
{"f25l32pa"},
|
|
||||||
{"mr25h256"}, {"mr25h10"},
|
/*
|
||||||
{"gd25q32"}, {"gd25q64"},
|
* Entries that were used in DTs without "nor-jedec" fallback and should
|
||||||
{"160s33b"}, {"320s33b"}, {"640s33b"},
|
* be kept for backward compatibility.
|
||||||
{"mx25l2005a"}, {"mx25l4005a"}, {"mx25l8005"}, {"mx25l1606e"},
|
*/
|
||||||
{"mx25l3205d"}, {"mx25l3255e"}, {"mx25l6405d"}, {"mx25l12805d"},
|
{"at25df321a"}, {"at25df641"}, {"at26df081a"},
|
||||||
{"mx25l12855e"},{"mx25l25635e"},{"mx25l25655e"},{"mx66l51235l"},
|
{"mr25h256"},
|
||||||
{"mx66l1g55g"},
|
{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
|
||||||
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q256a"},
|
{"mx25l25635e"},{"mx66l51235l"},
|
||||||
{"n25q512a"}, {"n25q512ax3"}, {"n25q00"},
|
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
|
||||||
{"pm25lv512"}, {"pm25lv010"}, {"pm25lq032"},
|
{"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
|
||||||
{"s25sl032p"}, {"s25sl064p"}, {"s25fl256s0"}, {"s25fl256s1"},
|
{"s25fl064k"},
|
||||||
{"s25fl512s"}, {"s70fl01gs"}, {"s25sl12800"}, {"s25sl12801"},
|
{"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
|
||||||
{"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"}, {"s25sl008a"},
|
{"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"},
|
||||||
{"s25sl016a"}, {"s25sl032a"}, {"s25sl064a"}, {"s25fl008k"},
|
{"m25p64"}, {"m25p128"},
|
||||||
{"s25fl016k"}, {"s25fl064k"}, {"s25fl132k"},
|
{"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
|
||||||
{"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"},
|
{"w25q80bl"}, {"w25q128"}, {"w25q256"},
|
||||||
{"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"},
|
|
||||||
{"sst25wf040"},
|
/* Flashes that can't be detected using JEDEC */
|
||||||
{"m25p05"}, {"m25p10"}, {"m25p20"}, {"m25p40"},
|
|
||||||
{"m25p80"}, {"m25p16"}, {"m25p32"}, {"m25p64"},
|
|
||||||
{"m25p128"}, {"n25q032"},
|
|
||||||
{"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
|
{"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
|
||||||
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
|
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
|
||||||
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
|
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
|
||||||
{"m45pe10"}, {"m45pe80"}, {"m45pe16"},
|
|
||||||
{"m25pe20"}, {"m25pe80"}, {"m25pe16"},
|
|
||||||
{"m25px16"}, {"m25px32"}, {"m25px32-s0"}, {"m25px32-s1"},
|
|
||||||
{"m25px64"}, {"m25px80"},
|
|
||||||
{"w25x10"}, {"w25x20"}, {"w25x40"}, {"w25x80"},
|
|
||||||
{"w25x16"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
|
|
||||||
{"w25x64"}, {"w25q64"}, {"w25q80"}, {"w25q80bl"},
|
|
||||||
{"w25q128"}, {"w25q256"}, {"cat25c11"},
|
|
||||||
{"cat25c03"}, {"cat25c09"}, {"cat25c17"}, {"cat25128"},
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generic support for SPI NOR that can be identified by the JEDEC READ
|
* Generic support for SPI NOR that can be identified by the JEDEC READ
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* SMI (Serial Memory Controller) device driver for Serial NOR Flash on
|
* SMI (Serial Memory Controller) device driver for Serial NOR Flash on
|
||||||
* SPEAr platform
|
* SPEAr platform
|
||||||
* The serial nor interface is largely based on drivers/mtd/m25p80.c,
|
* The serial nor interface is largely based on m25p80.c, however the SPI
|
||||||
* however the SPI interface has been replaced by SMI.
|
* interface has been replaced by SMI.
|
||||||
*
|
*
|
||||||
* Copyright © 2010 STMicroelectronics.
|
* Copyright © 2010 STMicroelectronics.
|
||||||
* Ashish Priyadarshi
|
* Ashish Priyadarshi
|
||||||
|
|
|
@ -326,7 +326,7 @@ config MTD_BFIN_ASYNC
|
||||||
|
|
||||||
config MTD_GPIO_ADDR
|
config MTD_GPIO_ADDR
|
||||||
tristate "GPIO-assisted Flash Chip Support"
|
tristate "GPIO-assisted Flash Chip Support"
|
||||||
depends on GPIOLIB
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
depends on MTD_COMPLEX_MAPPINGS
|
depends on MTD_COMPLEX_MAPPINGS
|
||||||
help
|
help
|
||||||
Map driver which allows flashes to be partially physically addressed
|
Map driver which allows flashes to be partially physically addressed
|
||||||
|
|
|
@ -138,7 +138,7 @@ static int amd76xrom_init_one(struct pci_dev *pdev,
|
||||||
/*
|
/*
|
||||||
* Try to reserve the window mem region. If this fails then
|
* Try to reserve the window mem region. If this fails then
|
||||||
* it is likely due to a fragment of the window being
|
* it is likely due to a fragment of the window being
|
||||||
* "reseved" by the BIOS. In the case that the
|
* "reserved" by the BIOS. In the case that the
|
||||||
* request_mem_region() fails then once the rom size is
|
* request_mem_region() fails then once the rom size is
|
||||||
* discovered we will try to reserve the unreserved fragment.
|
* discovered we will try to reserve the unreserved fragment.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -38,9 +38,9 @@ static void nw_en_write(void)
|
||||||
* we want to write a bit pattern XXX1 to Xilinx to enable
|
* we want to write a bit pattern XXX1 to Xilinx to enable
|
||||||
* the write gate, which will be open for about the next 2ms.
|
* the write gate, which will be open for about the next 2ms.
|
||||||
*/
|
*/
|
||||||
spin_lock_irqsave(&nw_gpio_lock, flags);
|
raw_spin_lock_irqsave(&nw_gpio_lock, flags);
|
||||||
nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
|
nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
|
||||||
spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* let the ISA bus to catch on...
|
* let the ISA bus to catch on...
|
||||||
|
|
|
@ -234,7 +234,7 @@ static int esb2rom_init_one(struct pci_dev *pdev,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to reserve the window mem region. If this fails then
|
* Try to reserve the window mem region. If this fails then
|
||||||
* it is likely due to the window being "reseved" by the BIOS.
|
* it is likely due to the window being "reserved" by the BIOS.
|
||||||
*/
|
*/
|
||||||
window->rsrc.name = MOD_NAME;
|
window->rsrc.name = MOD_NAME;
|
||||||
window->rsrc.start = window->phys;
|
window->rsrc.start = window->phys;
|
||||||
|
|
|
@ -167,7 +167,7 @@ static int ichxrom_init_one(struct pci_dev *pdev,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to reserve the window mem region. If this fails then
|
* Try to reserve the window mem region. If this fails then
|
||||||
* it is likely due to the window being "reseved" by the BIOS.
|
* it is likely due to the window being "reserved" by the BIOS.
|
||||||
*/
|
*/
|
||||||
window->rsrc.name = MOD_NAME;
|
window->rsrc.name = MOD_NAME;
|
||||||
window->rsrc.start = window->phys;
|
window->rsrc.start = window->phys;
|
||||||
|
|
|
@ -45,7 +45,6 @@ struct ltq_mtd {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char ltq_map_name[] = "ltq_nor";
|
static const char ltq_map_name[] = "ltq_nor";
|
||||||
static const char * const ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL };
|
|
||||||
|
|
||||||
static map_word
|
static map_word
|
||||||
ltq_read16(struct map_info *map, unsigned long adr)
|
ltq_read16(struct map_info *map, unsigned long adr)
|
||||||
|
@ -168,8 +167,7 @@ ltq_mtd_probe(struct platform_device *pdev)
|
||||||
cfi->addr_unlock2 ^= 1;
|
cfi->addr_unlock2 ^= 1;
|
||||||
|
|
||||||
ppdata.of_node = pdev->dev.of_node;
|
ppdata.of_node = pdev->dev.of_node;
|
||||||
err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types,
|
err = mtd_device_parse_register(ltq_mtd->mtd, NULL, &ppdata, NULL, 0);
|
||||||
&ppdata, NULL, 0);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "failed to add partitions\n");
|
dev_err(&pdev->dev, "failed to add partitions\n");
|
||||||
goto err_destroy;
|
goto err_destroy;
|
||||||
|
|
|
@ -147,7 +147,7 @@ static void of_free_probes(const char * const *probes)
|
||||||
kfree(probes);
|
kfree(probes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct of_device_id of_flash_match[];
|
static const struct of_device_id of_flash_match[];
|
||||||
static int of_flash_probe(struct platform_device *dev)
|
static int of_flash_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
const char * const *part_probe_types;
|
const char * const *part_probe_types;
|
||||||
|
@ -327,7 +327,7 @@ static int of_flash_probe(struct platform_device *dev)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct of_device_id of_flash_match[] = {
|
static const struct of_device_id of_flash_match[] = {
|
||||||
{
|
{
|
||||||
.compatible = "cfi-flash",
|
.compatible = "cfi-flash",
|
||||||
.data = (void *)"cfi_probe",
|
.data = (void *)"cfi_probe",
|
||||||
|
|
|
@ -197,6 +197,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||||
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
|
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
|
||||||
|
|
||||||
mutex_lock(&dev->lock);
|
mutex_lock(&dev->lock);
|
||||||
|
mutex_lock(&mtd_table_mutex);
|
||||||
|
|
||||||
if (dev->open)
|
if (dev->open)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
@ -220,6 +221,7 @@ 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);
|
||||||
blktrans_dev_put(dev);
|
blktrans_dev_put(dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -230,6 +232,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||||
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);
|
||||||
blktrans_dev_put(dev);
|
blktrans_dev_put(dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -243,6 +246,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&dev->lock);
|
mutex_lock(&dev->lock);
|
||||||
|
mutex_lock(&mtd_table_mutex);
|
||||||
|
|
||||||
if (--dev->open)
|
if (--dev->open)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
@ -256,6 +260,7 @@ 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);
|
||||||
blktrans_dev_put(dev);
|
blktrans_dev_put(dev);
|
||||||
}
|
}
|
||||||
|
@ -273,7 +278,7 @@ static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||||
if (!dev->mtd)
|
if (!dev->mtd)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
|
ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY;
|
||||||
unlock:
|
unlock:
|
||||||
mutex_unlock(&dev->lock);
|
mutex_unlock(&dev->lock);
|
||||||
blktrans_dev_put(dev);
|
blktrans_dev_put(dev);
|
||||||
|
|
|
@ -48,14 +48,34 @@
|
||||||
static struct backing_dev_info mtd_bdi = {
|
static struct backing_dev_info mtd_bdi = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mtd_cls_suspend(struct device *dev, pm_message_t state);
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int mtd_cls_resume(struct device *dev);
|
|
||||||
|
static int mtd_cls_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return mtd ? mtd_suspend(mtd) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mtd_cls_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (mtd)
|
||||||
|
mtd_resume(mtd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(mtd_cls_pm_ops, mtd_cls_suspend, mtd_cls_resume);
|
||||||
|
#define MTD_CLS_PM_OPS (&mtd_cls_pm_ops)
|
||||||
|
#else
|
||||||
|
#define MTD_CLS_PM_OPS NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct class mtd_class = {
|
static struct class mtd_class = {
|
||||||
.name = "mtd",
|
.name = "mtd",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.suspend = mtd_cls_suspend,
|
.pm = MTD_CLS_PM_OPS,
|
||||||
.resume = mtd_cls_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_IDR(mtd_idr);
|
static DEFINE_IDR(mtd_idr);
|
||||||
|
@ -88,22 +108,6 @@ static void mtd_release(struct device *dev)
|
||||||
device_destroy(&mtd_class, index + 1);
|
device_destroy(&mtd_class, index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mtd_cls_suspend(struct device *dev, pm_message_t state)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
return mtd ? mtd_suspend(mtd) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mtd_cls_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
if (mtd)
|
|
||||||
mtd_resume(mtd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t mtd_type_show(struct device *dev,
|
static ssize_t mtd_type_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
|
@ -375,8 +379,7 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
|
||||||
*
|
*
|
||||||
* Add a device to the list of MTD devices present in the system, and
|
* Add a device to the list of MTD devices present in the system, and
|
||||||
* notify each currently active MTD 'user' of its arrival. Returns
|
* notify each currently active MTD 'user' of its arrival. Returns
|
||||||
* zero on success or 1 on failure, which currently will only happen
|
* zero on success or non-zero on failure.
|
||||||
* if there is insufficient memory or a sysfs error.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int add_mtd_device(struct mtd_info *mtd)
|
int add_mtd_device(struct mtd_info *mtd)
|
||||||
|
@ -390,8 +393,10 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||||
mutex_lock(&mtd_table_mutex);
|
mutex_lock(&mtd_table_mutex);
|
||||||
|
|
||||||
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
|
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
|
||||||
if (i < 0)
|
if (i < 0) {
|
||||||
|
error = i;
|
||||||
goto fail_locked;
|
goto fail_locked;
|
||||||
|
}
|
||||||
|
|
||||||
mtd->index = i;
|
mtd->index = i;
|
||||||
mtd->usecount = 0;
|
mtd->usecount = 0;
|
||||||
|
@ -420,6 +425,8 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||||
printk(KERN_WARNING
|
printk(KERN_WARNING
|
||||||
"%s: unlock failed, writes may not work\n",
|
"%s: unlock failed, writes may not work\n",
|
||||||
mtd->name);
|
mtd->name);
|
||||||
|
/* Ignore unlock failures? */
|
||||||
|
error = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Caller should have set dev.parent to match the
|
/* Caller should have set dev.parent to match the
|
||||||
|
@ -430,7 +437,8 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||||
mtd->dev.devt = MTD_DEVT(i);
|
mtd->dev.devt = MTD_DEVT(i);
|
||||||
dev_set_name(&mtd->dev, "mtd%d", i);
|
dev_set_name(&mtd->dev, "mtd%d", i);
|
||||||
dev_set_drvdata(&mtd->dev, mtd);
|
dev_set_drvdata(&mtd->dev, mtd);
|
||||||
if (device_register(&mtd->dev) != 0)
|
error = device_register(&mtd->dev);
|
||||||
|
if (error)
|
||||||
goto fail_added;
|
goto fail_added;
|
||||||
|
|
||||||
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
|
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
|
||||||
|
@ -454,7 +462,7 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||||
idr_remove(&mtd_idr, i);
|
idr_remove(&mtd_idr, i);
|
||||||
fail_locked:
|
fail_locked:
|
||||||
mutex_unlock(&mtd_table_mutex);
|
mutex_unlock(&mtd_table_mutex);
|
||||||
return 1;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -510,8 +518,8 @@ static int mtd_add_device_partitions(struct mtd_info *mtd,
|
||||||
|
|
||||||
if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
|
if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
|
||||||
ret = add_mtd_device(mtd);
|
ret = add_mtd_device(mtd);
|
||||||
if (ret == 1)
|
if (ret)
|
||||||
return -ENODEV;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbparts > 0) {
|
if (nbparts > 0) {
|
||||||
|
|
|
@ -76,7 +76,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||||
|
|
||||||
config MTD_NAND_GPIO
|
config MTD_NAND_GPIO
|
||||||
tristate "GPIO assisted NAND Flash driver"
|
tristate "GPIO assisted NAND Flash driver"
|
||||||
depends on GPIOLIB
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This enables a NAND flash driver where control signals are
|
This enables a NAND flash driver where control signals are
|
||||||
connected to GPIO pins, and commands and data are communicated
|
connected to GPIO pins, and commands and data are communicated
|
||||||
|
@ -394,6 +394,14 @@ config MTD_NAND_GPMI_NAND
|
||||||
block, such as SD card. So pay attention to it when you enable
|
block, such as SD card. So pay attention to it when you enable
|
||||||
the GPMI.
|
the GPMI.
|
||||||
|
|
||||||
|
config MTD_NAND_BRCMNAND
|
||||||
|
tristate "Broadcom STB NAND controller"
|
||||||
|
depends on ARM || MIPS
|
||||||
|
help
|
||||||
|
Enables the Broadcom NAND controller driver. The controller was
|
||||||
|
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||||
|
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
||||||
|
|
||||||
config MTD_NAND_BCM47XXNFLASH
|
config MTD_NAND_BCM47XXNFLASH
|
||||||
tristate "Support for NAND flash on BCM4706 BCMA bus"
|
tristate "Support for NAND flash on BCM4706 BCMA bus"
|
||||||
depends on BCMA_NFLASH
|
depends on BCMA_NFLASH
|
||||||
|
|
|
@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
|
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
|
||||||
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
|
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
|
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
|
||||||
|
|
||||||
nand-objs := nand_base.o nand_bbt.o nand_timings.o
|
nand-objs := nand_base.o nand_bbt.o nand_timings.o
|
||||||
|
|
6
drivers/mtd/nand/brcmnand/Makefile
Normal file
6
drivers/mtd/nand/brcmnand/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# link order matters; don't link the more generic brcmstb_nand.o before the
|
||||||
|
# more specific iproc_nand.o, for instance
|
||||||
|
obj-$(CONFIG_MTD_NAND_BRCMNAND) += iproc_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o
|
111
drivers/mtd/nand/brcmnand/bcm63138_nand.c
Normal file
111
drivers/mtd/nand/brcmnand/bcm63138_nand.c
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2015 Broadcom Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "brcmnand.h"
|
||||||
|
|
||||||
|
struct bcm63138_nand_soc_priv {
|
||||||
|
void __iomem *base;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BCM63138_NAND_INT_STATUS 0x00
|
||||||
|
#define BCM63138_NAND_INT_EN 0x04
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BCM63138_CTLRDY = BIT(4),
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
|
||||||
|
{
|
||||||
|
struct bcm63138_nand_soc_priv *priv = soc->priv;
|
||||||
|
void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
|
||||||
|
u32 val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
if (val & BCM63138_CTLRDY) {
|
||||||
|
brcmnand_writel(val & ~BCM63138_CTLRDY, mmio);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||||
|
{
|
||||||
|
struct bcm63138_nand_soc_priv *priv = soc->priv;
|
||||||
|
void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
|
||||||
|
u32 val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
if (en)
|
||||||
|
val |= BCM63138_CTLRDY;
|
||||||
|
else
|
||||||
|
val &= ~BCM63138_CTLRDY;
|
||||||
|
|
||||||
|
brcmnand_writel(val, mmio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm63138_nand_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct bcm63138_nand_soc_priv *priv;
|
||||||
|
struct brcmnand_soc *soc;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
|
||||||
|
if (!soc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base");
|
||||||
|
priv->base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(priv->base))
|
||||||
|
return PTR_ERR(priv->base);
|
||||||
|
|
||||||
|
soc->pdev = pdev;
|
||||||
|
soc->priv = priv;
|
||||||
|
soc->ctlrdy_ack = bcm63138_nand_intc_ack;
|
||||||
|
soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
|
||||||
|
|
||||||
|
return brcmnand_probe(pdev, soc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id bcm63138_nand_of_match[] = {
|
||||||
|
{ .compatible = "brcm,nand-bcm63138" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver bcm63138_nand_driver = {
|
||||||
|
.probe = bcm63138_nand_probe,
|
||||||
|
.remove = brcmnand_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "bcm63138_nand",
|
||||||
|
.pm = &brcmnand_pm_ops,
|
||||||
|
.of_match_table = bcm63138_nand_of_match,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
module_platform_driver(bcm63138_nand_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Brian Norris");
|
||||||
|
MODULE_DESCRIPTION("NAND driver for BCM63138");
|
2246
drivers/mtd/nand/brcmnand/brcmnand.c
Normal file
2246
drivers/mtd/nand/brcmnand/brcmnand.c
Normal file
File diff suppressed because it is too large
Load diff
73
drivers/mtd/nand/brcmnand/brcmnand.h
Normal file
73
drivers/mtd/nand/brcmnand/brcmnand.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2015 Broadcom Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BRCMNAND_H__
|
||||||
|
#define __BRCMNAND_H__
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
struct platform_device;
|
||||||
|
struct dev_pm_ops;
|
||||||
|
|
||||||
|
struct brcmnand_soc {
|
||||||
|
struct platform_device *pdev;
|
||||||
|
void *priv;
|
||||||
|
bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
|
||||||
|
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
|
||||||
|
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc)
|
||||||
|
{
|
||||||
|
if (soc && soc->prepare_data_bus)
|
||||||
|
soc->prepare_data_bus(soc, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc)
|
||||||
|
{
|
||||||
|
if (soc && soc->prepare_data_bus)
|
||||||
|
soc->prepare_data_bus(soc, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 brcmnand_readl(void __iomem *addr)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* MIPS endianness is configured by boot strap, which also reverses all
|
||||||
|
* bus endianness (i.e., big-endian CPU + big endian bus ==> native
|
||||||
|
* endian I/O).
|
||||||
|
*
|
||||||
|
* Other architectures (e.g., ARM) either do not support big endian, or
|
||||||
|
* else leave I/O in little endian mode.
|
||||||
|
*/
|
||||||
|
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
|
||||||
|
return __raw_readl(addr);
|
||||||
|
else
|
||||||
|
return readl_relaxed(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void brcmnand_writel(u32 val, void __iomem *addr)
|
||||||
|
{
|
||||||
|
/* See brcmnand_readl() comments */
|
||||||
|
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
|
||||||
|
__raw_writel(val, addr);
|
||||||
|
else
|
||||||
|
writel_relaxed(val, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
|
||||||
|
int brcmnand_remove(struct platform_device *pdev);
|
||||||
|
|
||||||
|
extern const struct dev_pm_ops brcmnand_pm_ops;
|
||||||
|
|
||||||
|
#endif /* __BRCMNAND_H__ */
|
44
drivers/mtd/nand/brcmnand/brcmstb_nand.c
Normal file
44
drivers/mtd/nand/brcmnand/brcmstb_nand.c
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2015 Broadcom Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#include "brcmnand.h"
|
||||||
|
|
||||||
|
static const struct of_device_id brcmstb_nand_of_match[] = {
|
||||||
|
{ .compatible = "brcm,brcmnand" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match);
|
||||||
|
|
||||||
|
static int brcmstb_nand_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return brcmnand_probe(pdev, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver brcmstb_nand_driver = {
|
||||||
|
.probe = brcmstb_nand_probe,
|
||||||
|
.remove = brcmnand_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "brcmstb_nand",
|
||||||
|
.pm = &brcmnand_pm_ops,
|
||||||
|
.of_match_table = brcmstb_nand_of_match,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
module_platform_driver(brcmstb_nand_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Brian Norris");
|
||||||
|
MODULE_DESCRIPTION("NAND driver for Broadcom STB chips");
|
150
drivers/mtd/nand/brcmnand/iproc_nand.c
Normal file
150
drivers/mtd/nand/brcmnand/iproc_nand.c
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2015 Broadcom Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "brcmnand.h"
|
||||||
|
|
||||||
|
struct iproc_nand_soc_priv {
|
||||||
|
void __iomem *idm_base;
|
||||||
|
void __iomem *ext_base;
|
||||||
|
spinlock_t idm_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IPROC_NAND_CTLR_READY_OFFSET 0x10
|
||||||
|
#define IPROC_NAND_CTLR_READY BIT(0)
|
||||||
|
|
||||||
|
#define IPROC_NAND_IO_CTRL_OFFSET 0x00
|
||||||
|
#define IPROC_NAND_APB_LE_MODE BIT(24)
|
||||||
|
#define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6)
|
||||||
|
|
||||||
|
static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
|
||||||
|
{
|
||||||
|
struct iproc_nand_soc_priv *priv = soc->priv;
|
||||||
|
void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;
|
||||||
|
u32 val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
if (val & IPROC_NAND_CTLR_READY) {
|
||||||
|
brcmnand_writel(IPROC_NAND_CTLR_READY, mmio);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||||
|
{
|
||||||
|
struct iproc_nand_soc_priv *priv = soc->priv;
|
||||||
|
void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
|
||||||
|
u32 val;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->idm_lock, flags);
|
||||||
|
|
||||||
|
val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
if (en)
|
||||||
|
val |= IPROC_NAND_INT_CTRL_READ_ENABLE;
|
||||||
|
else
|
||||||
|
val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE;
|
||||||
|
|
||||||
|
brcmnand_writel(val, mmio);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&priv->idm_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
|
||||||
|
{
|
||||||
|
struct iproc_nand_soc_priv *priv = soc->priv;
|
||||||
|
void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
|
||||||
|
u32 val;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->idm_lock, flags);
|
||||||
|
|
||||||
|
val = brcmnand_readl(mmio);
|
||||||
|
|
||||||
|
if (prepare)
|
||||||
|
val |= IPROC_NAND_APB_LE_MODE;
|
||||||
|
else
|
||||||
|
val &= ~IPROC_NAND_APB_LE_MODE;
|
||||||
|
|
||||||
|
brcmnand_writel(val, mmio);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&priv->idm_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iproc_nand_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct iproc_nand_soc_priv *priv;
|
||||||
|
struct brcmnand_soc *soc;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
|
||||||
|
if (!soc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(&priv->idm_lock);
|
||||||
|
|
||||||
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm");
|
||||||
|
priv->idm_base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(priv->idm_base))
|
||||||
|
return PTR_ERR(priv->idm_base);
|
||||||
|
|
||||||
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext");
|
||||||
|
priv->ext_base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_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_set_enabled = iproc_nand_intc_set;
|
||||||
|
soc->prepare_data_bus = iproc_nand_apb_access;
|
||||||
|
|
||||||
|
return brcmnand_probe(pdev, soc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id iproc_nand_of_match[] = {
|
||||||
|
{ .compatible = "brcm,nand-iproc" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, iproc_nand_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver iproc_nand_driver = {
|
||||||
|
.probe = iproc_nand_probe,
|
||||||
|
.remove = brcmnand_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "iproc_nand",
|
||||||
|
.pm = &brcmnand_pm_ops,
|
||||||
|
.of_match_table = iproc_nand_of_match,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
module_platform_driver(iproc_nand_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Brian Norris");
|
||||||
|
MODULE_AUTHOR("Ray Jui");
|
||||||
|
MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");
|
|
@ -237,17 +237,23 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||||
/* Enable the following for a flash based bad block table */
|
/* Enable the following for a flash based bad block table */
|
||||||
this->bbt_options = NAND_BBT_USE_FLASH;
|
this->bbt_options = NAND_BBT_USE_FLASH;
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
|
||||||
if (nand_scan(new_mtd, 1)) {
|
if (!new_mtd->name) {
|
||||||
err = -ENXIO;
|
err = -ENOMEM;
|
||||||
goto out_ior;
|
goto out_ior;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
|
/* Scan to find existence of the device */
|
||||||
|
if (nand_scan(new_mtd, 1)) {
|
||||||
|
err = -ENXIO;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
cs553x_mtd[cs] = new_mtd;
|
cs553x_mtd[cs] = new_mtd;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
kfree(new_mtd->name);
|
||||||
out_ior:
|
out_ior:
|
||||||
iounmap(this->IO_ADDR_R);
|
iounmap(this->IO_ADDR_R);
|
||||||
out_mtd:
|
out_mtd:
|
||||||
|
|
|
@ -69,6 +69,9 @@ struct doc_priv {
|
||||||
int mh0_page;
|
int mh0_page;
|
||||||
int mh1_page;
|
int mh1_page;
|
||||||
struct mtd_info *nextdoc;
|
struct mtd_info *nextdoc;
|
||||||
|
|
||||||
|
/* Handle the last stage of initialization (BBT scan, partitioning) */
|
||||||
|
int (*late_init)(struct mtd_info *mtd);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This is the syndrome computed by the HW ecc generator upon reading an empty
|
/* This is the syndrome computed by the HW ecc generator upon reading an empty
|
||||||
|
@ -1294,14 +1297,11 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
|
||||||
this->bbt_md = NULL;
|
this->bbt_md = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
|
ret = this->scan_bbt(mtd);
|
||||||
At least as nand_bbt.c is currently written. */
|
if (ret)
|
||||||
if ((ret = nand_scan_bbt(mtd, NULL)))
|
|
||||||
return ret;
|
return ret;
|
||||||
mtd_device_register(mtd, NULL, 0);
|
|
||||||
if (!no_autopart)
|
return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
|
||||||
mtd_device_register(mtd, parts, numparts);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init inftl_scan_bbt(struct mtd_info *mtd)
|
static int __init inftl_scan_bbt(struct mtd_info *mtd)
|
||||||
|
@ -1344,10 +1344,10 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
|
||||||
this->bbt_md->pattern = "TBB_SYSM";
|
this->bbt_md->pattern = "TBB_SYSM";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
|
ret = this->scan_bbt(mtd);
|
||||||
At least as nand_bbt.c is currently written. */
|
if (ret)
|
||||||
if ((ret = nand_scan_bbt(mtd, NULL)))
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
memset((char *)parts, 0, sizeof(parts));
|
memset((char *)parts, 0, sizeof(parts));
|
||||||
numparts = inftl_partscan(mtd, parts);
|
numparts = inftl_partscan(mtd, parts);
|
||||||
/* At least for now, require the INFTL Media Header. We could probably
|
/* At least for now, require the INFTL Media Header. We could probably
|
||||||
|
@ -1355,10 +1355,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
|
||||||
autopartitioning, but I want to give it more thought. */
|
autopartitioning, but I want to give it more thought. */
|
||||||
if (!numparts)
|
if (!numparts)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
mtd_device_register(mtd, NULL, 0);
|
return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
|
||||||
if (!no_autopart)
|
|
||||||
mtd_device_register(mtd, parts, numparts);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __init doc2000_init(struct mtd_info *mtd)
|
static inline int __init doc2000_init(struct mtd_info *mtd)
|
||||||
|
@ -1369,7 +1366,7 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
|
||||||
this->read_byte = doc2000_read_byte;
|
this->read_byte = doc2000_read_byte;
|
||||||
this->write_buf = doc2000_writebuf;
|
this->write_buf = doc2000_writebuf;
|
||||||
this->read_buf = doc2000_readbuf;
|
this->read_buf = doc2000_readbuf;
|
||||||
this->scan_bbt = nftl_scan_bbt;
|
doc->late_init = nftl_scan_bbt;
|
||||||
|
|
||||||
doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
|
doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
|
||||||
doc2000_count_chips(mtd);
|
doc2000_count_chips(mtd);
|
||||||
|
@ -1396,13 +1393,13 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
|
||||||
can have multiple chips. */
|
can have multiple chips. */
|
||||||
doc2000_count_chips(mtd);
|
doc2000_count_chips(mtd);
|
||||||
mtd->name = "DiskOnChip 2000 (INFTL Model)";
|
mtd->name = "DiskOnChip 2000 (INFTL Model)";
|
||||||
this->scan_bbt = inftl_scan_bbt;
|
doc->late_init = inftl_scan_bbt;
|
||||||
return (4 * doc->chips_per_floor);
|
return (4 * doc->chips_per_floor);
|
||||||
} else {
|
} else {
|
||||||
/* Bog-standard Millennium */
|
/* Bog-standard Millennium */
|
||||||
doc->chips_per_floor = 1;
|
doc->chips_per_floor = 1;
|
||||||
mtd->name = "DiskOnChip Millennium";
|
mtd->name = "DiskOnChip Millennium";
|
||||||
this->scan_bbt = nftl_scan_bbt;
|
doc->late_init = nftl_scan_bbt;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1415,7 +1412,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
|
||||||
this->read_byte = doc2001plus_read_byte;
|
this->read_byte = doc2001plus_read_byte;
|
||||||
this->write_buf = doc2001plus_writebuf;
|
this->write_buf = doc2001plus_writebuf;
|
||||||
this->read_buf = doc2001plus_readbuf;
|
this->read_buf = doc2001plus_readbuf;
|
||||||
this->scan_bbt = inftl_scan_bbt;
|
doc->late_init = inftl_scan_bbt;
|
||||||
this->cmd_ctrl = NULL;
|
this->cmd_ctrl = NULL;
|
||||||
this->select_chip = doc2001plus_select_chip;
|
this->select_chip = doc2001plus_select_chip;
|
||||||
this->cmdfunc = doc2001plus_command;
|
this->cmdfunc = doc2001plus_command;
|
||||||
|
@ -1591,6 +1588,8 @@ static int __init doc_probe(unsigned long physadr)
|
||||||
nand->ecc.bytes = 6;
|
nand->ecc.bytes = 6;
|
||||||
nand->ecc.strength = 2;
|
nand->ecc.strength = 2;
|
||||||
nand->bbt_options = NAND_BBT_USE_FLASH;
|
nand->bbt_options = NAND_BBT_USE_FLASH;
|
||||||
|
/* Skip the automatic BBT scan so we can run it manually */
|
||||||
|
nand->options |= NAND_SKIP_BBTSCAN;
|
||||||
|
|
||||||
doc->physadr = physadr;
|
doc->physadr = physadr;
|
||||||
doc->virtadr = virtadr;
|
doc->virtadr = virtadr;
|
||||||
|
@ -1608,7 +1607,7 @@ static int __init doc_probe(unsigned long physadr)
|
||||||
else
|
else
|
||||||
numchips = doc2001_init(mtd);
|
numchips = doc2001_init(mtd);
|
||||||
|
|
||||||
if ((ret = nand_scan(mtd, numchips))) {
|
if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) {
|
||||||
/* DBB note: i believe nand_release is necessary here, as
|
/* DBB note: i believe nand_release is necessary here, as
|
||||||
buffers may have been allocated in nand_base. Check with
|
buffers may have been allocated in nand_base. Check with
|
||||||
Thomas. FIX ME! */
|
Thomas. FIX ME! */
|
||||||
|
|
|
@ -562,6 +562,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
|
||||||
dma_cookie_t cookie;
|
dma_cookie_t cookie;
|
||||||
unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
|
unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
|
||||||
int ret;
|
int ret;
|
||||||
|
unsigned long time_left;
|
||||||
|
|
||||||
if (direction == DMA_TO_DEVICE)
|
if (direction == DMA_TO_DEVICE)
|
||||||
chan = host->write_dma_chan;
|
chan = host->write_dma_chan;
|
||||||
|
@ -601,14 +602,13 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
|
||||||
|
|
||||||
dma_async_issue_pending(chan);
|
dma_async_issue_pending(chan);
|
||||||
|
|
||||||
ret =
|
time_left =
|
||||||
wait_for_completion_timeout(&host->dma_access_complete,
|
wait_for_completion_timeout(&host->dma_access_complete,
|
||||||
msecs_to_jiffies(3000));
|
msecs_to_jiffies(3000));
|
||||||
if (ret <= 0) {
|
if (time_left == 0) {
|
||||||
dmaengine_terminate_all(chan);
|
dmaengine_terminate_all(chan);
|
||||||
dev_err(host->dev, "wait_for_completion_timeout\n");
|
dev_err(host->dev, "wait_for_completion_timeout\n");
|
||||||
if (!ret)
|
ret = -ETIMEDOUT;
|
||||||
ret = -ETIMEDOUT;
|
|
||||||
goto unmap_dma;
|
goto unmap_dma;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -837,7 +837,7 @@ static int mpc5121_nfc_remove(struct platform_device *op)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct of_device_id mpc5121_nfc_match[] = {
|
static const struct of_device_id mpc5121_nfc_match[] = {
|
||||||
{ .compatible = "fsl,mpc5121-nfc", },
|
{ .compatible = "fsl,mpc5121-nfc", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
|
@ -189,6 +189,7 @@ struct mxc_nand_host {
|
||||||
int clk_act;
|
int clk_act;
|
||||||
int irq;
|
int irq;
|
||||||
int eccsize;
|
int eccsize;
|
||||||
|
int used_oobsize;
|
||||||
int active_cs;
|
int active_cs;
|
||||||
|
|
||||||
struct completion op_completion;
|
struct completion op_completion;
|
||||||
|
@ -280,12 +281,44 @@ static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
|
||||||
*t++ = __raw_readl(s++);
|
*t++ = __raw_readl(s++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u16 *t = trg;
|
||||||
|
const __iomem u16 *s = src;
|
||||||
|
|
||||||
|
/* We assume that src (IO) is always 32bit aligned */
|
||||||
|
if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) {
|
||||||
|
memcpy32_fromio(trg, src, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < (size >> 1); i++)
|
||||||
|
*t++ = __raw_readw(s++);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
|
static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
|
||||||
{
|
{
|
||||||
/* __iowrite32_copy use 32bit size values so divide by 4 */
|
/* __iowrite32_copy use 32bit size values so divide by 4 */
|
||||||
__iowrite32_copy(trg, src, size / 4);
|
__iowrite32_copy(trg, src, size / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void memcpy16_toio(void __iomem *trg, const void *src, int size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
__iomem u16 *t = trg;
|
||||||
|
const u16 *s = src;
|
||||||
|
|
||||||
|
/* We assume that trg (IO) is always 32bit aligned */
|
||||||
|
if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) {
|
||||||
|
memcpy32_toio(trg, src, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < (size >> 1); i++)
|
||||||
|
__raw_writew(*s++, t++);
|
||||||
|
}
|
||||||
|
|
||||||
static int check_int_v3(struct mxc_nand_host *host)
|
static int check_int_v3(struct mxc_nand_host *host)
|
||||||
{
|
{
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
|
@ -807,32 +840,48 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Function to transfer data to/from spare area.
|
* The controller splits a page into data chunks of 512 bytes + partial oob.
|
||||||
|
* There are writesize / 512 such chunks, the size of the partial oob parts is
|
||||||
|
* oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
|
||||||
|
* contains additionally the byte lost by rounding (if any).
|
||||||
|
* This function handles the needed shuffling between host->data_buf (which
|
||||||
|
* holds a page in natural order, i.e. writesize bytes data + oobsize bytes
|
||||||
|
* spare) and the NFC buffer.
|
||||||
*/
|
*/
|
||||||
static void copy_spare(struct mtd_info *mtd, bool bfrom)
|
static void copy_spare(struct mtd_info *mtd, bool bfrom)
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
struct mxc_nand_host *host = this->priv;
|
struct mxc_nand_host *host = this->priv;
|
||||||
u16 i, j;
|
u16 i, oob_chunk_size;
|
||||||
u16 n = mtd->writesize >> 9;
|
u16 num_chunks = mtd->writesize / 512;
|
||||||
|
|
||||||
u8 *d = host->data_buf + mtd->writesize;
|
u8 *d = host->data_buf + mtd->writesize;
|
||||||
u8 __iomem *s = host->spare0;
|
u8 __iomem *s = host->spare0;
|
||||||
u16 t = host->devtype_data->spare_len;
|
u16 sparebuf_size = host->devtype_data->spare_len;
|
||||||
|
|
||||||
j = (mtd->oobsize / n >> 1) << 1;
|
/* size of oob chunk for all but possibly the last one */
|
||||||
|
oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
|
||||||
|
|
||||||
if (bfrom) {
|
if (bfrom) {
|
||||||
for (i = 0; i < n - 1; i++)
|
for (i = 0; i < num_chunks - 1; i++)
|
||||||
memcpy32_fromio(d + i * j, s + i * t, j);
|
memcpy16_fromio(d + i * oob_chunk_size,
|
||||||
|
s + i * sparebuf_size,
|
||||||
|
oob_chunk_size);
|
||||||
|
|
||||||
/* the last section */
|
/* the last chunk */
|
||||||
memcpy32_fromio(d + i * j, s + i * t, mtd->oobsize - i * j);
|
memcpy16_fromio(d + i * oob_chunk_size,
|
||||||
|
s + i * sparebuf_size,
|
||||||
|
host->used_oobsize - i * oob_chunk_size);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < n - 1; i++)
|
for (i = 0; i < num_chunks - 1; i++)
|
||||||
memcpy32_toio(&s[i * t], &d[i * j], j);
|
memcpy16_toio(&s[i * sparebuf_size],
|
||||||
|
&d[i * oob_chunk_size],
|
||||||
|
oob_chunk_size);
|
||||||
|
|
||||||
/* the last section */
|
/* the last chunk */
|
||||||
memcpy32_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j);
|
memcpy16_toio(&s[oob_chunk_size * sparebuf_size],
|
||||||
|
&d[i * oob_chunk_size],
|
||||||
|
host->used_oobsize - i * oob_chunk_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -911,6 +960,23 @@ static int get_eccsize(struct mtd_info *mtd)
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ecc_8bit_layout_4k(struct nand_ecclayout *layout)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
layout->eccbytes = 8*18;
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
for (j = 0; j < 18; j++)
|
||||||
|
layout->eccpos[i*18 + j] = i*26 + j + 7;
|
||||||
|
|
||||||
|
layout->oobfree[0].offset = 2;
|
||||||
|
layout->oobfree[0].length = 4;
|
||||||
|
for (i = 1; i < 8; i++) {
|
||||||
|
layout->oobfree[i].offset = i*26;
|
||||||
|
layout->oobfree[i].length = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void preset_v1(struct mtd_info *mtd)
|
static void preset_v1(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct nand_chip *nand_chip = mtd->priv;
|
struct nand_chip *nand_chip = mtd->priv;
|
||||||
|
@ -1350,7 +1416,7 @@ static inline int is_imx53_nfc(struct mxc_nand_host *host)
|
||||||
return host->devtype_data == &imx53_nand_devtype_data;
|
return host->devtype_data == &imx53_nand_devtype_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_device_id mxcnd_devtype[] = {
|
static const struct platform_device_id mxcnd_devtype[] = {
|
||||||
{
|
{
|
||||||
.name = "imx21-nand",
|
.name = "imx21-nand",
|
||||||
.driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
|
.driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
|
||||||
|
@ -1587,8 +1653,20 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if (mtd->writesize == 2048)
|
if (mtd->writesize == 2048)
|
||||||
this->ecc.layout = host->devtype_data->ecclayout_2k;
|
this->ecc.layout = host->devtype_data->ecclayout_2k;
|
||||||
else if (mtd->writesize == 4096)
|
else if (mtd->writesize == 4096) {
|
||||||
this->ecc.layout = host->devtype_data->ecclayout_4k;
|
this->ecc.layout = host->devtype_data->ecclayout_4k;
|
||||||
|
if (get_eccsize(mtd) == 8)
|
||||||
|
ecc_8bit_layout_4k(this->ecc.layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Experimentation shows that i.MX NFC can only handle up to 218 oob
|
||||||
|
* bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
|
||||||
|
* into copying invalid data to/from the spare IO buffer, as this
|
||||||
|
* might cause ECC data corruption when doing sub-page write to a
|
||||||
|
* partially written page.
|
||||||
|
*/
|
||||||
|
host->used_oobsize = min(mtd->oobsize, 218U);
|
||||||
|
|
||||||
if (this->ecc.mode == NAND_ECC_HW) {
|
if (this->ecc.mode == NAND_ECC_HW) {
|
||||||
if (is_imx21_nfc(host) || is_imx27_nfc(host))
|
if (is_imx21_nfc(host) || is_imx27_nfc(host))
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand.c
|
|
||||||
*
|
|
||||||
* Overview:
|
* Overview:
|
||||||
* This is the generic MTD driver for NAND flash devices. It should be
|
* This is the generic MTD driver for NAND flash devices. It should be
|
||||||
* capable of working with almost all NAND chips currently available.
|
* capable of working with almost all NAND chips currently available.
|
||||||
|
@ -48,6 +46,7 @@
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/of_mtd.h>
|
||||||
|
|
||||||
/* Define default oob placement schemes for large and small page devices */
|
/* Define default oob placement schemes for large and small page devices */
|
||||||
static struct nand_ecclayout nand_oob_8 = {
|
static struct nand_ecclayout nand_oob_8 = {
|
||||||
|
@ -2928,9 +2927,6 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
& ONFI_OPT_CMD_SET_GET_FEATURES))
|
& ONFI_OPT_CMD_SET_GET_FEATURES))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* clear the sub feature parameters */
|
|
||||||
memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
|
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
|
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
|
||||||
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
|
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
|
||||||
*subfeature_param++ = chip->read_byte(mtd);
|
*subfeature_param++ = chip->read_byte(mtd);
|
||||||
|
@ -3689,7 +3685,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||||
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
|
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
|
||||||
goto ident_done;
|
goto ident_done;
|
||||||
} else if (*dev_id == type->dev_id) {
|
} else if (*dev_id == type->dev_id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3798,6 +3794,39 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
struct device_node *dn)
|
||||||
|
{
|
||||||
|
int ecc_mode, ecc_strength, ecc_step;
|
||||||
|
|
||||||
|
if (of_get_nand_bus_width(dn) == 16)
|
||||||
|
chip->options |= NAND_BUSWIDTH_16;
|
||||||
|
|
||||||
|
if (of_get_nand_on_flash_bbt(dn))
|
||||||
|
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||||
|
|
||||||
|
ecc_mode = of_get_nand_ecc_mode(dn);
|
||||||
|
ecc_strength = of_get_nand_ecc_strength(dn);
|
||||||
|
ecc_step = of_get_nand_ecc_step_size(dn);
|
||||||
|
|
||||||
|
if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
|
||||||
|
(!(ecc_step >= 0) && ecc_strength >= 0)) {
|
||||||
|
pr_err("must set both strength and step size in DT\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ecc_mode >= 0)
|
||||||
|
chip->ecc.mode = ecc_mode;
|
||||||
|
|
||||||
|
if (ecc_strength >= 0)
|
||||||
|
chip->ecc.strength = ecc_strength;
|
||||||
|
|
||||||
|
if (ecc_step > 0)
|
||||||
|
chip->ecc.size = ecc_step;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_scan_ident - [NAND Interface] Scan for the NAND device
|
* nand_scan_ident - [NAND Interface] Scan for the NAND device
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
|
@ -3815,6 +3844,13 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||||
int i, nand_maf_id, nand_dev_id;
|
int i, nand_maf_id, nand_dev_id;
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
struct nand_flash_dev *type;
|
struct nand_flash_dev *type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (chip->dn) {
|
||||||
|
ret = nand_dt_init(mtd, chip, chip->dn);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set the default functions */
|
/* Set the default functions */
|
||||||
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand_bbt.c
|
|
||||||
*
|
|
||||||
* Overview:
|
* Overview:
|
||||||
* Bad block table support for the NAND driver
|
* Bad block table support for the NAND driver
|
||||||
*
|
*
|
||||||
|
@ -64,7 +62,6 @@
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/bbm.h>
|
#include <linux/mtd/bbm.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/mtd/nand_ecc.h>
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
|
@ -720,7 +717,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||||
/* Must we save the block contents? */
|
/* Must we save the block contents? */
|
||||||
if (td->options & NAND_BBT_SAVECONTENT) {
|
if (td->options & NAND_BBT_SAVECONTENT) {
|
||||||
/* Make it block aligned */
|
/* Make it block aligned */
|
||||||
to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));
|
to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1);
|
||||||
len = 1 << this->bbt_erase_shift;
|
len = 1 << this->bbt_erase_shift;
|
||||||
res = mtd_read(mtd, to, len, &retlen, buf);
|
res = mtd_read(mtd, to, len, &retlen, buf);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
@ -1075,10 +1072,10 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
* The bad block table memory is allocated here. It must be freed by calling
|
* The bad block table memory is allocated here. It must be freed by calling
|
||||||
* the nand_free_bbt function.
|
* the nand_free_bbt function.
|
||||||
*/
|
*/
|
||||||
int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
int len, res = 0;
|
int len, res;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
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;
|
||||||
|
@ -1099,10 +1096,9 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
if (!td) {
|
if (!td) {
|
||||||
if ((res = nand_memory_bbt(mtd, bd))) {
|
if ((res = nand_memory_bbt(mtd, bd))) {
|
||||||
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
|
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
|
||||||
kfree(this->bbt);
|
goto err;
|
||||||
this->bbt = NULL;
|
|
||||||
}
|
}
|
||||||
return res;
|
return 0;
|
||||||
}
|
}
|
||||||
verify_bbt_descr(mtd, td);
|
verify_bbt_descr(mtd, td);
|
||||||
verify_bbt_descr(mtd, md);
|
verify_bbt_descr(mtd, md);
|
||||||
|
@ -1112,9 +1108,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
len += (len >> this->page_shift) * mtd->oobsize;
|
len += (len >> this->page_shift) * mtd->oobsize;
|
||||||
buf = vmalloc(len);
|
buf = vmalloc(len);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
kfree(this->bbt);
|
res = -ENOMEM;
|
||||||
this->bbt = NULL;
|
goto err;
|
||||||
return -ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is the bbt at a given page? */
|
/* Is the bbt at a given page? */
|
||||||
|
@ -1126,6 +1121,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = check_create(mtd, buf, bd);
|
res = check_create(mtd, buf, bd);
|
||||||
|
if (res)
|
||||||
|
goto err;
|
||||||
|
|
||||||
/* Prevent the bbt regions from erasing / writing */
|
/* Prevent the bbt regions from erasing / writing */
|
||||||
mark_bbt_region(mtd, td);
|
mark_bbt_region(mtd, td);
|
||||||
|
@ -1133,6 +1130,11 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||||
mark_bbt_region(mtd, md);
|
mark_bbt_region(mtd, md);
|
||||||
|
|
||||||
vfree(buf);
|
vfree(buf);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
kfree(this->bbt);
|
||||||
|
this->bbt = NULL;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nandids.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
|
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
|
|
@ -743,6 +743,11 @@ static int init_nandsim(struct mtd_info *mtd)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
ns->partitions[i].name = get_partition_name(i);
|
ns->partitions[i].name = get_partition_name(i);
|
||||||
|
if (!ns->partitions[i].name) {
|
||||||
|
NS_ERR("unable to allocate memory.\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
ns->partitions[i].offset = next_offset;
|
ns->partitions[i].offset = next_offset;
|
||||||
ns->partitions[i].size = part_sz;
|
ns->partitions[i].size = part_sz;
|
||||||
next_offset += ns->partitions[i].size;
|
next_offset += ns->partitions[i].size;
|
||||||
|
@ -756,6 +761,11 @@ static int init_nandsim(struct mtd_info *mtd)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
ns->partitions[i].name = get_partition_name(i);
|
ns->partitions[i].name = get_partition_name(i);
|
||||||
|
if (!ns->partitions[i].name) {
|
||||||
|
NS_ERR("unable to allocate memory.\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
ns->partitions[i].offset = next_offset;
|
ns->partitions[i].offset = next_offset;
|
||||||
ns->partitions[i].size = remains;
|
ns->partitions[i].size = remains;
|
||||||
ns->nbparts += 1;
|
ns->nbparts += 1;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/ndfc.c
|
|
||||||
*
|
|
||||||
* Overview:
|
* Overview:
|
||||||
* Platform independent driver for NDFC (NanD Flash Controller)
|
* Platform independent driver for NDFC (NanD Flash Controller)
|
||||||
* integrated into EP440 cores
|
* integrated into EP440 cores
|
||||||
|
|
|
@ -24,8 +24,6 @@ struct plat_nand_data {
|
||||||
void __iomem *io_base;
|
void __iomem *io_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *part_probe_types[] = { "cmdlinepart", NULL };
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Probe for the NAND device.
|
* Probe for the NAND device.
|
||||||
*/
|
*/
|
||||||
|
@ -95,7 +93,7 @@ static int plat_nand_probe(struct platform_device *pdev)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
part_types = pdata->chip.part_probe_types ? : part_probe_types;
|
part_types = pdata->chip.part_probe_types;
|
||||||
|
|
||||||
ppdata.of_node = pdev->dev.of_node;
|
ppdata.of_node = pdev->dev.of_node;
|
||||||
err = mtd_device_parse_register(&data->mtd, part_types, &ppdata,
|
err = mtd_device_parse_register(&data->mtd, part_types, &ppdata,
|
||||||
|
|
|
@ -22,13 +22,14 @@
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_mtd.h>
|
#include <linux/of_mtd.h>
|
||||||
|
|
||||||
#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
|
#if defined(CONFIG_ARM) && (defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP))
|
||||||
#define ARCH_HAS_DMA
|
#define ARCH_HAS_DMA
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -483,7 +484,8 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
|
||||||
static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
||||||
{
|
{
|
||||||
if (info->ecc_bch) {
|
if (info->ecc_bch) {
|
||||||
int timeout;
|
u32 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* According to the datasheet, when reading from NDDB
|
* According to the datasheet, when reading from NDDB
|
||||||
|
@ -494,18 +496,14 @@ 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) {
|
||||||
__raw_readsl(info->mmio_base + NDDB, data, 8);
|
readsl(info->mmio_base + NDDB, data, 8);
|
||||||
|
|
||||||
for (timeout = 0;
|
ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
|
||||||
!(nand_readl(info, NDSR) & NDSR_RDDREQ);
|
val & NDSR_RDDREQ, 1000, 5000);
|
||||||
timeout++) {
|
if (ret) {
|
||||||
if (timeout >= 5) {
|
dev_err(&info->pdev->dev,
|
||||||
dev_err(&info->pdev->dev,
|
"Timeout on RDDREQ while draining the FIFO\n");
|
||||||
"Timeout on RDDREQ while draining the FIFO\n");
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mdelay(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data += 32;
|
data += 32;
|
||||||
|
@ -513,7 +511,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__raw_readsl(info->mmio_base + NDDB, data, len);
|
readsl(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)
|
||||||
|
@ -522,14 +520,14 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||||
|
|
||||||
switch (info->state) {
|
switch (info->state) {
|
||||||
case STATE_PIO_WRITING:
|
case STATE_PIO_WRITING:
|
||||||
__raw_writesl(info->mmio_base + NDDB,
|
writesl(info->mmio_base + NDDB,
|
||||||
info->data_buff + info->data_buff_pos,
|
info->data_buff + info->data_buff_pos,
|
||||||
DIV_ROUND_UP(do_bytes, 4));
|
DIV_ROUND_UP(do_bytes, 4));
|
||||||
|
|
||||||
if (info->oob_size > 0)
|
if (info->oob_size > 0)
|
||||||
__raw_writesl(info->mmio_base + NDDB,
|
writesl(info->mmio_base + NDDB,
|
||||||
info->oob_buff + info->oob_buff_pos,
|
info->oob_buff + info->oob_buff_pos,
|
||||||
DIV_ROUND_UP(info->oob_size, 4));
|
DIV_ROUND_UP(info->oob_size, 4));
|
||||||
break;
|
break;
|
||||||
case STATE_PIO_READING:
|
case STATE_PIO_READING:
|
||||||
drain_fifo(info,
|
drain_fifo(info,
|
||||||
|
@ -1630,8 +1628,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
info->pdev = pdev;
|
info->pdev = pdev;
|
||||||
info->variant = pxa3xx_nand_get_variant(pdev);
|
info->variant = pxa3xx_nand_get_variant(pdev);
|
||||||
for (cs = 0; cs < pdata->num_cs; cs++) {
|
for (cs = 0; cs < pdata->num_cs; cs++) {
|
||||||
mtd = (struct mtd_info *)((unsigned int)&info[1] +
|
mtd = (void *)&info[1] + (sizeof(*mtd) + sizeof(*host)) * cs;
|
||||||
(sizeof(*mtd) + sizeof(*host)) * cs);
|
|
||||||
chip = (struct nand_chip *)(&mtd[1]);
|
chip = (struct nand_chip *)(&mtd[1]);
|
||||||
host = (struct pxa3xx_nand_host *)chip;
|
host = (struct pxa3xx_nand_host *)chip;
|
||||||
info->host[cs] = host;
|
info->host[cs] = host;
|
||||||
|
|
|
@ -653,11 +653,15 @@ static int r852_register_nand_device(struct r852_device *dev)
|
||||||
if (sm_register_device(dev->mtd, dev->sm))
|
if (sm_register_device(dev->mtd, dev->sm))
|
||||||
goto error2;
|
goto error2;
|
||||||
|
|
||||||
if (device_create_file(&dev->mtd->dev, &dev_attr_media_type))
|
if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) {
|
||||||
message("can't create media type sysfs attribute");
|
message("can't create media type sysfs attribute");
|
||||||
|
goto error3;
|
||||||
|
}
|
||||||
|
|
||||||
dev->card_registred = 1;
|
dev->card_registred = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
error3:
|
||||||
|
nand_release(dev->mtd);
|
||||||
error2:
|
error2:
|
||||||
kfree(dev->mtd);
|
kfree(dev->mtd);
|
||||||
error1:
|
error1:
|
||||||
|
|
|
@ -1105,7 +1105,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
|
||||||
|
|
||||||
/* driver device registration */
|
/* driver device registration */
|
||||||
|
|
||||||
static struct platform_device_id s3c24xx_driver_ids[] = {
|
static const struct platform_device_id s3c24xx_driver_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "s3c2410-nand",
|
.name = "s3c2410-nand",
|
||||||
.driver_data = TYPE_S3C2410,
|
.driver_data = TYPE_S3C2410,
|
||||||
|
|
|
@ -160,14 +160,10 @@ static int xway_nand_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allow users to override the partition in DT using the cmdline */
|
|
||||||
static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL };
|
|
||||||
|
|
||||||
static struct platform_nand_data xway_nand_data = {
|
static struct platform_nand_data xway_nand_data = {
|
||||||
.chip = {
|
.chip = {
|
||||||
.nr_chips = 1,
|
.nr_chips = 1,
|
||||||
.chip_delay = 30,
|
.chip_delay = 30,
|
||||||
.part_probe_types = part_probes,
|
|
||||||
},
|
},
|
||||||
.ctrl = {
|
.ctrl = {
|
||||||
.probe = xway_nand_probe,
|
.probe = xway_nand_probe,
|
||||||
|
|
|
@ -1083,7 +1083,7 @@ static const struct dev_pm_ops s3c_pm_ops = {
|
||||||
.resume = s3c_pm_ops_resume,
|
.resume = s3c_pm_ops_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device_id s3c_onenand_driver_ids[] = {
|
static const struct platform_device_id s3c_onenand_driver_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "s3c6400-onenand",
|
.name = "s3c6400-onenand",
|
||||||
.driver_data = TYPE_S3C6400,
|
.driver_data = TYPE_S3C6400,
|
||||||
|
|
|
@ -662,7 +662,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct of_device_id fsl_qspi_dt_ids[] = {
|
static const struct of_device_id fsl_qspi_dt_ids[] = {
|
||||||
{ .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
|
{ .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
|
||||||
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
|
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
|
|
|
@ -513,6 +513,13 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
/* NOTE: double check command sets and memory organization when you add
|
/* NOTE: double check command sets and memory organization when you add
|
||||||
* more nor chips. This current list focusses on newer chips, which
|
* more nor chips. This current list focusses on newer chips, which
|
||||||
* have been converging on command sets which including JEDEC ID.
|
* have been converging on command sets which including JEDEC ID.
|
||||||
|
*
|
||||||
|
* All newly added entries should describe *hardware* and should use SECT_4K
|
||||||
|
* (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage
|
||||||
|
* scenarios excluding small sectors there is config option that can be
|
||||||
|
* disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS.
|
||||||
|
* For historical (and compatibility) reasons (before we got above config) some
|
||||||
|
* old entries may be missing 4K flag.
|
||||||
*/
|
*/
|
||||||
static const struct spi_device_id spi_nor_ids[] = {
|
static const struct spi_device_id spi_nor_ids[] = {
|
||||||
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
|
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
|
||||||
|
@ -538,7 +545,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||||
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
|
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
|
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
|
||||||
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
||||||
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, 0) },
|
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
|
|
||||||
/* ESMT */
|
/* ESMT */
|
||||||
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
|
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
|
@ -560,7 +567,11 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||||
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
|
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
|
||||||
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
|
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
|
||||||
|
|
||||||
|
/* ISSI */
|
||||||
|
{ "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) },
|
||||||
|
|
||||||
/* Macronix */
|
/* Macronix */
|
||||||
|
{ "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) },
|
||||||
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
|
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
|
||||||
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
|
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
{ "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
|
{ "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
|
||||||
|
@ -602,7 +613,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||||
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
|
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
|
||||||
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
||||||
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
||||||
{ "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
|
{ "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||||
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
|
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
|
||||||
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
|
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
|
||||||
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
|
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
|
||||||
|
@ -613,7 +624,8 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||||
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
|
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
|
||||||
{ "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, 0) },
|
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
|
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
|
|
||||||
/* 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) },
|
||||||
|
|
|
@ -272,12 +272,9 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
|
||||||
mutex_lock(&f->sem);
|
mutex_lock(&f->sem);
|
||||||
|
|
||||||
ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
|
ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
iget_failed(inode);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
inode->i_mode = jemode_to_cpu(latest_node.mode);
|
inode->i_mode = jemode_to_cpu(latest_node.mode);
|
||||||
i_uid_write(inode, je16_to_cpu(latest_node.uid));
|
i_uid_write(inode, je16_to_cpu(latest_node.uid));
|
||||||
i_gid_write(inode, je16_to_cpu(latest_node.gid));
|
i_gid_write(inode, je16_to_cpu(latest_node.gid));
|
||||||
|
|
|
@ -1203,17 +1203,13 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",
|
JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",
|
||||||
ret, retlen, sizeof(*latest_node));
|
ret, retlen, sizeof(*latest_node));
|
||||||
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
|
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
|
||||||
mutex_unlock(&f->sem);
|
return ret ? ret : -EIO;
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return ret?ret:-EIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
|
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
|
||||||
if (crc != je32_to_cpu(latest_node->node_crc)) {
|
if (crc != je32_to_cpu(latest_node->node_crc)) {
|
||||||
JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
|
JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
|
||||||
f->inocache->ino, ref_offset(rii.latest_ref));
|
f->inocache->ino, ref_offset(rii.latest_ref));
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1250,16 +1246,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
* keep in RAM to facilitate quick follow symlink
|
* keep in RAM to facilitate quick follow symlink
|
||||||
* operation. */
|
* operation. */
|
||||||
uint32_t csize = je32_to_cpu(latest_node->csize);
|
uint32_t csize = je32_to_cpu(latest_node->csize);
|
||||||
if (csize > JFFS2_MAX_NAME_LEN) {
|
if (csize > JFFS2_MAX_NAME_LEN)
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -ENAMETOOLONG;
|
return -ENAMETOOLONG;
|
||||||
}
|
|
||||||
f->target = kmalloc(csize + 1, GFP_KERNEL);
|
f->target = kmalloc(csize + 1, GFP_KERNEL);
|
||||||
if (!f->target) {
|
if (!f->target) {
|
||||||
JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);
|
JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1271,8 +1262,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
kfree(f->target);
|
kfree(f->target);
|
||||||
f->target = NULL;
|
f->target = NULL;
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1289,15 +1278,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
if (f->metadata) {
|
if (f->metadata) {
|
||||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
|
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
|
||||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (!frag_first(&f->fragtree)) {
|
if (!frag_first(&f->fragtree)) {
|
||||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
|
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
|
||||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
/* ASSERT: f->fraglist != NULL */
|
/* ASSERT: f->fraglist != NULL */
|
||||||
|
@ -1305,8 +1290,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n",
|
JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n",
|
||||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||||
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
|
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
|
||||||
mutex_unlock(&f->sem);
|
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
/* OK. We're happy */
|
/* OK. We're happy */
|
||||||
|
@ -1400,10 +1383,8 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
|
||||||
f->inocache = ic;
|
f->inocache = ic;
|
||||||
|
|
||||||
ret = jffs2_do_read_inode_internal(c, f, &n);
|
ret = jffs2_do_read_inode_internal(c, f, &n);
|
||||||
if (!ret) {
|
mutex_unlock(&f->sem);
|
||||||
mutex_unlock(&f->sem);
|
jffs2_do_clear_inode(c, f);
|
||||||
jffs2_do_clear_inode(c, f);
|
|
||||||
}
|
|
||||||
jffs2_xattr_do_crccheck_inode(c, ic);
|
jffs2_xattr_do_crccheck_inode(c, ic);
|
||||||
kfree (f);
|
kfree (f);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -296,183 +296,19 @@ struct cfi_private {
|
||||||
struct flchip chips[0]; /* per-chip data structure for each chip */
|
struct flchip chips[0]; /* per-chip data structure for each chip */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
|
||||||
* Returns the command address according to the given geometry.
|
struct map_info *map, struct cfi_private *cfi);
|
||||||
*/
|
|
||||||
static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
|
|
||||||
struct map_info *map, struct cfi_private *cfi)
|
|
||||||
{
|
|
||||||
unsigned bankwidth = map_bankwidth(map);
|
|
||||||
unsigned interleave = cfi_interleave(cfi);
|
|
||||||
unsigned type = cfi->device_type;
|
|
||||||
uint32_t addr;
|
|
||||||
|
|
||||||
addr = (cmd_ofs * type) * interleave;
|
|
||||||
|
|
||||||
/* Modify the unlock address if we are in compatibility mode.
|
map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi);
|
||||||
* For 16bit devices on 8 bit busses
|
|
||||||
* and 32bit devices on 16 bit busses
|
|
||||||
* set the low bit of the alternating bit sequence of the address.
|
|
||||||
*/
|
|
||||||
if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa))
|
|
||||||
addr |= (type >> 1)*interleave;
|
|
||||||
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Transforms the CFI command for the given geometry (bus width & interleave).
|
|
||||||
* It looks too long to be inline, but in the common case it should almost all
|
|
||||||
* get optimised away.
|
|
||||||
*/
|
|
||||||
static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
|
|
||||||
{
|
|
||||||
map_word val = { {0} };
|
|
||||||
int wordwidth, words_per_bus, chip_mode, chips_per_word;
|
|
||||||
unsigned long onecmd;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* We do it this way to give the compiler a fighting chance
|
|
||||||
of optimising away all the crap for 'bankwidth' larger than
|
|
||||||
an unsigned long, in the common case where that support is
|
|
||||||
disabled */
|
|
||||||
if (map_bankwidth_is_large(map)) {
|
|
||||||
wordwidth = sizeof(unsigned long);
|
|
||||||
words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
|
|
||||||
} else {
|
|
||||||
wordwidth = map_bankwidth(map);
|
|
||||||
words_per_bus = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
|
|
||||||
chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
|
|
||||||
|
|
||||||
/* First, determine what the bit-pattern should be for a single
|
|
||||||
device, according to chip mode and endianness... */
|
|
||||||
switch (chip_mode) {
|
|
||||||
default: BUG();
|
|
||||||
case 1:
|
|
||||||
onecmd = cmd;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
onecmd = cpu_to_cfi16(map, cmd);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
onecmd = cpu_to_cfi32(map, cmd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now replicate it across the size of an unsigned long, or
|
|
||||||
just to the bus width as appropriate */
|
|
||||||
switch (chips_per_word) {
|
|
||||||
default: BUG();
|
|
||||||
#if BITS_PER_LONG >= 64
|
|
||||||
case 8:
|
|
||||||
onecmd |= (onecmd << (chip_mode * 32));
|
|
||||||
#endif
|
|
||||||
case 4:
|
|
||||||
onecmd |= (onecmd << (chip_mode * 16));
|
|
||||||
case 2:
|
|
||||||
onecmd |= (onecmd << (chip_mode * 8));
|
|
||||||
case 1:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* And finally, for the multi-word case, replicate it
|
|
||||||
in all words in the structure */
|
|
||||||
for (i=0; i < words_per_bus; i++) {
|
|
||||||
val.x[i] = onecmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
#define CMD(x) cfi_build_cmd((x), map, cfi)
|
#define CMD(x) cfi_build_cmd((x), map, cfi)
|
||||||
|
|
||||||
|
unsigned long cfi_merge_status(map_word val, struct map_info *map,
|
||||||
static inline unsigned long cfi_merge_status(map_word val, struct map_info *map,
|
struct cfi_private *cfi);
|
||||||
struct cfi_private *cfi)
|
|
||||||
{
|
|
||||||
int wordwidth, words_per_bus, chip_mode, chips_per_word;
|
|
||||||
unsigned long onestat, res = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* We do it this way to give the compiler a fighting chance
|
|
||||||
of optimising away all the crap for 'bankwidth' larger than
|
|
||||||
an unsigned long, in the common case where that support is
|
|
||||||
disabled */
|
|
||||||
if (map_bankwidth_is_large(map)) {
|
|
||||||
wordwidth = sizeof(unsigned long);
|
|
||||||
words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
|
|
||||||
} else {
|
|
||||||
wordwidth = map_bankwidth(map);
|
|
||||||
words_per_bus = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
|
|
||||||
chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
|
|
||||||
|
|
||||||
onestat = val.x[0];
|
|
||||||
/* Or all status words together */
|
|
||||||
for (i=1; i < words_per_bus; i++) {
|
|
||||||
onestat |= val.x[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
res = onestat;
|
|
||||||
switch(chips_per_word) {
|
|
||||||
default: BUG();
|
|
||||||
#if BITS_PER_LONG >= 64
|
|
||||||
case 8:
|
|
||||||
res |= (onestat >> (chip_mode * 32));
|
|
||||||
#endif
|
|
||||||
case 4:
|
|
||||||
res |= (onestat >> (chip_mode * 16));
|
|
||||||
case 2:
|
|
||||||
res |= (onestat >> (chip_mode * 8));
|
|
||||||
case 1:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Last, determine what the bit-pattern should be for a single
|
|
||||||
device, according to chip mode and endianness... */
|
|
||||||
switch (chip_mode) {
|
|
||||||
case 1:
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
res = cfi16_to_cpu(map, res);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
res = cfi32_to_cpu(map, res);
|
|
||||||
break;
|
|
||||||
default: BUG();
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MERGESTATUS(x) cfi_merge_status((x), map, cfi)
|
#define MERGESTATUS(x) cfi_merge_status((x), map, cfi)
|
||||||
|
|
||||||
|
uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
|
||||||
/*
|
|
||||||
* Sends a CFI command to a bank of flash for the given geometry.
|
|
||||||
*
|
|
||||||
* Returns the offset in flash where the command was written.
|
|
||||||
* If prev_val is non-null, it will be set to the value at the command address,
|
|
||||||
* before the command was written.
|
|
||||||
*/
|
|
||||||
static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
|
|
||||||
struct map_info *map, struct cfi_private *cfi,
|
struct map_info *map, struct cfi_private *cfi,
|
||||||
int type, map_word *prev_val)
|
int type, map_word *prev_val);
|
||||||
{
|
|
||||||
map_word val;
|
|
||||||
uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi);
|
|
||||||
val = cfi_build_cmd(cmd, map, cfi);
|
|
||||||
|
|
||||||
if (prev_val)
|
|
||||||
*prev_val = map_read(map, addr);
|
|
||||||
|
|
||||||
map_write(map, val, addr);
|
|
||||||
|
|
||||||
return addr - base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
|
static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
|
||||||
{
|
{
|
||||||
|
@ -506,15 +342,7 @@ static inline uint16_t cfi_read_query16(struct map_info *map, uint32_t addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cfi_udelay(int us)
|
void cfi_udelay(int us);
|
||||||
{
|
|
||||||
if (us >= 1000) {
|
|
||||||
msleep((us+999)/1000);
|
|
||||||
} else {
|
|
||||||
udelay(us);
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int __xipram cfi_qry_present(struct map_info *map, __u32 base,
|
int __xipram cfi_qry_present(struct map_info *map, __u32 base,
|
||||||
struct cfi_private *cfi);
|
struct cfi_private *cfi);
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
struct mtd_info;
|
struct mtd_info;
|
||||||
struct nand_flash_dev;
|
struct nand_flash_dev;
|
||||||
|
struct device_node;
|
||||||
|
|
||||||
/* Scan and identify a NAND device */
|
/* Scan and identify a NAND device */
|
||||||
extern int nand_scan(struct mtd_info *mtd, int max_chips);
|
extern int nand_scan(struct mtd_info *mtd, int max_chips);
|
||||||
/*
|
/*
|
||||||
|
@ -542,6 +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
|
||||||
* @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
|
||||||
|
@ -644,6 +647,8 @@ 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;
|
||||||
|
|
||||||
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);
|
||||||
void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
|
void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
|
||||||
|
@ -833,7 +838,6 @@ struct nand_manufacturers {
|
||||||
extern struct nand_flash_dev nand_flash_ids[];
|
extern struct nand_flash_dev nand_flash_ids[];
|
||||||
extern struct nand_manufacturers nand_manuf_ids[];
|
extern struct nand_manufacturers nand_manuf_ids[];
|
||||||
|
|
||||||
extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
|
||||||
extern int nand_default_bbt(struct mtd_info *mtd);
|
extern int nand_default_bbt(struct mtd_info *mtd);
|
||||||
extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
||||||
extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs);
|
extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs);
|
||||||
|
|
Loading…
Reference in a new issue