mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +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
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: 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)
|
||||
M: Rafał Miłecki <zajec5@gmail.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
|
@ -16,6 +16,7 @@ config MTD_CFI
|
||||
config MTD_JEDECPROBE
|
||||
tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
|
||||
select MTD_GEN_PROBE
|
||||
select MTD_CFI_UTIL
|
||||
help
|
||||
This option enables JEDEC-style probing of flash chips which are not
|
||||
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);
|
||||
int gap = adr - bus_ofs;
|
||||
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)) {
|
||||
/* partial write of a word, load old contents */
|
||||
|
@ -23,6 +23,194 @@
|
||||
#include <linux/mtd/map.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,
|
||||
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
|
||||
* @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;
|
||||
int cfg;
|
||||
@ -1828,6 +1828,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||
case DOC_CHIPID_G3:
|
||||
mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
|
||||
docg3->device_id);
|
||||
if (!mtd->name)
|
||||
return -ENOMEM;
|
||||
docg3->max_block = 2047;
|
||||
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->ecclayout = &docg3_oobinfo;
|
||||
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;
|
||||
if (chip_id != (u16)(~chip_id_inv)) {
|
||||
goto nomem3;
|
||||
goto nomem4;
|
||||
}
|
||||
|
||||
switch (chip_id) {
|
||||
@ -1910,15 +1914,19 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
|
||||
break;
|
||||
default:
|
||||
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_reload_bbt(docg3);
|
||||
return mtd;
|
||||
|
||||
nomem4:
|
||||
kfree(docg3->bbt);
|
||||
nomem3:
|
||||
kfree(mtd);
|
||||
nomem2:
|
||||
@ -2117,7 +2125,7 @@ static int docg3_release(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#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" },
|
||||
{}
|
||||
};
|
||||
|
@ -261,45 +261,33 @@ static int m25p_remove(struct spi_device *spi)
|
||||
* keep them available as module aliases for existing platforms.
|
||||
*/
|
||||
static const struct spi_device_id m25p_ids[] = {
|
||||
{"at25fs010"}, {"at25fs040"}, {"at25df041a"}, {"at25df321a"},
|
||||
{"at25df641"}, {"at26f004"}, {"at26df081a"}, {"at26df161a"},
|
||||
{"at26df321"}, {"at45db081d"},
|
||||
{"en25f32"}, {"en25p32"}, {"en25q32b"}, {"en25p64"},
|
||||
{"en25q64"}, {"en25qh128"}, {"en25qh256"},
|
||||
{"f25l32pa"},
|
||||
{"mr25h256"}, {"mr25h10"},
|
||||
{"gd25q32"}, {"gd25q64"},
|
||||
{"160s33b"}, {"320s33b"}, {"640s33b"},
|
||||
{"mx25l2005a"}, {"mx25l4005a"}, {"mx25l8005"}, {"mx25l1606e"},
|
||||
{"mx25l3205d"}, {"mx25l3255e"}, {"mx25l6405d"}, {"mx25l12805d"},
|
||||
{"mx25l12855e"},{"mx25l25635e"},{"mx25l25655e"},{"mx66l51235l"},
|
||||
{"mx66l1g55g"},
|
||||
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q256a"},
|
||||
{"n25q512a"}, {"n25q512ax3"}, {"n25q00"},
|
||||
{"pm25lv512"}, {"pm25lv010"}, {"pm25lq032"},
|
||||
{"s25sl032p"}, {"s25sl064p"}, {"s25fl256s0"}, {"s25fl256s1"},
|
||||
{"s25fl512s"}, {"s70fl01gs"}, {"s25sl12800"}, {"s25sl12801"},
|
||||
{"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"}, {"s25sl008a"},
|
||||
{"s25sl016a"}, {"s25sl032a"}, {"s25sl064a"}, {"s25fl008k"},
|
||||
{"s25fl016k"}, {"s25fl064k"}, {"s25fl132k"},
|
||||
{"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"},
|
||||
{"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"},
|
||||
{"sst25wf040"},
|
||||
{"m25p05"}, {"m25p10"}, {"m25p20"}, {"m25p40"},
|
||||
{"m25p80"}, {"m25p16"}, {"m25p32"}, {"m25p64"},
|
||||
{"m25p128"}, {"n25q032"},
|
||||
/*
|
||||
* Entries not used in DTs that should be safe to drop after replacing
|
||||
* them with "nor-jedec" in platform data.
|
||||
*/
|
||||
{"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
|
||||
|
||||
/*
|
||||
* Entries that were used in DTs without "nor-jedec" fallback and should
|
||||
* be kept for backward compatibility.
|
||||
*/
|
||||
{"at25df321a"}, {"at25df641"}, {"at26df081a"},
|
||||
{"mr25h256"},
|
||||
{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
|
||||
{"mx25l25635e"},{"mx66l51235l"},
|
||||
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
|
||||
{"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
|
||||
{"s25fl064k"},
|
||||
{"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
|
||||
{"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"},
|
||||
{"m25p64"}, {"m25p128"},
|
||||
{"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
|
||||
{"w25q80bl"}, {"w25q128"}, {"w25q256"},
|
||||
|
||||
/* Flashes that can't be detected using JEDEC */
|
||||
{"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
|
||||
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-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
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
* SMI (Serial Memory Controller) device driver for Serial NOR Flash on
|
||||
* SPEAr platform
|
||||
* The serial nor interface is largely based on drivers/mtd/m25p80.c,
|
||||
* however the SPI interface has been replaced by SMI.
|
||||
* The serial nor interface is largely based on m25p80.c, however the SPI
|
||||
* interface has been replaced by SMI.
|
||||
*
|
||||
* Copyright © 2010 STMicroelectronics.
|
||||
* Ashish Priyadarshi
|
||||
|
@ -326,7 +326,7 @@ config MTD_BFIN_ASYNC
|
||||
|
||||
config MTD_GPIO_ADDR
|
||||
tristate "GPIO-assisted Flash Chip Support"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
* 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);
|
||||
spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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.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
|
||||
* 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.start = window->phys;
|
||||
|
@ -45,7 +45,6 @@ struct ltq_mtd {
|
||||
};
|
||||
|
||||
static const char ltq_map_name[] = "ltq_nor";
|
||||
static const char * const ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL };
|
||||
|
||||
static map_word
|
||||
ltq_read16(struct map_info *map, unsigned long adr)
|
||||
@ -168,8 +167,7 @@ ltq_mtd_probe(struct platform_device *pdev)
|
||||
cfi->addr_unlock2 ^= 1;
|
||||
|
||||
ppdata.of_node = pdev->dev.of_node;
|
||||
err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types,
|
||||
&ppdata, NULL, 0);
|
||||
err = mtd_device_parse_register(ltq_mtd->mtd, NULL, &ppdata, NULL, 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to add partitions\n");
|
||||
goto err_destroy;
|
||||
|
@ -147,7 +147,7 @@ static void of_free_probes(const char * const *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)
|
||||
{
|
||||
const char * const *part_probe_types;
|
||||
@ -327,7 +327,7 @@ err_flash_remove:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct of_device_id of_flash_match[] = {
|
||||
static const struct of_device_id of_flash_match[] = {
|
||||
{
|
||||
.compatible = "cfi-flash",
|
||||
.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*/
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
mutex_lock(&mtd_table_mutex);
|
||||
|
||||
if (dev->open)
|
||||
goto unlock;
|
||||
@ -220,6 +221,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||
|
||||
unlock:
|
||||
dev->open++;
|
||||
mutex_unlock(&mtd_table_mutex);
|
||||
mutex_unlock(&dev->lock);
|
||||
blktrans_dev_put(dev);
|
||||
return ret;
|
||||
@ -230,6 +232,7 @@ error_release:
|
||||
error_put:
|
||||
module_put(dev->tr->owner);
|
||||
kref_put(&dev->ref, blktrans_dev_release);
|
||||
mutex_unlock(&mtd_table_mutex);
|
||||
mutex_unlock(&dev->lock);
|
||||
blktrans_dev_put(dev);
|
||||
return ret;
|
||||
@ -243,6 +246,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
mutex_lock(&mtd_table_mutex);
|
||||
|
||||
if (--dev->open)
|
||||
goto unlock;
|
||||
@ -256,6 +260,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
|
||||
__put_mtd_device(dev->mtd);
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&mtd_table_mutex);
|
||||
mutex_unlock(&dev->lock);
|
||||
blktrans_dev_put(dev);
|
||||
}
|
||||
@ -273,7 +278,7 @@ static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||
if (!dev->mtd)
|
||||
goto unlock;
|
||||
|
||||
ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
|
||||
ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY;
|
||||
unlock:
|
||||
mutex_unlock(&dev->lock);
|
||||
blktrans_dev_put(dev);
|
||||
|
@ -48,14 +48,34 @@
|
||||
static struct backing_dev_info mtd_bdi = {
|
||||
};
|
||||
|
||||
static int mtd_cls_suspend(struct device *dev, pm_message_t state);
|
||||
static int mtd_cls_resume(struct device *dev);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
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 = {
|
||||
.name = "mtd",
|
||||
.owner = THIS_MODULE,
|
||||
.suspend = mtd_cls_suspend,
|
||||
.resume = mtd_cls_resume,
|
||||
.pm = MTD_CLS_PM_OPS,
|
||||
};
|
||||
|
||||
static DEFINE_IDR(mtd_idr);
|
||||
@ -88,22 +108,6 @@ static void mtd_release(struct device *dev)
|
||||
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,
|
||||
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
|
||||
* notify each currently active MTD 'user' of its arrival. Returns
|
||||
* zero on success or 1 on failure, which currently will only happen
|
||||
* if there is insufficient memory or a sysfs error.
|
||||
* zero on success or non-zero on failure.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
|
||||
if (i < 0)
|
||||
if (i < 0) {
|
||||
error = i;
|
||||
goto fail_locked;
|
||||
}
|
||||
|
||||
mtd->index = i;
|
||||
mtd->usecount = 0;
|
||||
@ -420,6 +425,8 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||
printk(KERN_WARNING
|
||||
"%s: unlock failed, writes may not work\n",
|
||||
mtd->name);
|
||||
/* Ignore unlock failures? */
|
||||
error = 0;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
dev_set_name(&mtd->dev, "mtd%d", i);
|
||||
dev_set_drvdata(&mtd->dev, mtd);
|
||||
if (device_register(&mtd->dev) != 0)
|
||||
error = device_register(&mtd->dev);
|
||||
if (error)
|
||||
goto fail_added;
|
||||
|
||||
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
|
||||
@ -454,7 +462,7 @@ fail_added:
|
||||
idr_remove(&mtd_idr, i);
|
||||
fail_locked:
|
||||
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)) {
|
||||
ret = add_mtd_device(mtd);
|
||||
if (ret == 1)
|
||||
return -ENODEV;
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (nbparts > 0) {
|
||||
|
@ -76,7 +76,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||
|
||||
config MTD_NAND_GPIO
|
||||
tristate "GPIO assisted NAND Flash driver"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
This enables a NAND flash driver where control signals are
|
||||
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
|
||||
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
|
||||
tristate "Support for NAND flash on BCM4706 BCMA bus"
|
||||
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_SUNXI) += sunxi_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
|
||||
|
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 */
|
||||
this->bbt_options = NAND_BBT_USE_FLASH;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(new_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
|
||||
if (!new_mtd->name) {
|
||||
err = -ENOMEM;
|
||||
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;
|
||||
goto out;
|
||||
|
||||
out_free:
|
||||
kfree(new_mtd->name);
|
||||
out_ior:
|
||||
iounmap(this->IO_ADDR_R);
|
||||
out_mtd:
|
||||
|
@ -69,6 +69,9 @@ struct doc_priv {
|
||||
int mh0_page;
|
||||
int mh1_page;
|
||||
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
|
||||
@ -1294,14 +1297,11 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
|
||||
this->bbt_md = NULL;
|
||||
}
|
||||
|
||||
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
|
||||
At least as nand_bbt.c is currently written. */
|
||||
if ((ret = nand_scan_bbt(mtd, NULL)))
|
||||
ret = this->scan_bbt(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
mtd_device_register(mtd, NULL, 0);
|
||||
if (!no_autopart)
|
||||
mtd_device_register(mtd, parts, numparts);
|
||||
return 0;
|
||||
|
||||
return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
|
||||
At least as nand_bbt.c is currently written. */
|
||||
if ((ret = nand_scan_bbt(mtd, NULL)))
|
||||
ret = this->scan_bbt(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memset((char *)parts, 0, sizeof(parts));
|
||||
numparts = inftl_partscan(mtd, parts);
|
||||
/* 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. */
|
||||
if (!numparts)
|
||||
return -EIO;
|
||||
mtd_device_register(mtd, NULL, 0);
|
||||
if (!no_autopart)
|
||||
mtd_device_register(mtd, parts, numparts);
|
||||
return 0;
|
||||
return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
|
||||
}
|
||||
|
||||
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->write_buf = doc2000_writebuf;
|
||||
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;
|
||||
doc2000_count_chips(mtd);
|
||||
@ -1396,13 +1393,13 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
|
||||
can have multiple chips. */
|
||||
doc2000_count_chips(mtd);
|
||||
mtd->name = "DiskOnChip 2000 (INFTL Model)";
|
||||
this->scan_bbt = inftl_scan_bbt;
|
||||
doc->late_init = inftl_scan_bbt;
|
||||
return (4 * doc->chips_per_floor);
|
||||
} else {
|
||||
/* Bog-standard Millennium */
|
||||
doc->chips_per_floor = 1;
|
||||
mtd->name = "DiskOnChip Millennium";
|
||||
this->scan_bbt = nftl_scan_bbt;
|
||||
doc->late_init = nftl_scan_bbt;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -1415,7 +1412,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
|
||||
this->read_byte = doc2001plus_read_byte;
|
||||
this->write_buf = doc2001plus_writebuf;
|
||||
this->read_buf = doc2001plus_readbuf;
|
||||
this->scan_bbt = inftl_scan_bbt;
|
||||
doc->late_init = inftl_scan_bbt;
|
||||
this->cmd_ctrl = NULL;
|
||||
this->select_chip = doc2001plus_select_chip;
|
||||
this->cmdfunc = doc2001plus_command;
|
||||
@ -1591,6 +1588,8 @@ static int __init doc_probe(unsigned long physadr)
|
||||
nand->ecc.bytes = 6;
|
||||
nand->ecc.strength = 2;
|
||||
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->virtadr = virtadr;
|
||||
@ -1608,7 +1607,7 @@ static int __init doc_probe(unsigned long physadr)
|
||||
else
|
||||
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
|
||||
buffers may have been allocated in nand_base. Check with
|
||||
Thomas. FIX ME! */
|
||||
|
@ -562,6 +562,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
|
||||
dma_cookie_t cookie;
|
||||
unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
|
||||
int ret;
|
||||
unsigned long time_left;
|
||||
|
||||
if (direction == DMA_TO_DEVICE)
|
||||
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);
|
||||
|
||||
ret =
|
||||
time_left =
|
||||
wait_for_completion_timeout(&host->dma_access_complete,
|
||||
msecs_to_jiffies(3000));
|
||||
if (ret <= 0) {
|
||||
if (time_left == 0) {
|
||||
dmaengine_terminate_all(chan);
|
||||
dev_err(host->dev, "wait_for_completion_timeout\n");
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
ret = -ETIMEDOUT;
|
||||
goto unmap_dma;
|
||||
}
|
||||
|
||||
|
@ -837,7 +837,7 @@ static int mpc5121_nfc_remove(struct platform_device *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id mpc5121_nfc_match[] = {
|
||||
static const struct of_device_id mpc5121_nfc_match[] = {
|
||||
{ .compatible = "fsl,mpc5121-nfc", },
|
||||
{},
|
||||
};
|
||||
|
@ -189,6 +189,7 @@ struct mxc_nand_host {
|
||||
int clk_act;
|
||||
int irq;
|
||||
int eccsize;
|
||||
int used_oobsize;
|
||||
int active_cs;
|
||||
|
||||
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++);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* __iowrite32_copy use 32bit size values so divide by 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct mxc_nand_host *host = this->priv;
|
||||
u16 i, j;
|
||||
u16 n = mtd->writesize >> 9;
|
||||
u16 i, oob_chunk_size;
|
||||
u16 num_chunks = mtd->writesize / 512;
|
||||
|
||||
u8 *d = host->data_buf + mtd->writesize;
|
||||
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) {
|
||||
for (i = 0; i < n - 1; i++)
|
||||
memcpy32_fromio(d + i * j, s + i * t, j);
|
||||
for (i = 0; i < num_chunks - 1; i++)
|
||||
memcpy16_fromio(d + i * oob_chunk_size,
|
||||
s + i * sparebuf_size,
|
||||
oob_chunk_size);
|
||||
|
||||
/* the last section */
|
||||
memcpy32_fromio(d + i * j, s + i * t, mtd->oobsize - i * j);
|
||||
/* the last chunk */
|
||||
memcpy16_fromio(d + i * oob_chunk_size,
|
||||
s + i * sparebuf_size,
|
||||
host->used_oobsize - i * oob_chunk_size);
|
||||
} else {
|
||||
for (i = 0; i < n - 1; i++)
|
||||
memcpy32_toio(&s[i * t], &d[i * j], j);
|
||||
for (i = 0; i < num_chunks - 1; i++)
|
||||
memcpy16_toio(&s[i * sparebuf_size],
|
||||
&d[i * oob_chunk_size],
|
||||
oob_chunk_size);
|
||||
|
||||
/* the last section */
|
||||
memcpy32_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j);
|
||||
/* the last chunk */
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static struct platform_device_id mxcnd_devtype[] = {
|
||||
static const struct platform_device_id mxcnd_devtype[] = {
|
||||
{
|
||||
.name = "imx21-nand",
|
||||
.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)
|
||||
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;
|
||||
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 (is_imx21_nfc(host) || is_imx27_nfc(host))
|
||||
|
@ -1,6 +1,4 @@
|
||||
/*
|
||||
* drivers/mtd/nand.c
|
||||
*
|
||||
* Overview:
|
||||
* This is the generic MTD driver for NAND flash devices. It should be
|
||||
* capable of working with almost all NAND chips currently available.
|
||||
@ -48,6 +46,7 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/of_mtd.h>
|
||||
|
||||
/* Define default oob placement schemes for large and small page devices */
|
||||
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))
|
||||
return -EINVAL;
|
||||
|
||||
/* clear the sub feature parameters */
|
||||
memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
|
||||
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
|
||||
*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))
|
||||
goto ident_done;
|
||||
} else if (*dev_id == type->dev_id) {
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3798,6 +3794,39 @@ ident_done:
|
||||
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
|
||||
* @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;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
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 */
|
||||
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
||||
|
@ -1,6 +1,4 @@
|
||||
/*
|
||||
* drivers/mtd/nand_bbt.c
|
||||
*
|
||||
* Overview:
|
||||
* Bad block table support for the NAND driver
|
||||
*
|
||||
@ -64,7 +62,6 @@
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.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? */
|
||||
if (td->options & NAND_BBT_SAVECONTENT) {
|
||||
/* 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;
|
||||
res = mtd_read(mtd, to, len, &retlen, buf);
|
||||
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 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;
|
||||
int len, res = 0;
|
||||
int len, res;
|
||||
uint8_t *buf;
|
||||
struct nand_bbt_descr *td = this->bbt_td;
|
||||
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 ((res = nand_memory_bbt(mtd, bd))) {
|
||||
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
|
||||
kfree(this->bbt);
|
||||
this->bbt = NULL;
|
||||
goto err;
|
||||
}
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
verify_bbt_descr(mtd, td);
|
||||
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;
|
||||
buf = vmalloc(len);
|
||||
if (!buf) {
|
||||
kfree(this->bbt);
|
||||
this->bbt = NULL;
|
||||
return -ENOMEM;
|
||||
res = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
/* Prevent the bbt regions from erasing / writing */
|
||||
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);
|
||||
|
||||
vfree(buf);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(this->bbt);
|
||||
this->bbt = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
/*
|
||||
* drivers/mtd/nandids.c
|
||||
*
|
||||
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
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].size = part_sz;
|
||||
next_offset += ns->partitions[i].size;
|
||||
@ -756,6 +761,11 @@ static int init_nandsim(struct mtd_info *mtd)
|
||||
goto error;
|
||||
}
|
||||
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].size = remains;
|
||||
ns->nbparts += 1;
|
||||
|
@ -1,6 +1,4 @@
|
||||
/*
|
||||
* drivers/mtd/ndfc.c
|
||||
*
|
||||
* Overview:
|
||||
* Platform independent driver for NDFC (NanD Flash Controller)
|
||||
* integrated into EP440 cores
|
||||
|
@ -24,8 +24,6 @@ struct plat_nand_data {
|
||||
void __iomem *io_base;
|
||||
};
|
||||
|
||||
static const char *part_probe_types[] = { "cmdlinepart", NULL };
|
||||
|
||||
/*
|
||||
* Probe for the NAND device.
|
||||
*/
|
||||
@ -95,7 +93,7 @@ static int plat_nand_probe(struct platform_device *pdev)
|
||||
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;
|
||||
err = mtd_device_parse_register(&data->mtd, part_types, &ppdata,
|
||||
|
@ -22,13 +22,14 @@
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.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
|
||||
#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)
|
||||
{
|
||||
if (info->ecc_bch) {
|
||||
int timeout;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
while (len > 8) {
|
||||
__raw_readsl(info->mmio_base + NDDB, data, 8);
|
||||
readsl(info->mmio_base + NDDB, data, 8);
|
||||
|
||||
for (timeout = 0;
|
||||
!(nand_readl(info, NDSR) & NDSR_RDDREQ);
|
||||
timeout++) {
|
||||
if (timeout >= 5) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"Timeout on RDDREQ while draining the FIFO\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mdelay(1);
|
||||
ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
|
||||
val & NDSR_RDDREQ, 1000, 5000);
|
||||
if (ret) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"Timeout on RDDREQ while draining the FIFO\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
@ -522,14 +520,14 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
|
||||
|
||||
switch (info->state) {
|
||||
case STATE_PIO_WRITING:
|
||||
__raw_writesl(info->mmio_base + NDDB,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(do_bytes, 4));
|
||||
writesl(info->mmio_base + NDDB,
|
||||
info->data_buff + info->data_buff_pos,
|
||||
DIV_ROUND_UP(do_bytes, 4));
|
||||
|
||||
if (info->oob_size > 0)
|
||||
__raw_writesl(info->mmio_base + NDDB,
|
||||
info->oob_buff + info->oob_buff_pos,
|
||||
DIV_ROUND_UP(info->oob_size, 4));
|
||||
writesl(info->mmio_base + NDDB,
|
||||
info->oob_buff + info->oob_buff_pos,
|
||||
DIV_ROUND_UP(info->oob_size, 4));
|
||||
break;
|
||||
case STATE_PIO_READING:
|
||||
drain_fifo(info,
|
||||
@ -1630,8 +1628,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||
info->pdev = pdev;
|
||||
info->variant = pxa3xx_nand_get_variant(pdev);
|
||||
for (cs = 0; cs < pdata->num_cs; cs++) {
|
||||
mtd = (struct mtd_info *)((unsigned int)&info[1] +
|
||||
(sizeof(*mtd) + sizeof(*host)) * cs);
|
||||
mtd = (void *)&info[1] + (sizeof(*mtd) + sizeof(*host)) * cs;
|
||||
chip = (struct nand_chip *)(&mtd[1]);
|
||||
host = (struct pxa3xx_nand_host *)chip;
|
||||
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))
|
||||
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");
|
||||
goto error3;
|
||||
}
|
||||
|
||||
dev->card_registred = 1;
|
||||
return 0;
|
||||
error3:
|
||||
nand_release(dev->mtd);
|
||||
error2:
|
||||
kfree(dev->mtd);
|
||||
error1:
|
||||
|
@ -1105,7 +1105,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
|
||||
|
||||
/* driver device registration */
|
||||
|
||||
static struct platform_device_id s3c24xx_driver_ids[] = {
|
||||
static const struct platform_device_id s3c24xx_driver_ids[] = {
|
||||
{
|
||||
.name = "s3c2410-nand",
|
||||
.driver_data = TYPE_S3C2410,
|
||||
|
@ -160,14 +160,10 @@ static int xway_nand_probe(struct platform_device *pdev)
|
||||
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 = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_delay = 30,
|
||||
.part_probe_types = part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.probe = xway_nand_probe,
|
||||
|
@ -1083,7 +1083,7 @@ static const struct dev_pm_ops s3c_pm_ops = {
|
||||
.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",
|
||||
.driver_data = TYPE_S3C6400,
|
||||
|
@ -662,7 +662,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
|
||||
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,imx6sx-qspi", .data = (void *)&imx6sx_data, },
|
||||
{ /* 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
|
||||
* more nor chips. This current list focusses on newer chips, which
|
||||
* 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[] = {
|
||||
/* 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) },
|
||||
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 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 */
|
||||
{ "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) },
|
||||
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
|
||||
|
||||
/* ISSI */
|
||||
{ "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) },
|
||||
|
||||
/* Macronix */
|
||||
{ "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) },
|
||||
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "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) },
|
||||
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 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) },
|
||||
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 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) },
|
||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, 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 */
|
||||
{ "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);
|
||||
|
||||
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);
|
||||
i_uid_write(inode, je16_to_cpu(latest_node.uid));
|
||||
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",
|
||||
ret, retlen, sizeof(*latest_node));
|
||||
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return ret?ret:-EIO;
|
||||
return ret ? ret : -EIO;
|
||||
}
|
||||
|
||||
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
|
||||
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",
|
||||
f->inocache->ino, ref_offset(rii.latest_ref));
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
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
|
||||
* operation. */
|
||||
uint32_t csize = je32_to_cpu(latest_node->csize);
|
||||
if (csize > JFFS2_MAX_NAME_LEN) {
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
if (csize > JFFS2_MAX_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
f->target = kmalloc(csize + 1, GFP_KERNEL);
|
||||
if (!f->target) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1271,8 +1262,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||
ret = -EIO;
|
||||
kfree(f->target);
|
||||
f->target = NULL;
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1289,15 +1278,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||
if (f->metadata) {
|
||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
|
||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -EIO;
|
||||
}
|
||||
if (!frag_first(&f->fragtree)) {
|
||||
JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
|
||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -EIO;
|
||||
}
|
||||
/* 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",
|
||||
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 */
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -EIO;
|
||||
}
|
||||
/* 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;
|
||||
|
||||
ret = jffs2_do_read_inode_internal(c, f, &n);
|
||||
if (!ret) {
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
}
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
jffs2_xattr_do_crccheck_inode(c, ic);
|
||||
kfree (f);
|
||||
return ret;
|
||||
|
@ -296,183 +296,19 @@ struct cfi_private {
|
||||
struct flchip chips[0]; /* per-chip data structure for each chip */
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the command address according to the given geometry.
|
||||
*/
|
||||
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;
|
||||
uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
|
||||
struct map_info *map, struct cfi_private *cfi);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi);
|
||||
#define CMD(x) cfi_build_cmd((x), map, cfi)
|
||||
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
unsigned long cfi_merge_status(map_word val, struct map_info *map,
|
||||
struct cfi_private *cfi);
|
||||
#define MERGESTATUS(x) cfi_merge_status((x), map, cfi)
|
||||
|
||||
|
||||
/*
|
||||
* 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,
|
||||
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;
|
||||
}
|
||||
int type, map_word *prev_val);
|
||||
|
||||
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)
|
||||
{
|
||||
if (us >= 1000) {
|
||||
msleep((us+999)/1000);
|
||||
} else {
|
||||
udelay(us);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
void cfi_udelay(int us);
|
||||
|
||||
int __xipram cfi_qry_present(struct map_info *map, __u32 base,
|
||||
struct cfi_private *cfi);
|
||||
|
@ -26,6 +26,8 @@
|
||||
|
||||
struct mtd_info;
|
||||
struct nand_flash_dev;
|
||||
struct device_node;
|
||||
|
||||
/* Scan and identify a NAND device */
|
||||
extern int nand_scan(struct mtd_info *mtd, int max_chips);
|
||||
/*
|
||||
@ -542,6 +544,7 @@ struct nand_buffers {
|
||||
* flash device
|
||||
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
|
||||
* flash device.
|
||||
* @dn: [BOARDSPECIFIC] device node describing this instance
|
||||
* @read_byte: [REPLACEABLE] read one byte from the chip
|
||||
* @read_word: [REPLACEABLE] read one word from the chip
|
||||
* @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_W;
|
||||
|
||||
struct device_node *dn;
|
||||
|
||||
uint8_t (*read_byte)(struct mtd_info *mtd);
|
||||
u16 (*read_word)(struct mtd_info *mtd);
|
||||
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_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_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
|
Loading…
Reference in New Issue
Block a user