Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
linux-elphel
Commits
1ac03d4a
Commit
1ac03d4a
authored
Sep 26, 2019
by
Oleg Dzhimiev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
updated nand driver from linux-xlnx 4.14 to 4.19
parent
88aecd2b
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
3502 additions
and
2486 deletions
+3502
-2486
elphel393-zynq-base.dtsi
src/arch/arm/boot/dts/elphel393-zynq-base.dtsi
+13
-13
elphel393_4_mt9p006.dts
src/arch/arm/boot/dts/elphel393_4_mt9p006.dts
+26
-34
pl35x_nand.c
src/drivers/mtd/nand/pl35x_nand.c
+0
-1419
Kconfig
src/drivers/mtd/nand/raw/Kconfig
+123
-127
Makefile
src/drivers/mtd/nand/raw/Makefile
+7
-10
nand.h
src/drivers/mtd/nand/raw/nand.h
+12
-0
nand_base.c
src/drivers/mtd/nand/raw/nand_base.c
+2482
-853
nand_micron.c
src/drivers/mtd/nand/raw/nand_micron.c
+807
-0
nand_micron_mt29f.c
src/drivers/mtd/nand/raw/nand_micron_mt29f.c
+32
-30
No files found.
src/arch/arm/boot/dts/elphel393-zynq-base.dtsi
View file @
1ac03d4a
...
@@ -296,24 +296,24 @@
...
@@ -296,24 +296,24 @@
reg
=
<
0xf8001000
0x1000
>;
reg
=
<
0xf8001000
0x1000
>;
}
;
}
;
ps7_smcc_0
:
ps7
-
smcc
@
e000e000
{
smcc
:
memory
-
controller
@
e000e000
{
#
address
-
cells
=
<
1
>;
#
address
-
cells
=
<
2
>;
#
size
-
cells
=
<
1
>;
#
size
-
cells
=
<
1
>;
clock
-
names
=
"memclk"
,
"aclk"
;
clock
-
names
=
"memclk"
,
"a
pb_p
clk"
;
clocks
=
<&
clkc
11
>,
<&
clkc
44
>;
clocks
=
<&
clkc
11
>,
<&
clkc
44
>;
compatible
=
"arm,pl353-smc-r2p1"
;
compatible
=
"arm,pl353-smc-r2p1"
,
"arm,primecell"
;
interrupt
-
parent
=
<&
ps7_scugic_0
>;
interrupt
-
parent
=
<&
ps7_scugic_0
>;
interrupts
=
<
0
18
4
>;
interrupts
=
<
0
18
4
>;
ranges
;
ranges
=
<
0x0
0x0
0xe1000000
0x1000000
//
Nand
CS
Region
reg
=
<
0xe000e000
0x1000
>;
0x1
0x0
0xe2000000
0x2000000
//
SRAM
/
NOR
CS
Region
arm
,
addr25
=
<
0x0
>;
0x2
0x0
0xe4000000
0x2000000
>;
//
SRAM
/
NOR
CS
Region
arm
,
nor
-
chip
-
sel0
=
<
0x0
>;
arm
,
nor
-
chip
-
sel1
=
<
0x0
>;
reg
=
<
0xe000e000
0x1000
>;
arm
,
sram
-
chip
-
sel0
=
<
0x0
>;
nand0
:
flash
@
e1000000
{
arm
,
sram
-
chip
-
sel1
=
<
0x0
>;
ps7_nand_0
:
ps7
-
nand
@
e1000000
{
compatible
=
"arm,pl353-nand-r2p1"
;
compatible
=
"arm,pl353-nand-r2p1"
;
reg
=
<
0xe1000000
0x1000000
>;
reg
=
<
0
0
0x1000000
>;
nand
-
ecc
-
mode
=
"on-die"
;
nand
-
ecc
-
step
-
size
=
<
2048
>;
/*
arm
,
nand
-
clk
-
freq
-
hz
=
<
0x5f5e100
>;*/
/*
arm
,
nand
-
clk
-
freq
-
hz
=
<
0x5f5e100
>;*/
arm
,
nand
-
width
=
<
0x8
>;
arm
,
nand
-
width
=
<
0x8
>;
arm
,
nand
-
cycle
-
t0
=
<
0x4
>;
arm
,
nand
-
cycle
-
t0
=
<
0x4
>;
...
...
src/arch/arm/boot/dts/elphel393_4_mt9p006.dts
View file @
1ac03d4a
...
@@ -119,40 +119,32 @@
...
@@ -119,40 +119,32 @@
};
};
};
};
ps7_smcc_0: ps7-smcc@e000e000 {
smcc: memory-controller@e000e000 {
ps7_nand_0: ps7-nand@e1000000 {
nand0: flash@e1000000 {
compatible = "arm,pl353-nand-r2p1";
partitions {
reg = < 0xe1000000 0x1000000 >;
compatible = "fixed-partitions";
/*arm,nand-clk-freq-hz = <0x5f5e100>;*/
#address-cells = <1>;
arm,nand-width = <0x8>;
#size-cells = <1>;
arm,nand-cycle-t0 = <0x4>;
partition@0 {
arm,nand-cycle-t1 = <0x4>;
label = "u-boot-spl";
arm,nand-cycle-t2 = <0x1>;
reg = <0x0 0x100000>;/*1MB for backup spl image(s)*/
arm,nand-cycle-t3 = <0x2>;
};
arm,nand-cycle-t4 = <0x2>;
partition@1 {
arm,nand-cycle-t5 = <0x2>;
label = "u-boot";
arm,nand-cycle-t6 = <0x4>;
reg = <0x100000 0x400000>;/*4MB*/
#address-cells = <0x1>;
};
#size-cells = <0x1>;
partition@2 {
partition@0 {
label = "device-tree";
label = "u-boot-spl";
reg = <0x500000 0x100000>;/*1MB*/
reg = <0x0 0x100000>;/*1MB for backup spl image(s)*/
};
};
partition@3 {
partition@1 {
label = "kernel";
label = "u-boot";
reg = <0x600000 0x1000000>;/*16MB*/
reg = <0x100000 0x400000>;/*4MB*/
};
};
partition@4 {
partition@2 {
label = "rootfs";
label = "device-tree";
reg = <0x1600000 0x10000000>;/*256MB*/
reg = <0x500000 0x100000>;/*1MB*/
};
};
partition@3 {
label = "kernel";
reg = <0x600000 0x1000000>;/*16MB*/
};
partition@4 {
label = "rootfs";
reg = <0x1600000 0x10000000>;/*256MB*/
};
};
} ;
} ;
} ;
} ;
...
...
src/drivers/mtd/nand/pl35x_nand.c
deleted
100644 → 0
View file @
88aecd2b
/*
* ARM PL35X NAND Flash Controller Driver
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.
*
* This driver is based on plat_nand.c and mxc_nand.c drivers
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/memory/pl35x-smc.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define PL35X_NAND_DRIVER_NAME "pl35x-nand"
/* NAND flash driver defines */
#define PL35X_NAND_CMD_PHASE 1
/* End command valid in command phase */
#define PL35X_NAND_DATA_PHASE 2
/* End command valid in data phase */
#define PL35X_NAND_ECC_SIZE 512
/* Size of data for ECC operation */
/* Flash memory controller operating parameters */
#define PL35X_NAND_ECC_CONFIG (BIT(4) |
/* ECC read at end of page */
\
(0 << 5))
/* No Jumping */
/* AXI Address definitions */
#define START_CMD_SHIFT 3
#define END_CMD_SHIFT 11
#define END_CMD_VALID_SHIFT 20
#define ADDR_CYCLES_SHIFT 21
#define CLEAR_CS_SHIFT 21
#define ECC_LAST_SHIFT 10
#define COMMAND_PHASE (0 << 19)
#define DATA_PHASE BIT(19)
#define PL35X_NAND_ECC_LAST BIT(ECC_LAST_SHIFT)
/* Set ECC_Last */
#define PL35X_NAND_CLEAR_CS BIT(CLEAR_CS_SHIFT)
/* Clear chip select */
#define ONDIE_ECC_FEATURE_ADDR 0x90
#define PL35X_NAND_ECC_BUSY_TIMEOUT (1 * HZ)
#define PL35X_NAND_DEV_BUSY_TIMEOUT (1 * HZ)
#define PL35X_NAND_LAST_TRANSFER_LENGTH 4
/* Inline function for the NAND controller register write */
static
inline
void
pl35x_nand_write32
(
void
__iomem
*
addr
,
u32
val
)
{
writel_relaxed
((
val
),
(
addr
));
}
/**
* struct pl35x_nand_command_format - Defines NAND flash command format
* @start_cmd: First cycle command (Start command)
* @end_cmd: Second cycle command (Last command)
* @addr_cycles: Number of address cycles required to send the address
* @end_cmd_valid: The second cycle command is valid for cmd or data phase
*/
struct
pl35x_nand_command_format
{
int
start_cmd
;
int
end_cmd
;
u8
addr_cycles
;
u8
end_cmd_valid
;
};
/**
* struct pl35x_nand_info - Defines the NAND flash driver instance
* @chip: NAND chip information structure
* @nand_base: Virtual address of the NAND flash device
* @end_cmd_pending: End command is pending
* @end_cmd: End command
* @row_addr_cycles: Row address cycles
* @col_addr_cycles: Column address cycles
*/
struct
pl35x_nand_info
{
struct
nand_chip
chip
;
void
__iomem
*
nand_base
;
unsigned
long
end_cmd_pending
;
unsigned
long
end_cmd
;
u8
row_addr_cycles
;
u8
col_addr_cycles
;
};
/*
* The NAND flash operations command format
*/
// Elphel: added UNLOCK1, UNLOCK2 and LOCK
static
const
struct
pl35x_nand_command_format
pl35x_nand_commands
[]
=
{
{
NAND_CMD_READ0
,
NAND_CMD_READSTART
,
5
,
PL35X_NAND_CMD_PHASE
},
{
NAND_CMD_RNDOUT
,
NAND_CMD_RNDOUTSTART
,
2
,
PL35X_NAND_CMD_PHASE
},
{
NAND_CMD_READID
,
NAND_CMD_NONE
,
1
,
NAND_CMD_NONE
},
{
NAND_CMD_STATUS
,
NAND_CMD_NONE
,
0
,
NAND_CMD_NONE
},
{
NAND_CMD_SEQIN
,
NAND_CMD_PAGEPROG
,
5
,
PL35X_NAND_DATA_PHASE
},
{
NAND_CMD_RNDIN
,
NAND_CMD_NONE
,
2
,
NAND_CMD_NONE
},
{
NAND_CMD_ERASE1
,
NAND_CMD_ERASE2
,
3
,
PL35X_NAND_CMD_PHASE
},
{
NAND_CMD_RESET
,
NAND_CMD_NONE
,
0
,
NAND_CMD_NONE
},
{
NAND_CMD_PARAM
,
NAND_CMD_NONE
,
1
,
NAND_CMD_NONE
},
{
NAND_CMD_GET_FEATURES
,
NAND_CMD_NONE
,
1
,
NAND_CMD_NONE
},
{
NAND_CMD_SET_FEATURES
,
NAND_CMD_NONE
,
1
,
NAND_CMD_NONE
},
{
NAND_CMD_UNLOCK1
,
NAND_CMD_NONE
,
3
,
NAND_CMD_NONE
},
{
NAND_CMD_UNLOCK2
,
NAND_CMD_NONE
,
3
,
NAND_CMD_NONE
},
{
NAND_CMD_LOCK
,
NAND_CMD_NONE
,
0
,
NAND_CMD_NONE
},
{
NAND_CMD_NONE
,
NAND_CMD_NONE
,
0
,
0
},
/* Add all the flash commands supported by the flash device and Linux */
/*
* The cache program command is not supported by driver because driver
* cant differentiate between page program and cached page program from
* start command, these commands can be differentiated through end
* command, which doesn't fit in to the driver design. The cache program
* command is not supported by NAND subsystem also, look at 1612 line
* number (in nand_write_page function) of nand_base.c file.
* {NAND_CMD_SEQIN, NAND_CMD_CACHEDPROG, 5, PL35X_NAND_YES},
*/
};
static
int
pl35x_ecc_ooblayout16_ecc
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
>=
chip
->
ecc
.
steps
)
return
-
ERANGE
;
oobregion
->
offset
=
(
section
*
16
)
+
0
;
oobregion
->
length
=
chip
->
ecc
.
bytes
;
return
0
;
}
static
int
pl35x_ecc_ooblayout16_free
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
>=
chip
->
ecc
.
steps
)
return
-
ERANGE
;
oobregion
->
offset
=
(
section
*
16
)
+
8
;
oobregion
->
length
=
8
;
return
0
;
}
static
const
struct
mtd_ooblayout_ops
fsmc_ecc_ooblayout16_ops
=
{
.
ecc
=
pl35x_ecc_ooblayout16_ecc
,
.
free
=
pl35x_ecc_ooblayout16_free
,
};
static
int
pl35x_ecc_ooblayout64_ecc
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
>=
chip
->
ecc
.
steps
)
return
-
ERANGE
;
oobregion
->
offset
=
(
section
*
chip
->
ecc
.
bytes
)
+
52
;
oobregion
->
length
=
chip
->
ecc
.
bytes
;
return
0
;
}
static
int
pl35x_ecc_ooblayout64_free
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
)
return
-
ERANGE
;
if
(
section
>=
chip
->
ecc
.
steps
)
return
-
ERANGE
;
oobregion
->
offset
=
(
section
*
chip
->
ecc
.
bytes
)
+
2
;
oobregion
->
length
=
50
;
return
0
;
}
static
const
struct
mtd_ooblayout_ops
fsmc_ecc_ooblayout64_ops
=
{
.
ecc
=
pl35x_ecc_ooblayout64_ecc
,
.
free
=
pl35x_ecc_ooblayout64_free
,
};
static
int
pl35x_ecc_ooblayout_ondie64_ecc
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
>=
chip
->
ecc
.
steps
)
return
-
ERANGE
;
oobregion
->
offset
=
(
section
*
16
)
+
8
;
oobregion
->
length
=
chip
->
ecc
.
bytes
;
return
0
;
}
static
int
pl35x_ecc_ooblayout_ondie64_free
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
>=
chip
->
ecc
.
steps
)
return
-
ERANGE
;
oobregion
->
length
=
4
;
if
(
!
section
)
oobregion
->
offset
=
4
;
else
oobregion
->
offset
=
(
section
*
16
)
+
4
;
return
0
;
}
static
const
struct
mtd_ooblayout_ops
fsmc_ecc_ooblayout_ondie64_ops
=
{
.
ecc
=
pl35x_ecc_ooblayout_ondie64_ecc
,
.
free
=
pl35x_ecc_ooblayout_ondie64_free
,
};
/* Generic flash bbt decriptors */
static
uint8_t
bbt_pattern
[]
=
{
'B'
,
'b'
,
't'
,
'0'
};
static
uint8_t
mirror_pattern
[]
=
{
'1'
,
't'
,
'b'
,
'B'
};
static
struct
nand_bbt_descr
bbt_main_descr
=
{
.
options
=
NAND_BBT_LASTBLOCK
|
NAND_BBT_CREATE
|
NAND_BBT_WRITE
|
NAND_BBT_2BIT
|
NAND_BBT_VERSION
|
NAND_BBT_PERCHIP
,
.
offs
=
4
,
.
len
=
4
,
.
veroffs
=
20
,
.
maxblocks
=
4
,
.
pattern
=
bbt_pattern
};
static
struct
nand_bbt_descr
bbt_mirror_descr
=
{
.
options
=
NAND_BBT_LASTBLOCK
|
NAND_BBT_CREATE
|
NAND_BBT_WRITE
|
NAND_BBT_2BIT
|
NAND_BBT_VERSION
|
NAND_BBT_PERCHIP
,
.
offs
=
4
,
.
len
=
4
,
.
veroffs
=
20
,
.
maxblocks
=
4
,
.
pattern
=
mirror_pattern
};
/**
* pl35x_nand_calculate_hwecc - Calculate Hardware ECC
* @mtd: Pointer to the mtd_info structure
* @data: Pointer to the page data
* @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored
*
* This function retrieves the Hardware ECC data from the controller and returns
* ECC data back to the MTD subsystem.
*
* Return: 0 on success or error value on failure
*/
static
int
pl35x_nand_calculate_hwecc
(
struct
mtd_info
*
mtd
,
const
u8
*
data
,
u8
*
ecc_code
)
{
u32
ecc_value
,
ecc_status
;
u8
ecc_reg
,
ecc_byte
;
unsigned
long
timeout
=
jiffies
+
PL35X_NAND_ECC_BUSY_TIMEOUT
;
/* Wait till the ECC operation is complete or timeout */
do
{
if
(
pl35x_smc_ecc_is_busy
())
cpu_relax
();
else
break
;
}
while
(
!
time_after_eq
(
jiffies
,
timeout
));
if
(
time_after_eq
(
jiffies
,
timeout
))
{
pr_err
(
"%s timed out
\n
"
,
__func__
);
return
-
ETIMEDOUT
;
}
for
(
ecc_reg
=
0
;
ecc_reg
<
4
;
ecc_reg
++
)
{
/* Read ECC value for each block */
ecc_value
=
pl35x_smc_get_ecc_val
(
ecc_reg
);
ecc_status
=
(
ecc_value
>>
24
)
&
0xFF
;
/* ECC value valid */
if
(
ecc_status
&
0x40
)
{
for
(
ecc_byte
=
0
;
ecc_byte
<
3
;
ecc_byte
++
)
{
/* Copy ECC bytes to MTD buffer */
*
ecc_code
=
~
ecc_value
&
0xFF
;
ecc_value
=
ecc_value
>>
8
;
ecc_code
++
;
}
}
else
{
pr_warn
(
"%s status failed
\n
"
,
__func__
);
return
-
1
;
}
}
return
0
;
}
/**
* onehot - onehot function
* @value: Value to check for onehot
*
* This function checks whether a value is onehot or not.
* onehot is if and only if onebit is set.
*
* Return: 1 if it is onehot else 0
*/
static
int
onehot
(
unsigned
short
value
)
{
return
(
value
&
(
value
-
1
))
==
0
;
}
/**
* pl35x_nand_correct_data - ECC correction function
* @mtd: Pointer to the mtd_info structure
* @buf: Pointer to the page data
* @read_ecc: Pointer to the ECC value read from spare data area
* @calc_ecc: Pointer to the calculated ECC value
*
* This function corrects the ECC single bit errors & detects 2-bit errors.
*
* Return: 0 if no ECC errors found
* 1 if single bit error found and corrected.
* -1 if multiple ECC errors found.
*/
static
int
pl35x_nand_correct_data
(
struct
mtd_info
*
mtd
,
unsigned
char
*
buf
,
unsigned
char
*
read_ecc
,
unsigned
char
*
calc_ecc
)
{
unsigned
char
bit_addr
;
unsigned
int
byte_addr
;
unsigned
short
ecc_odd
,
ecc_even
,
read_ecc_lower
,
read_ecc_upper
;
unsigned
short
calc_ecc_lower
,
calc_ecc_upper
;
read_ecc_lower
=
(
read_ecc
[
0
]
|
(
read_ecc
[
1
]
<<
8
))
&
0xfff
;
read_ecc_upper
=
((
read_ecc
[
1
]
>>
4
)
|
(
read_ecc
[
2
]
<<
4
))
&
0xfff
;
calc_ecc_lower
=
(
calc_ecc
[
0
]
|
(
calc_ecc
[
1
]
<<
8
))
&
0xfff
;
calc_ecc_upper
=
((
calc_ecc
[
1
]
>>
4
)
|
(
calc_ecc
[
2
]
<<
4
))
&
0xfff
;
ecc_odd
=
read_ecc_lower
^
calc_ecc_lower
;
ecc_even
=
read_ecc_upper
^
calc_ecc_upper
;
if
((
ecc_odd
==
0
)
&&
(
ecc_even
==
0
))
return
0
;
/* no error */
if
(
ecc_odd
==
(
~
ecc_even
&
0xfff
))
{
/* bits [11:3] of error code is byte offset */
byte_addr
=
(
ecc_odd
>>
3
)
&
0x1ff
;
/* bits [2:0] of error code is bit offset */
bit_addr
=
ecc_odd
&
0x7
;
/* Toggling error bit */
buf
[
byte_addr
]
^=
(
1
<<
bit_addr
);
return
1
;
}
if
(
onehot
(
ecc_odd
|
ecc_even
)
==
1
)
return
1
;
/* one error in parity */
return
-
1
;
/* Uncorrectable error */
}
/**
* pl35x_nand_read_oob - [REPLACABLE] the most common OOB data read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to read
*
* Return: Always return zero
*/
static
int
pl35x_nand_read_oob
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
page
)
{
unsigned
long
data_phase_addr
;
uint8_t
*
p
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
unsigned
long
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READOOB
,
0
,
page
);
p
=
chip
->
oob_poi
;
chip
->
read_buf
(
mtd
,
p
,
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
p
+=
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_R
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
read_buf
(
mtd
,
p
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
return
0
;
}
/**
* pl35x_nand_write_oob - [REPLACABLE] the most common OOB data write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to write
*
* Return: Zero on success and EIO on failure
*/
static
int
pl35x_nand_write_oob
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
page
)
{
int
status
=
0
;
const
uint8_t
*
buf
=
chip
->
oob_poi
;
unsigned
long
data_phase_addr
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
unsigned
long
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
chip
->
cmdfunc
(
mtd
,
NAND_CMD_SEQIN
,
mtd
->
writesize
,
page
);
chip
->
write_buf
(
mtd
,
buf
,
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
buf
+=
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_W
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
|=
(
1
<<
END_CMD_VALID_SHIFT
);
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_W
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
write_buf
(
mtd
,
buf
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
/* Send command to program the OOB data */
chip
->
cmdfunc
(
mtd
,
NAND_CMD_PAGEPROG
,
-
1
,
-
1
);
status
=
chip
->
waitfunc
(
mtd
,
chip
);
return
(
status
&
NAND_STATUS_FAIL
)
?
-
EIO
:
0
;
}
/**
* pl35x_nand_read_page_ondie - [Intern] read page data with ondie ecc
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static
int
pl35x_nand_read_page_ondie
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
u8
*
buf
,
int
oob_required
,
int
page
)
{
unsigned
long
data_phase_addr
;
u8
*
p
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
unsigned
long
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
int
status
;
chip
->
read_buf
(
mtd
,
buf
,
mtd
->
writesize
);
p
=
chip
->
oob_poi
;
chip
->
read_buf
(
mtd
,
p
,
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
p
+=
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_R
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
read_buf
(
mtd
,
p
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
/* Check ECC info */
chip
->
cmdfunc
(
mtd
,
NAND_CMD_STATUS
,
-
1
,
-
1
);
status
=
(
int
)
chip
->
read_byte
(
mtd
);
if
(
!
(
status
&
NAND_STATUS_READY
))
{
pr_err
(
"%s: page %d status=%#x - transfer not finished!
\n
"
,
__func__
,
page
,
status
);
ndelay
(
100
);
if
(
!
(
status
&
NAND_STATUS_READY
))
return
-
EIO
;
}
/* UnCorrectable Error bitflips. 5+ on our chip */
#define NAND_UCE_BITS 5
if
(
status
&
NAND_STATUS_FAIL
)
{
pr_warn
(
"%s: page %d status=%#x - ECC Uncorrectable Error
\n
"
,
__func__
,
page
,
status
);
/*
* Note: don't return -EIO which means no data and is fatal
* during a filesystem mount.
* Instead return NAND_UCE_BITS bitflips and increment
* the uncorrectable error count. This allows the layer above
* (such as UBI) to know data is present but corrupt and to
* be safely manage if its only a EC or VI page.
*/
mtd
->
ecc_stats
.
failed
++
;
return
NAND_UCE_BITS
;
}
#define NAND_STATUS_ECC_COR BIT(3)
/* Correctable error */
if
(
status
&
NAND_STATUS_ECC_COR
)
{
pr_info
(
"%s: page %d status=%#x - ECC Correctable error
\n
"
,
__func__
,
page
,
status
);
mtd
->
ecc_stats
.
corrected
++
;
return
1
;
}
return
0
;
}
/**
* pl35x_nand_read_page_raw - [Intern] read raw page data without ecc
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static
int
pl35x_nand_read_page_raw
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
unsigned
long
data_phase_addr
;
uint8_t
*
p
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
unsigned
long
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
chip
->
read_buf
(
mtd
,
buf
,
mtd
->
writesize
);
p
=
chip
->
oob_poi
;
chip
->
read_buf
(
mtd
,
p
,
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
p
+=
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_R
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
read_buf
(
mtd
,
p
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
return
0
;
}
/**
* pl35x_nand_write_page_raw - [Intern] raw page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to write
*
* Return: Always return zero
*/
static
int
pl35x_nand_write_page_raw
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
unsigned
long
data_phase_addr
;
uint8_t
*
p
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
unsigned
long
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
chip
->
write_buf
(
mtd
,
buf
,
mtd
->
writesize
);
p
=
chip
->
oob_poi
;
chip
->
write_buf
(
mtd
,
p
,
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
p
+=
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_W
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
|=
(
1
<<
END_CMD_VALID_SHIFT
);
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_W
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
write_buf
(
mtd
,
p
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
return
0
;
}
/**
* nand_write_page_hwecc - Hardware ECC based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to write
*
* This functions writes data and hardware generated ECC values in to the page.
*
* Return: Always return zero
*/
static
int
pl35x_nand_write_page_hwecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
int
eccsize
=
chip
->
ecc
.
size
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
const
uint8_t
*
p
=
buf
;
unsigned
long
data_phase_addr
;
uint8_t
*
oob_ptr
;
u32
ret
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
unsigned
long
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
for
(
;
(
eccsteps
-
1
);
eccsteps
--
)
{
chip
->
write_buf
(
mtd
,
p
,
eccsize
);
p
+=
eccsize
;
}
chip
->
write_buf
(
mtd
,
p
,
(
eccsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
p
+=
(
eccsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
/* Set ECC Last bit to 1 */
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_W
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_ECC_LAST
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_W
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
write_buf
(
mtd
,
p
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
p
=
buf
;
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
0
]);
/* Wait for ECC to be calculated and read the error values */
ret
=
mtd_ooblayout_set_eccbytes
(
mtd
,
ecc_calc
,
chip
->
oob_poi
,
0
,
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
/* Clear ECC last bit */
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_W
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
&=
~
PL35X_NAND_ECC_LAST
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_W
=
(
void
__iomem
*
__force
)
data_phase_addr
;
/* Write the spare area with ECC bytes */
oob_ptr
=
chip
->
oob_poi
;
chip
->
write_buf
(
mtd
,
oob_ptr
,
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_W
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
|=
(
1
<<
END_CMD_VALID_SHIFT
);
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_W
=
(
void
__iomem
*
__force
)
data_phase_addr
;
oob_ptr
+=
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
chip
->
write_buf
(
mtd
,
oob_ptr
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
return
0
;
}
/**
* pl35x_nand_write_page_swecc - [REPLACABLE] software ecc based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to write
*
* Return: Always return zero
*/
static
int
pl35x_nand_write_page_swecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
const
uint8_t
*
p
=
buf
;
u32
ret
;
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
0
]);
ret
=
mtd_ooblayout_set_eccbytes
(
mtd
,
ecc_calc
,
chip
->
oob_poi
,
0
,
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
chip
->
ecc
.
write_page_raw
(
mtd
,
chip
,
buf
,
1
,
page
);
return
0
;
}
/**
* pl35x_nand_read_page_hwecc - Hardware ECC based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* This functions reads data and checks the data integrity by comparing hardware
* generated ECC values and read ECC values from spare area.
*
* Return: 0 always and updates ECC operation status in to MTD structure
*/
static
int
pl35x_nand_read_page_hwecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
int
i
,
stat
,
eccsize
=
chip
->
ecc
.
size
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
p
=
buf
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
unsigned
long
data_phase_addr
;
uint8_t
*
oob_ptr
;
u32
ret
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
unsigned
long
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
for
(
;
(
eccsteps
-
1
);
eccsteps
--
)
{
chip
->
read_buf
(
mtd
,
p
,
eccsize
);
p
+=
eccsize
;
}
chip
->
read_buf
(
mtd
,
p
,
(
eccsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
p
+=
(
eccsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
/* Set ECC Last bit to 1 */
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_R
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_ECC_LAST
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
read_buf
(
mtd
,
p
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
/* Read the calculated ECC value */
p
=
buf
;
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
0
]);
/* Clear ECC last bit */
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_R
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
&=
~
PL35X_NAND_ECC_LAST
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
/* Read the stored ECC value */
oob_ptr
=
chip
->
oob_poi
;
chip
->
read_buf
(
mtd
,
oob_ptr
,
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
));
/* de-assert chip select */
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_R
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
oob_ptr
+=
(
mtd
->
oobsize
-
PL35X_NAND_LAST_TRANSFER_LENGTH
);
chip
->
read_buf
(
mtd
,
oob_ptr
,
PL35X_NAND_LAST_TRANSFER_LENGTH
);
ret
=
mtd_ooblayout_get_eccbytes
(
mtd
,
ecc_code
,
chip
->
oob_poi
,
0
,
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
eccsteps
=
chip
->
ecc
.
steps
;
p
=
buf
;
/* Check ECC error for all blocks and correct if it is correctable */
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
&
ecc_code
[
i
],
&
ecc_calc
[
i
]);
if
(
stat
<
0
)
mtd
->
ecc_stats
.
failed
++
;
else
mtd
->
ecc_stats
.
corrected
+=
stat
;
}
return
0
;
}
/**
* pl35x_nand_read_page_swecc - [REPLACABLE] software ecc based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static
int
pl35x_nand_read_page_swecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
p
=
buf
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
u32
ret
;
chip
->
ecc
.
read_page_raw
(
mtd
,
chip
,
buf
,
page
,
1
);
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
ret
=
mtd_ooblayout_get_eccbytes
(
mtd
,
ecc_calc
,
chip
->
oob_poi
,
0
,
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
eccsteps
=
chip
->
ecc
.
steps
;
p
=
buf
;
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
int
stat
;
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
&
ecc_code
[
i
],
&
ecc_calc
[
i
]);
if
(
stat
<
0
)
mtd
->
ecc_stats
.
failed
++
;
else
mtd
->
ecc_stats
.
corrected
+=
stat
;
}
return
0
;
}
/**
* pl35x_nand_select_chip - Select the flash device
* @mtd: Pointer to the mtd info structure
* @chipnr: chipnumber to select, -1 for deselect
*
* This function is empty as the NAND controller handles chip select line
* internally based on the chip address passed in command and data phase.
*/
static
void
pl35x_nand_select_chip
(
struct
mtd_info
*
mtd
,
int
chipnr
)
{
unsigned
long
data_phase_addr
;
struct
pl35x_nand_info
*
xnand
;
unsigned
long
nand_offset
;
struct
nand_chip
*
chip
;
/*
* Chip selection will happen in command/data operation
*/
if
(
chipnr
>=
0
)
return
;
/* de-assert chip select */
chip
=
mtd_to_nand
(
mtd
);
data_phase_addr
=
(
unsigned
long
__force
)
chip
->
IO_ADDR_R
;
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
nand_offset
=
(
unsigned
long
__force
)
xnand
->
nand_base
;
data_phase_addr
-=
nand_offset
;
data_phase_addr
|=
PL35X_NAND_CLEAR_CS
;
data_phase_addr
+=
nand_offset
;
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
read_byte
(
mtd
);
}
/**
* pl35x_nand_cmd_function - Send command to NAND device
* @mtd: Pointer to the mtd_info structure
* @command: The command to be sent to the flash device
* @column: The column address for this command, -1 if none
* @page_addr: The page address for this command, -1 if none
*/
static
void
pl35x_nand_cmd_function
(
struct
mtd_info
*
mtd
,
unsigned
int
command
,
int
column
,
int
page_addr
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
const
struct
pl35x_nand_command_format
*
curr_cmd
=
NULL
;
struct
pl35x_nand_info
*
xnand
=
container_of
(
chip
,
struct
pl35x_nand_info
,
chip
);
void
__iomem
*
cmd_addr
;
unsigned
long
cmd_data
=
0
,
end_cmd_valid
=
0
;
unsigned
long
cmd_phase_addr
,
data_phase_addr
,
end_cmd
,
i
;
unsigned
long
timeout
=
jiffies
+
PL35X_NAND_DEV_BUSY_TIMEOUT
;
u32
addrcycles
;
if
(
xnand
->
end_cmd_pending
)
{
/*
* Check for end command if this command request is same as the
* pending command then return
*/
if
(
xnand
->
end_cmd
==
command
)
{
xnand
->
end_cmd
=
0
;
xnand
->
end_cmd_pending
=
0
;
return
;
}
}
/* Emulate NAND_CMD_READOOB for large page device */
if
((
mtd
->
writesize
>
PL35X_NAND_ECC_SIZE
)
&&
(
command
==
NAND_CMD_READOOB
))
{
column
+=
mtd
->
writesize
;
command
=
NAND_CMD_READ0
;
}
/* Get the command format */
for
(
i
=
0
;
(
pl35x_nand_commands
[
i
].
start_cmd
!=
NAND_CMD_NONE
||
pl35x_nand_commands
[
i
].
end_cmd
!=
NAND_CMD_NONE
);
i
++
)
if
(
command
==
pl35x_nand_commands
[
i
].
start_cmd
)
curr_cmd
=
&
pl35x_nand_commands
[
i
];
if
(
curr_cmd
==
NULL
)
return
;
/* Clear interrupt */
pl35x_smc_clr_nand_int
();
/* Get the command phase address */
if
(
curr_cmd
->
end_cmd_valid
==
PL35X_NAND_CMD_PHASE
)
end_cmd_valid
=
1
;
if
(
curr_cmd
->
end_cmd
==
NAND_CMD_NONE
)
end_cmd
=
0x0
;
else
end_cmd
=
curr_cmd
->
end_cmd
;
if
(
command
==
NAND_CMD_READ0
||
command
==
NAND_CMD_SEQIN
)
addrcycles
=
xnand
->
row_addr_cycles
+
xnand
->
col_addr_cycles
;
else
if
(
command
==
NAND_CMD_ERASE1
)
addrcycles
=
xnand
->
row_addr_cycles
;
else
addrcycles
=
curr_cmd
->
addr_cycles
;
cmd_phase_addr
=
(
unsigned
long
__force
)
xnand
->
nand_base
+
(
(
addrcycles
<<
ADDR_CYCLES_SHIFT
)
|
(
end_cmd_valid
<<
END_CMD_VALID_SHIFT
)
|
(
COMMAND_PHASE
)
|
(
end_cmd
<<
END_CMD_SHIFT
)
|
(
curr_cmd
->
start_cmd
<<
START_CMD_SHIFT
));
cmd_addr
=
(
void
__iomem
*
__force
)
cmd_phase_addr
;
/* Get the data phase address */
end_cmd_valid
=
0
;
data_phase_addr
=
(
unsigned
long
__force
)
xnand
->
nand_base
+
(
(
0x0
<<
CLEAR_CS_SHIFT
)
|
(
end_cmd_valid
<<
END_CMD_VALID_SHIFT
)
|
(
DATA_PHASE
)
|
(
end_cmd
<<
END_CMD_SHIFT
)
|
(
0x0
<<
ECC_LAST_SHIFT
));
chip
->
IO_ADDR_R
=
(
void
__iomem
*
__force
)
data_phase_addr
;
chip
->
IO_ADDR_W
=
chip
->
IO_ADDR_R
;
/* Command phase AXI write */
/* Read & Write */
if
(
column
!=
-
1
&&
page_addr
!=
-
1
)
{
/* Adjust columns for 16 bit bus width */
if
(
chip
->
options
&
NAND_BUSWIDTH_16
)
column
>>=
1
;
cmd_data
=
column
;
if
(
mtd
->
writesize
>
PL35X_NAND_ECC_SIZE
)
{
cmd_data
|=
page_addr
<<
16
;
/* Another address cycle for devices > 128MiB */
if
(
chip
->
chipsize
>
(
128
<<
20
))
{
pl35x_nand_write32
(
cmd_addr
,
cmd_data
);
cmd_data
=
(
page_addr
>>
16
);
}
}
else
{
cmd_data
|=
page_addr
<<
8
;
}
}
else
if
(
page_addr
!=
-
1
)
{
/* Erase */
cmd_data
=
page_addr
;
}
else
if
(
column
!=
-
1
)
{
/*
* Change read/write column, read id etc
* Adjust columns for 16 bit bus width
*/
if
((
chip
->
options
&
NAND_BUSWIDTH_16
)
&&
((
command
==
NAND_CMD_READ0
)
||
(
command
==
NAND_CMD_SEQIN
)
||
(
command
==
NAND_CMD_RNDOUT
)
||
(
command
==
NAND_CMD_RNDIN
)))
column
>>=
1
;
cmd_data
=
column
;
}
pl35x_nand_write32
(
cmd_addr
,
cmd_data
);
if
(
curr_cmd
->
end_cmd_valid
)
{
xnand
->
end_cmd
=
curr_cmd
->
end_cmd
;
xnand
->
end_cmd_pending
=
1
;
}
ndelay
(
100
);
if
((
command
==
NAND_CMD_READ0
)
||
(
command
==
NAND_CMD_RESET
)
||
(
command
==
NAND_CMD_PARAM
)
||
(
command
==
NAND_CMD_GET_FEATURES
))
{
/* Wait till the device is ready or timeout */
do
{
if
(
chip
->
dev_ready
(
mtd
))
break
;
else
cpu_relax
();
}
while
(
!
time_after_eq
(
jiffies
,
timeout
));
if
(
time_after_eq
(
jiffies
,
timeout
))
pr_err
(
"%s timed out
\n
"
,
__func__
);
return
;
}
}
/**
* pl35x_nand_read_buf - read chip data into buffer
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to read
*/
static
void
pl35x_nand_read_buf
(
struct
mtd_info
*
mtd
,
uint8_t
*
buf
,
int
len
)
{
int
i
;
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
unsigned
long
*
ptr
=
(
unsigned
long
*
)
buf
;
len
>>=
2
;
for
(
i
=
0
;
i
<
len
;
i
++
)
ptr
[
i
]
=
readl
(
chip
->
IO_ADDR_R
);
}
/**
* pl35x_nand_write_buf - write buffer to chip
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to write
*/
static
void
pl35x_nand_write_buf
(
struct
mtd_info
*
mtd
,
const
uint8_t
*
buf
,
int
len
)
{
int
i
;
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
unsigned
long
*
ptr
=
(
unsigned
long
*
)
buf
;
len
>>=
2
;
for
(
i
=
0
;
i
<
len
;
i
++
)
writel
(
ptr
[
i
],
chip
->
IO_ADDR_W
);
}
/**
* pl35x_nand_device_ready - Check device ready/busy line
* @mtd: Pointer to the mtd_info structure
*
* Return: 0 on busy or 1 on ready state
*/
static
int
pl35x_nand_device_ready
(
struct
mtd_info
*
mtd
)
{
if
(
pl35x_smc_get_nand_int_status_raw
())
{
pl35x_smc_clr_nand_int
();
return
1
;
}
return
0
;
}
// Elphel: whole function
/**
* pl35x_nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
* @mtd: MTD device structure
* @chip: nand chip info structure
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
static
int
pl35x_nand_onfi_set_features
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
addr
,
uint8_t
*
subfeature_param
)
{
int
status
;
int
i
;
uint8_t
ondie_ecc_feature
;
if
(
!
chip
->
onfi_version
||
!
(
le16_to_cpu
(
chip
->
onfi_params
.
opt_cmd
)
&
ONFI_OPT_CMD_SET_GET_FEATURES
))
return
-
EINVAL
;
if
(
addr
==
ONDIE_ECC_FEATURE_ADDR
){
//keep ondie ecc on;
chip
->
cmdfunc
(
mtd
,
NAND_CMD_GET_FEATURES
,
addr
,
-
1
);
ondie_ecc_feature
=
readb
(
chip
->
IO_ADDR_R
);
subfeature_param
[
0
]
|=
(
ondie_ecc_feature
&
0x08
);
}
chip
->
cmdfunc
(
mtd
,
NAND_CMD_SET_FEATURES
,
addr
,
-
1
);
for
(
i
=
0
;
i
<
ONFI_SUBFEATURE_PARAM_LEN
;
++
i
)
writeb
(
subfeature_param
[
i
],
chip
->
IO_ADDR_W
);
//chip->write_byte(mtd, subfeature_param[i]);
status
=
chip
->
waitfunc
(
mtd
,
chip
);
if
(
status
&
NAND_STATUS_FAIL
)
return
-
EIO
;
return
0
;
}
// Elphel: whole function
/**
* nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
* @mtd: MTD device structure
* @chip: nand chip info structure
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
static
int
pl35x_nand_onfi_get_features
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
addr
,
uint8_t
*
subfeature_param
)
{
int
i
;
if
(
!
chip
->
onfi_version
||
!
(
le16_to_cpu
(
chip
->
onfi_params
.
opt_cmd
)
&
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
);
return
0
;
}
/**
* pl35x_nand_detect_ondie_ecc - Get the flash ondie ecc state
* @mtd: Pointer to the mtd_info structure
*
* This function enables the ondie ecc for the Micron ondie ecc capable devices
*
* Return: 1 on detect, 0 if fail to detect
*/
static
int
pl35x_nand_detect_ondie_ecc
(
struct
mtd_info
*
mtd
)
{
struct
nand_chip
*
nand_chip
=
mtd_to_nand
(
mtd
);
u8
maf_id
,
dev_id
,
i
,
get_feature
;
u8
set_feature
[
4
]
=
{
0x08
,
0x00
,
0x00
,
0x00
};
/* Check if On-Die ECC flash */
nand_chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
nand_chip
->
cmdfunc
(
mtd
,
NAND_CMD_READID
,
0x00
,
-
1
);
/* Read manufacturer and device IDs */
maf_id
=
readb
(
nand_chip
->
IO_ADDR_R
);
dev_id
=
readb
(
nand_chip
->
IO_ADDR_R
);
if
((
maf_id
==
NAND_MFR_MICRON
)
&&
((
dev_id
==
0xf1
)
||
(
dev_id
==
0xa1
)
||
(
dev_id
==
0xb1
)
||
(
dev_id
==
0xaa
)
||
(
dev_id
==
0xba
)
||
(
dev_id
==
0xda
)
||
(
dev_id
==
0xca
)
||
(
dev_id
==
0xac
)
||
(
dev_id
==
0xbc
)
||
(
dev_id
==
0xdc
)
||
(
dev_id
==
0xcc
)
||
(
dev_id
==
0xa3
)
||
(
dev_id
==
0xb3
)
||
(
dev_id
==
0xd3
)
||
(
dev_id
==
0xc3
)))
{
nand_chip
->
cmdfunc
(
mtd
,
NAND_CMD_GET_FEATURES
,
ONDIE_ECC_FEATURE_ADDR
,
-
1
);
get_feature
=
readb
(
nand_chip
->
IO_ADDR_R
);
if
(
get_feature
&
0x08
)
{
return
1
;
}
else
{
nand_chip
->
cmdfunc
(
mtd
,
NAND_CMD_SET_FEATURES
,
ONDIE_ECC_FEATURE_ADDR
,
-
1
);
for
(
i
=
0
;
i
<
4
;
i
++
)
writeb
(
set_feature
[
i
],
nand_chip
->
IO_ADDR_W
);
ndelay
(
1000
);
nand_chip
->
cmdfunc
(
mtd
,
NAND_CMD_GET_FEATURES
,
ONDIE_ECC_FEATURE_ADDR
,
-
1
);
get_feature
=
readb
(
nand_chip
->
IO_ADDR_R
);
if
(
get_feature
&
0x08
)
return
1
;
}
}
return
0
;
}
/**
* pl35x_nand_ecc_init - Initialize the ecc information as per the ecc mode
* @mtd: Pointer to the mtd_info structure
* @ecc: Pointer to ECC control structure
* @ondie_ecc_state: ondie ecc status
*
* This function initializes the ecc block and functional pointers as per the
* ecc mode
*/
static
void
pl35x_nand_ecc_init
(
struct
mtd_info
*
mtd
,
struct
nand_ecc_ctrl
*
ecc
,
int
ondie_ecc_state
)
{
struct
nand_chip
*
nand_chip
=
mtd_to_nand
(
mtd
);
ecc
->
mode
=
NAND_ECC_HW
;
ecc
->
read_oob
=
pl35x_nand_read_oob
;
ecc
->
read_page_raw
=
pl35x_nand_read_page_raw
;
ecc
->
strength
=
1
;
ecc
->
write_oob
=
pl35x_nand_write_oob
;
ecc
->
write_page_raw
=
pl35x_nand_write_page_raw
;
if
(
ondie_ecc_state
)
{
/* bypass the controller ECC block */
pl35x_smc_set_ecc_mode
(
PL35X_SMC_ECCMODE_BYPASS
);
/*
* The software ECC routines won't work with the
* SMC controller
*/
ecc
->
bytes
=
0
;
mtd_set_ooblayout
(
mtd
,
&
fsmc_ecc_ooblayout_ondie64_ops
);
ecc
->
read_page
=
pl35x_nand_read_page_ondie
;
ecc
->
write_page
=
pl35x_nand_write_page_raw
;
ecc
->
size
=
mtd
->
writesize
;
/*
* On-Die ECC spare bytes offset 8 is used for ECC codes
* Use the BBT pattern descriptors
*/
nand_chip
->
bbt_td
=
&
bbt_main_descr
;
nand_chip
->
bbt_md
=
&
bbt_mirror_descr
;
}
else
{
/* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
ecc
->
bytes
=
3
;
ecc
->
calculate
=
pl35x_nand_calculate_hwecc
;
ecc
->
correct
=
pl35x_nand_correct_data
;
ecc
->
hwctl
=
NULL
;
ecc
->
read_page
=
pl35x_nand_read_page_hwecc
;
ecc
->
size
=
PL35X_NAND_ECC_SIZE
;
ecc
->
write_page
=
pl35x_nand_write_page_hwecc
;
pl35x_smc_set_ecc_pg_size
(
mtd
->
writesize
);
switch
(
mtd
->
writesize
)
{
case
512
:
case
1024
:
case
2048
:
pl35x_smc_set_ecc_mode
(
PL35X_SMC_ECCMODE_APB
);
break
;
default:
/*
* The software ECC routines won't work with the
* SMC controller
*/
ecc
->
calculate
=
nand_calculate_ecc
;
ecc
->
correct
=
nand_correct_data
;
ecc
->
read_page
=
pl35x_nand_read_page_swecc
;
ecc
->
write_page
=
pl35x_nand_write_page_swecc
;
ecc
->
size
=
256
;
break
;
}
if
(
mtd
->
oobsize
==
16
)
mtd_set_ooblayout
(
mtd
,
&
fsmc_ecc_ooblayout16_ops
);
else
if
(
mtd
->
oobsize
==
64
)
mtd_set_ooblayout
(
mtd
,
&
fsmc_ecc_ooblayout64_ops
);
}
}
/**
* pl35x_nand_probe - Probe method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function initializes the driver data structures and the hardware.
*
* Return: 0 on success or error value on failure
*/
static
int
pl35x_nand_probe
(
struct
platform_device
*
pdev
)
{
struct
pl35x_nand_info
*
xnand
;
struct
mtd_info
*
mtd
;
struct
nand_chip
*
nand_chip
;
struct
resource
*
res
;
int
ondie_ecc_state
;
xnand
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
xnand
),
GFP_KERNEL
);
if
(
!
xnand
)
return
-
ENOMEM
;
/* Map physical address of NAND flash */
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
xnand
->
nand_base
=
devm_ioremap_resource
(
&
pdev
->
dev
,
res
);
if
(
IS_ERR
(
xnand
->
nand_base
))
return
PTR_ERR
(
xnand
->
nand_base
);
nand_chip
=
&
xnand
->
chip
;
mtd
=
nand_to_mtd
(
nand_chip
);
nand_set_controller_data
(
nand_chip
,
xnand
);
mtd
->
priv
=
nand_chip
;
mtd
->
owner
=
THIS_MODULE
;
mtd
->
name
=
PL35X_NAND_DRIVER_NAME
;
nand_set_flash_node
(
nand_chip
,
pdev
->
dev
.
of_node
);
/* Set address of NAND IO lines */
nand_chip
->
IO_ADDR_R
=
xnand
->
nand_base
;
nand_chip
->
IO_ADDR_W
=
xnand
->
nand_base
;
/* Set the driver entry points for MTD */
nand_chip
->
cmdfunc
=
pl35x_nand_cmd_function
;
nand_chip
->
dev_ready
=
pl35x_nand_device_ready
;
nand_chip
->
select_chip
=
pl35x_nand_select_chip
;
nand_chip
->
onfi_set_features
=
pl35x_nand_onfi_set_features
;
nand_chip
->
onfi_get_features
=
pl35x_nand_onfi_get_features
;
//nand_chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
//nand_chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
/* If we don't set this delay driver sets 20us by default */
nand_chip
->
chip_delay
=
30
;
/* Buffer read/write routines */
nand_chip
->
read_buf
=
pl35x_nand_read_buf
;
nand_chip
->
write_buf
=
pl35x_nand_write_buf
;
/* Set the device option and flash width */
nand_chip
->
options
=
NAND_BUSWIDTH_AUTO
;
nand_chip
->
bbt_options
=
NAND_BBT_USE_FLASH
;
platform_set_drvdata
(
pdev
,
xnand
);
ondie_ecc_state
=
pl35x_nand_detect_ondie_ecc
(
mtd
);
/* first scan to find the device and get the page size */
if
(
nand_scan_ident
(
mtd
,
1
,
NULL
))
{
dev_err
(
&
pdev
->
dev
,
"nand_scan_ident for NAND failed
\n
"
);
return
-
ENXIO
;
}
xnand
->
row_addr_cycles
=
nand_chip
->
onfi_params
.
addr_cycles
&
0xF
;
xnand
->
col_addr_cycles
=
(
nand_chip
->
onfi_params
.
addr_cycles
>>
4
)
&
0xF
;
pl35x_nand_ecc_init
(
mtd
,
&
nand_chip
->
ecc
,
ondie_ecc_state
);
if
(
nand_chip
->
options
&
NAND_BUSWIDTH_16
)
pl35x_smc_set_buswidth
(
PL35X_SMC_MEM_WIDTH_16
);
/* second phase scan */
if
(
nand_scan_tail
(
mtd
))
{
dev_err
(
&
pdev
->
dev
,
"nand_scan_tail for NAND failed
\n
"
);
// elphel393: don't want to fail exit on the very first boot when flash is locked and the bad block table is not written yet
//return -ENXIO;
}
//elphel393 modification for Micron NAND chips
//TODO: add Micron chip ID checking
mtd
->
_unlock
=
nand_unlock
;
mtd
->
_lock
=
nand_lock
;
mtd_device_register
(
mtd
,
NULL
,
0
);
return
0
;
}
/**
* pl35x_nand_remove - Remove method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function is called if the driver module is being unloaded. It frees all
* resources allocated to the device.
*
* Return: 0 on success or error value on failure
*/
static
int
pl35x_nand_remove
(
struct
platform_device
*
pdev
)
{
struct
pl35x_nand_info
*
xnand
=
platform_get_drvdata
(
pdev
);
struct
mtd_info
*
mtd
=
nand_to_mtd
(
&
xnand
->
chip
);
/* Release resources, unregister device */
nand_release
(
mtd
);
return
0
;
}
/* Match table for device tree binding */
static
const
struct
of_device_id
pl35x_nand_of_match
[]
=
{
{
.
compatible
=
"arm,pl353-nand-r2p1"
},
{},
};
MODULE_DEVICE_TABLE
(
of
,
pl35x_nand_of_match
);
/*
* pl35x_nand_driver - This structure defines the NAND subsystem platform driver
*/
static
struct
platform_driver
pl35x_nand_driver
=
{
.
probe
=
pl35x_nand_probe
,
.
remove
=
pl35x_nand_remove
,
.
driver
=
{
.
name
=
PL35X_NAND_DRIVER_NAME
,
.
of_match_table
=
pl35x_nand_of_match
,
},
};
module_platform_driver
(
pl35x_nand_driver
);
MODULE_AUTHOR
(
"Xilinx, Inc."
);
MODULE_ALIAS
(
"platform:"
PL35X_NAND_DRIVER_NAME
);
MODULE_DESCRIPTION
(
"ARM PL35X NAND Flash Driver"
);
MODULE_LICENSE
(
"GPL"
);
src/drivers/mtd/nand/Kconfig
→
src/drivers/mtd/nand/
raw/
Kconfig
View file @
1ac03d4a
...
@@ -11,12 +11,12 @@ config MTD_NAND_ECC_SMC
...
@@ -11,12 +11,12 @@ config MTD_NAND_ECC_SMC
menuconfig MTD_NAND
menuconfig MTD_NAND
tristate "NAND Device Support"
tristate "
Raw/Parallel
NAND Device Support"
depends on MTD
depends on MTD
select MTD_NAND_ECC
select MTD_NAND_ECC
help
help
This enables support for accessing all type of
NAND flash
This enables support for accessing all type of
raw/parallel
devices. For further information see
NAND flash
devices. For further information see
<http://www.linux-mtd.infradead.org/doc/nand.html>.
<http://www.linux-mtd.infradead.org/doc/nand.html>.
if MTD_NAND
if MTD_NAND
...
@@ -44,12 +44,12 @@ config MTD_NAND_DENALI
...
@@ -44,12 +44,12 @@ config MTD_NAND_DENALI
tristate
tristate
config MTD_NAND_DENALI_PCI
config MTD_NAND_DENALI_PCI
tristate "Support Denali NAND controller on Intel Moorestown"
tristate "Support Denali NAND controller on Intel Moorestown"
select MTD_NAND_DENALI
select MTD_NAND_DENALI
depends on
HAS_DMA &&
PCI
depends on PCI
help
help
Enable the driver for NAND flash on Intel Moorestown, using the
Enable the driver for NAND flash on Intel Moorestown, using the
Denali NAND controller core.
Denali NAND controller core.
config MTD_NAND_DENALI_DT
config MTD_NAND_DENALI_DT
tristate "Support Denali NAND controller as a DT device"
tristate "Support Denali NAND controller as a DT device"
...
@@ -77,9 +77,10 @@ config MTD_NAND_AMS_DELTA
...
@@ -77,9 +77,10 @@ config MTD_NAND_AMS_DELTA
config MTD_NAND_OMAP2
config MTD_NAND_OMAP2
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
depends on HAS_IOMEM
help
help
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
and Keystone platforms.
and Keystone platforms.
config MTD_NAND_OMAP_BCH
config MTD_NAND_OMAP_BCH
...
@@ -116,38 +117,6 @@ config MTD_NAND_AU1550
...
@@ -116,38 +117,6 @@ config MTD_NAND_AU1550
This enables the driver for the NAND flash controller on the
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
AMD/Alchemy 1550 SOC.
config MTD_NAND_BF5XX
tristate "Blackfin on-chip NAND Flash Controller driver"
depends on BF54x || BF52x
help
This enables the Blackfin on-chip NAND flash controller
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
This driver can also be built as a module. If so, the module
will be called bf5xx-nand.
config MTD_NAND_BF5XX_HWECC
bool "BF5XX NAND Hardware ECC"
default y
depends on MTD_NAND_BF5XX
help
Enable the use of the BF5XX's internal ECC generator when
using NAND.
config MTD_NAND_BF5XX_BOOTROM_ECC
bool "Use Blackfin BootROM ECC Layout"
default n
depends on MTD_NAND_BF5XX_HWECC
help
If you wish to modify NAND pages and allow the Blackfin on-chip
BootROM to boot from them, say Y here. This is only necessary
if you are booting U-Boot out of NAND and you wish to update
U-Boot from Linux' userspace. Otherwise, you should say N here.
If unsure, say N.
config MTD_NAND_S3C2410
config MTD_NAND_S3C2410
tristate "NAND Flash support for Samsung S3C SoCs"
tristate "NAND Flash support for Samsung S3C SoCs"
depends on ARCH_S3C24XX || ARCH_S3C64XX
depends on ARCH_S3C24XX || ARCH_S3C64XX
...
@@ -169,7 +138,7 @@ config MTD_NAND_NDFC
...
@@ -169,7 +138,7 @@ config MTD_NAND_NDFC
depends on 4xx
depends on 4xx
select MTD_NAND_ECC_SMC
select MTD_NAND_ECC_SMC
help
help
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
config MTD_NAND_S3C2410_CLKSTOP
config MTD_NAND_S3C2410_CLKSTOP
bool "Samsung S3C NAND IDLE clock stop"
bool "Samsung S3C NAND IDLE clock stop"
...
@@ -184,7 +153,7 @@ config MTD_NAND_S3C2410_CLKSTOP
...
@@ -184,7 +153,7 @@ config MTD_NAND_S3C2410_CLKSTOP
config MTD_NAND_TANGO
config MTD_NAND_TANGO
tristate "NAND Flash support for Tango chips"
tristate "NAND Flash support for Tango chips"
depends on ARCH_TANGO || COMPILE_TEST
depends on ARCH_TANGO || COMPILE_TEST
depends on HAS_
DMA
depends on HAS_
IOMEM
help
help
Enables the NAND Flash controller on Tango chips.
Enables the NAND Flash controller on Tango chips.
...
@@ -201,40 +170,40 @@ config MTD_NAND_DISKONCHIP
...
@@ -201,40 +170,40 @@ config MTD_NAND_DISKONCHIP
these devices.
these devices.
config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
bool "Advanced detection options for DiskOnChip"
bool "Advanced detection options for DiskOnChip"
depends on MTD_NAND_DISKONCHIP
depends on MTD_NAND_DISKONCHIP
help
help
This option allows you to specify nonstandard address at which to
This option allows you to specify nonstandard address at which to
probe for a DiskOnChip, or to change the detection options. You
probe for a DiskOnChip, or to change the detection options. You
are unlikely to need any of this unless you are using LinuxBIOS.
are unlikely to need any of this unless you are using LinuxBIOS.
Say 'N'.
Say 'N'.
config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
depends on MTD_NAND_DISKONCHIP
depends on MTD_NAND_DISKONCHIP
default "0"
default "0"
---help---
help
By default, the probe for DiskOnChip devices will look for a
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
This option allows you to specify a single address at which to probe
This option allows you to specify a single address at which to probe
for the device, which is useful if you have other devices in that
for the device, which is useful if you have other devices in that
range which get upset when they are probed.
range which get upset when they are probed.
(Note that on PowerPC, the normal probe will only check at
(Note that on PowerPC, the normal probe will only check at
0xE4000000.)
0xE4000000.)
Normally, you should leave this set to zero, to allow the probe at
Normally, you should leave this set to zero, to allow the probe at
the normal addresses.
the normal addresses.
config MTD_NAND_DISKONCHIP_PROBE_HIGH
config MTD_NAND_DISKONCHIP_PROBE_HIGH
bool "Probe high addresses"
bool "Probe high addresses"
depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
help
help
By default, the probe for DiskOnChip devices will look for a
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
This option changes to make it probe between 0xFFFC8000 and
This option changes to make it probe between 0xFFFC8000 and
0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
useful to you. Say 'N'.
useful to you. Say 'N'.
config MTD_NAND_DISKONCHIP_BBTWRITE
config MTD_NAND_DISKONCHIP_BBTWRITE
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
...
@@ -280,7 +249,8 @@ config MTD_NAND_DOCG4
...
@@ -280,7 +249,8 @@ config MTD_NAND_DOCG4
config MTD_NAND_SHARPSL
config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on ARCH_PXA
depends on ARCH_PXA || COMPILE_TEST
depends on HAS_IOMEM
config MTD_NAND_CAFE
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
tristate "NAND support for OLPC CAFÉ chip"
...
@@ -307,22 +277,30 @@ config MTD_NAND_CS553X
...
@@ -307,22 +277,30 @@ config MTD_NAND_CS553X
config MTD_NAND_ATMEL
config MTD_NAND_ATMEL
tristate "Support for NAND Flash / SmartMedia on AT91"
tristate "Support for NAND Flash / SmartMedia on AT91"
depends on ARCH_AT91
depends on ARCH_AT91 || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_ALLOCATOR
select MFD_ATMEL_SMC
select MFD_ATMEL_SMC
help
help
Enables support for NAND Flash / Smart Media Card interface
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 processors.
on Atmel AT91 processors.
config MTD_NAND_PXA3xx
config MTD_NAND_MARVELL
tristate "NAND support on PXA3xx and Armada 370/XP"
tristate "NAND controller support on Marvell boards"
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
COMPILE_TEST
depends on HAS_IOMEM
help
help
This enables the driver for the NAND flash device found on
This enables the NAND flash controller driver for Marvell boards,
PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
including:
- PXA3xx processors (NFCv1)
- 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
- 64-bit Aramda platforms (7k, 8k) (NFCv2)
config MTD_NAND_SLC_LPC32XX
config MTD_NAND_SLC_LPC32XX
tristate "NXP LPC32xx SLC Controller"
tristate "NXP LPC32xx SLC Controller"
depends on ARCH_LPC32XX
depends on ARCH_LPC32XX || COMPILE_TEST
depends on HAS_IOMEM
help
help
Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
chips) NAND controller. This is the default for the PHYTEC 3250
chips) NAND controller. This is the default for the PHYTEC 3250
...
@@ -333,7 +311,8 @@ config MTD_NAND_SLC_LPC32XX
...
@@ -333,7 +311,8 @@ config MTD_NAND_SLC_LPC32XX
config MTD_NAND_MLC_LPC32XX
config MTD_NAND_MLC_LPC32XX
tristate "NXP LPC32xx MLC Controller"
tristate "NXP LPC32xx MLC Controller"
depends on ARCH_LPC32XX
depends on ARCH_LPC32XX || COMPILE_TEST
depends on HAS_IOMEM
help
help
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
controller. This is the default for the WORK92105 controller
controller. This is the default for the WORK92105 controller
...
@@ -367,19 +346,18 @@ config MTD_NAND_NANDSIM
...
@@ -367,19 +346,18 @@ config MTD_NAND_NANDSIM
MTD nand layer.
MTD nand layer.
config MTD_NAND_GPMI_NAND
config MTD_NAND_GPMI_NAND
tristate "GPMI NAND Flash Controller driver"
tristate "GPMI NAND Flash Controller driver"
depends on MTD_NAND && MXS_DMA
depends on MXS_DMA
help
help
Enables NAND Flash support for IMX23, IMX28 or IMX6.
Enables NAND Flash support for IMX23, IMX28 or IMX6.
The GPMI controller is very powerful, with the help of BCH
The GPMI controller is very powerful, with the help of BCH
module, it can do the hardware ECC. The GPMI supports several
module, it can do the hardware ECC. The GPMI supports several
NAND flashs at the same time. The GPMI may conflicts with other
NAND flashs at the same time.
block, such as SD card. So pay attention to it when you enable
the GPMI.
config MTD_NAND_BRCMNAND
config MTD_NAND_BRCMNAND
tristate "Broadcom STB NAND controller"
tristate "Broadcom STB NAND controller"
depends on ARM || ARM64 || MIPS
depends on ARM || ARM64 || MIPS || COMPILE_TEST
depends on HAS_IOMEM
help
help
Enables the Broadcom NAND controller driver. The controller was
Enables the Broadcom NAND controller driver. The controller was
originally designed for Set-Top Box but is used on various BCM7xxx,
originally designed for Set-Top Box but is used on various BCM7xxx,
...
@@ -388,6 +366,7 @@ config MTD_NAND_BRCMNAND
...
@@ -388,6 +366,7 @@ config MTD_NAND_BRCMNAND
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
depends on BCMA
help
help
BCMA bus can have various flash memories attached, they are
BCMA bus can have various flash memories attached, they are
registered by bcma as platform devices. This enables driver for
registered by bcma as platform devices. This enables driver for
...
@@ -429,7 +408,8 @@ config MTD_NAND_FSL_ELBC
...
@@ -429,7 +408,8 @@ config MTD_NAND_FSL_ELBC
config MTD_NAND_FSL_IFC
config MTD_NAND_FSL_IFC
tristate "NAND support for Freescale IFC controller"
tristate "NAND support for Freescale IFC controller"
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST
depends on HAS_IOMEM
select FSL_IFC
select FSL_IFC
select MEMORY
select MEMORY
help
help
...
@@ -467,7 +447,8 @@ config MTD_NAND_VF610_NFC
...
@@ -467,7 +447,8 @@ config MTD_NAND_VF610_NFC
config MTD_NAND_MXC
config MTD_NAND_MXC
tristate "MXC NAND support"
tristate "MXC NAND support"
depends on ARCH_MXC
depends on ARCH_MXC || COMPILE_TEST
depends on HAS_IOMEM
help
help
This enables the driver for the NAND flash controller on the
This enables the driver for the NAND flash controller on the
MXC processors.
MXC processors.
...
@@ -476,21 +457,22 @@ config MTD_NAND_SH_FLCTL
...
@@ -476,21 +457,22 @@ config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL"
tristate "Support for NAND on Renesas SuperH FLCTL"
depends on SUPERH || COMPILE_TEST
depends on SUPERH || COMPILE_TEST
depends on HAS_IOMEM
depends on HAS_IOMEM
depends on HAS_DMA
help
help
Several Renesas SuperH CPU has FLCTL. This option enables support
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL.
for NAND Flash using FLCTL.
config MTD_NAND_DAVINCI
config MTD_NAND_DAVINCI
tristate "Support NAND on DaVinci/Keystone SoC"
tristate "Support NAND on DaVinci/Keystone SoC"
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF)
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) || COMPILE_TEST
help
depends on HAS_IOMEM
help
Enable the driver for NAND flash chips on Texas Instruments
Enable the driver for NAND flash chips on Texas Instruments
DaVinci/Keystone processors.
DaVinci/Keystone processors.
config MTD_NAND_TXX9NDFMC
config MTD_NAND_TXX9NDFMC
tristate "NAND Flash support for TXx9 SoC"
tristate "NAND Flash support for TXx9 SoC"
depends on SOC_TX4938 || SOC_TX4939
depends on SOC_TX4938 || SOC_TX4939 || COMPILE_TEST
depends on HAS_IOMEM
help
help
This enables the NAND flash controller on the TXx9 SoCs.
This enables the NAND flash controller on the TXx9 SoCs.
...
@@ -502,36 +484,31 @@ config MTD_NAND_SOCRATES
...
@@ -502,36 +484,31 @@ config MTD_NAND_SOCRATES
config MTD_NAND_NUC900
config MTD_NAND_NUC900
tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
depends on ARCH_W90X900
depends on ARCH_W90X900 || COMPILE_TEST
depends on HAS_IOMEM
help
help
This enables the driver for the NAND Flash on evaluation board based
This enables the driver for the NAND Flash on evaluation board based
on w90p910 / NUC9xx.
on w90p910 / NUC9xx.
config MTD_NAND_PL35X
tristate "ARM Pl35X NAND flash driver"
depends on MTD_NAND && ARM
depends on PL35X_SMC
help
This enables access to the NAND flash device on PL351/PL353
SMC controller.
config MTD_NAND_JZ4740
config MTD_NAND_JZ4740
tristate "Support for JZ4740 SoC NAND controller"
tristate "Support for JZ4740 SoC NAND controller"
depends on MACH_JZ4740
depends on MACH_JZ4740 || COMPILE_TEST
depends on HAS_IOMEM
help
help
Enables support for NAND Flash on JZ4740 SoC based boards.
Enables support for NAND Flash on JZ4740 SoC based boards.
config MTD_NAND_JZ4780
config MTD_NAND_JZ4780
tristate "Support for NAND on JZ4780 SoC"
tristate "Support for NAND on JZ4780 SoC"
depends on
MACH_JZ4780 &&
JZ4780_NEMC
depends on JZ4780_NEMC
help
help
Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
based boards, using the BCH controller for hardware error correction.
based boards, using the BCH controller for hardware error correction.
config MTD_NAND_FSMC
config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC"
tristate "Support for NAND on ST Micros FSMC"
depends on OF
depends on OF && HAS_IOMEM
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 || \
COMPILE_TEST
help
help
Enables support for NAND Flash chips on the ST Microelectronics
Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC)
Flexible Static Memory Controller (FSMC)
...
@@ -545,28 +522,22 @@ config MTD_NAND_XWAY
...
@@ -545,28 +522,22 @@ config MTD_NAND_XWAY
config MTD_NAND_SUNXI
config MTD_NAND_SUNXI
tristate "Support for NAND on Allwinner SoCs"
tristate "Support for NAND on Allwinner SoCs"
depends on ARCH_SUNXI
depends on ARCH_SUNXI || COMPILE_TEST
help
Enables support for NAND Flash chips on Allwinner SoCs.
config MTD_NAND_ARASAN
tristate "Support for Arasan Nand Flash controller"
depends on HAS_IOMEM
depends on HAS_IOMEM
depends on HAS_DMA
help
help
Enables the driver for the Arasan Nand Flash controller on
Enables support for NAND Flash chips on Allwinner SoCs.
Zynq Ultrascale+ MPSoC.
config MTD_NAND_HISI504
config MTD_NAND_HISI504
tristate "Support for NAND controller on Hisilicon SoC Hip04"
tristate "Support for NAND controller on Hisilicon SoC Hip04"
depends on ARCH_HISI || COMPILE_TEST
depends on ARCH_HISI || COMPILE_TEST
depends on HAS_
DMA
depends on HAS_
IOMEM
help
help
Enables support for NAND controller on Hisilicon SoC Hip04.
Enables support for NAND controller on Hisilicon SoC Hip04.
config MTD_NAND_QCOM
config MTD_NAND_QCOM
tristate "Support for NAND on QCOM SoCs"
tristate "Support for NAND on QCOM SoCs"
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
depends on HAS_IOMEM
help
help
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
controller. This controller is found on IPQ806x SoC.
controller. This controller is found on IPQ806x SoC.
...
@@ -574,12 +545,37 @@ config MTD_NAND_QCOM
...
@@ -574,12 +545,37 @@ config MTD_NAND_QCOM
config MTD_NAND_MTK
config MTD_NAND_MTK
tristate "Support for NAND controller on MTK SoCs"
tristate "Support for NAND controller on MTK SoCs"
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on HAS_
DMA
depends on HAS_
IOMEM
help
help
Enables support for NAND controller on MTK SoCs.
Enables support for NAND controller on MTK SoCs.
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
# Elphel (comes from elphel393.cfg)
config MTD_NAND_TEGRA
tristate "Support for NAND controller on NVIDIA Tegra"
depends on ARCH_TEGRA || COMPILE_TEST
depends on HAS_IOMEM
help
Enables support for NAND flash controller on NVIDIA Tegra SoC.
The driver has been developed and tested on a Tegra 2 SoC. DMA
support, raw read/write page as well as HW ECC read/write page
is supported. Extra OOB bytes when using HW ECC are currently
not supported.
config MTD_NAND_ARASAN
tristate "Support for Arasan Nand Flash controller"
depends on HAS_IOMEM && HAS_DMA
help
Enables the driver for the Arasan Nand Flash controller on
Zynq Ultrascale+ MPSoC.
config MTD_NAND_PL353
tristate "ARM Pl353 NAND flash driver"
depends on MTD_NAND && ARM
depends on PL353_SMC
help
Enables support for PrimeCell Static Memory Controller PL353.
# Elphel (enabled in elphel393.cfg)
config MTD_NAND_OTP
config MTD_NAND_OTP
tristate "NAND OTP area r/w support for Micron devices"
tristate "NAND OTP area r/w support for Micron devices"
default n
default n
...
...
src/drivers/mtd/nand/Makefile
→
src/drivers/mtd/nand/
raw/
Makefile
View file @
1ac03d4a
# SPDX-License-Identifier: GPL-2.0
# SPDX-License-Identifier: GPL-2.0
#
# linux/drivers/nand/Makefile
#
obj-$(CONFIG_MTD_NAND)
+=
nand.o
obj-$(CONFIG_MTD_NAND)
+=
nand.o
obj-$(CONFIG_MTD_NAND_ECC)
+=
nand_ecc.o
obj-$(CONFIG_MTD_NAND_ECC)
+=
nand_ecc.o
...
@@ -14,7 +11,6 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
...
@@ -14,7 +11,6 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
obj-$(CONFIG_MTD_NAND_DENALI_PCI)
+=
denali_pci.o
obj-$(CONFIG_MTD_NAND_DENALI_PCI)
+=
denali_pci.o
obj-$(CONFIG_MTD_NAND_DENALI_DT)
+=
denali_dt.o
obj-$(CONFIG_MTD_NAND_DENALI_DT)
+=
denali_dt.o
obj-$(CONFIG_MTD_NAND_AU1550)
+=
au1550nd.o
obj-$(CONFIG_MTD_NAND_AU1550)
+=
au1550nd.o
obj-$(CONFIG_MTD_NAND_BF5XX)
+=
bf5xx_nand.o
obj-$(CONFIG_MTD_NAND_S3C2410)
+=
s3c2410.o
obj-$(CONFIG_MTD_NAND_S3C2410)
+=
s3c2410.o
obj-$(CONFIG_MTD_NAND_TANGO)
+=
tango_nand.o
obj-$(CONFIG_MTD_NAND_TANGO)
+=
tango_nand.o
obj-$(CONFIG_MTD_NAND_DAVINCI)
+=
davinci_nand.o
obj-$(CONFIG_MTD_NAND_DAVINCI)
+=
davinci_nand.o
...
@@ -31,7 +27,7 @@ omap2_nand-objs := omap2.o
...
@@ -31,7 +27,7 @@ omap2_nand-objs := omap2.o
obj-$(CONFIG_MTD_NAND_OMAP2)
+=
omap2_nand.o
obj-$(CONFIG_MTD_NAND_OMAP2)
+=
omap2_nand.o
obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD)
+=
omap_elm.o
obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD)
+=
omap_elm.o
obj-$(CONFIG_MTD_NAND_CM_X270)
+=
cmx270_nand.o
obj-$(CONFIG_MTD_NAND_CM_X270)
+=
cmx270_nand.o
obj-$(CONFIG_MTD_NAND_
PXA3xx)
+=
pxa3xx
_nand.o
obj-$(CONFIG_MTD_NAND_
MARVELL)
+=
marvell
_nand.o
obj-$(CONFIG_MTD_NAND_TMIO)
+=
tmio_nand.o
obj-$(CONFIG_MTD_NAND_TMIO)
+=
tmio_nand.o
obj-$(CONFIG_MTD_NAND_PLATFORM)
+=
plat_nand.o
obj-$(CONFIG_MTD_NAND_PLATFORM)
+=
plat_nand.o
obj-$(CONFIG_MTD_NAND_PASEMI)
+=
pasemi_nand.o
obj-$(CONFIG_MTD_NAND_PASEMI)
+=
pasemi_nand.o
...
@@ -56,17 +52,18 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
...
@@ -56,17 +52,18 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY)
+=
xway_nand.o
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_PL35X)
+=
pl35x_nand.o
obj-$(CONFIG_MTD_NAND_ARASAN)
+=
arasan_nand.o
obj-$(CONFIG_MTD_NAND_HISI504)
+=
hisi504_nand.o
obj-$(CONFIG_MTD_NAND_HISI504)
+=
hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND)
+=
brcmnand/
obj-$(CONFIG_MTD_NAND_BRCMNAND)
+=
brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM)
+=
qcom_nandc.o
obj-$(CONFIG_MTD_NAND_QCOM)
+=
qcom_nandc.o
obj-$(CONFIG_MTD_NAND_MTK)
+=
mtk_nand.o
mtk_ecc.o
obj-$(CONFIG_MTD_NAND_MTK)
+=
mtk_ecc.o
mtk_nand.o
obj-$(CONFIG_MTD_NAND_TEGRA)
+=
tegra_nand.o
obj-$(CONFIG_MTD_NAND_ARASAN)
+=
arasan_nand.o
obj-$(CONFIG_MTD_NAND_PL353)
+=
pl353_nand.o
nand-objs
:=
nand_base.o nand_bbt.o nand_timings.o nand_ids.o
nandchip-micron.o
nand-objs
:=
nand_base.o nand_bbt.o nand_timings.o nand_ids.o
nand-objs
+=
nand_amd.o
nand-objs
+=
nand_amd.o
nand-objs
+=
nand_hynix.o
nand-objs
+=
nand_hynix.o
nand-objs
+=
nand_macronix.o
nand-objs
+=
nand_macronix.o
nand-objs
+=
nand_micron.o
nand-objs
+=
nand_micron.o
nand_micron_mt29f.o
nand-objs
+=
nand_samsung.o
nand-objs
+=
nand_samsung.o
nand-objs
+=
nand_toshiba.o
nand-objs
+=
nand_toshiba.o
src/drivers/mtd/nand/nand.h
→
src/drivers/mtd/nand/
raw/
nand.h
View file @
1ac03d4a
...
@@ -2,12 +2,11 @@
...
@@ -2,12 +2,11 @@
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
int
nand_check_wp
(
struct
mtd_info
*
mtd
);
int
check_offs_len
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
uint64_t
len
);
int
nand_get_device
(
struct
mtd_info
*
mtd
,
int
new_state
);
int
nand_get_device
(
struct
mtd_info
*
mtd
,
int
new_state
);
void
nand_release_device
(
struct
mtd_info
*
mtd
);
void
nand_release_device
(
struct
mtd_info
*
mtd
);
int
nand_do_read_ops
(
struct
mtd_info
*
mtd
,
loff_t
from
,
struct
mtd_oob_ops
*
ops
);
int
nand_do_write_ops
(
struct
mtd_info
*
mtd
,
loff_t
to
,
struct
mtd_oob_ops
*
ops
);
int
nand_do_read_ops
(
struct
mtd_info
*
mtd
,
loff_t
from
,
void
nand_micron_mt29f_init
(
struct
mtd_info
*
mtd
,
int
dev_id
);
struct
mtd_oob_ops
*
ops
);
int
nand_do_write_ops
(
struct
mtd_info
*
mtd
,
loff_t
to
,
struct
mtd_oob_ops
*
ops
);
void
nandchip_micron_init
(
struct
mtd_info
*
mtd
,
int
dev_id
);
src/drivers/mtd/nand/nand_base.c
→
src/drivers/mtd/nand/
raw/
nand_base.c
View file @
1ac03d4a
This source diff could not be displayed because it is too large. You can
view the blob
instead.
src/drivers/mtd/nand/raw/nand_micron.c
0 → 100644
View file @
1ac03d4a
/*
* Copyright (C) 2017 Free Electrons
* Copyright (C) 2017 NextThing Co
*
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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/mtd/rawnand.h>
#include <linux/slab.h>
#include "nand.h"
/*
* Special Micron status bit 3 indicates that the block has been
* corrected by on-die ECC and should be rewritten.
*/
#define NAND_ECC_STATUS_WRITE_RECOMMENDED BIT(3)
/*
* On chips with 8-bit ECC and additional bit can be used to distinguish
* cases where a errors were corrected without needing a rewrite
*
* Bit 4 Bit 3 Bit 0 Description
* ----- ----- ----- -----------
* 0 0 0 No Errors
* 0 0 1 Multiple uncorrected errors
* 0 1 0 4 - 6 errors corrected, recommend rewrite
* 0 1 1 Reserved
* 1 0 0 1 - 3 errors corrected
* 1 0 1 Reserved
* 1 1 0 7 - 8 errors corrected, recommend rewrite
*/
#define NAND_ECC_STATUS_MASK (BIT(4) | BIT(3) | BIT(0))
#define NAND_ECC_STATUS_UNCORRECTABLE BIT(0)
#define NAND_ECC_STATUS_4_6_CORRECTED BIT(3)
#define NAND_ECC_STATUS_1_3_CORRECTED BIT(4)
#define NAND_ECC_STATUS_7_8_CORRECTED (BIT(4) | BIT(3))
struct
nand_onfi_vendor_micron
{
u8
two_plane_read
;
u8
read_cache
;
u8
read_unique_id
;
u8
dq_imped
;
u8
dq_imped_num_settings
;
u8
dq_imped_feat_addr
;
u8
rb_pulldown_strength
;
u8
rb_pulldown_strength_feat_addr
;
u8
rb_pulldown_strength_num_settings
;
u8
otp_mode
;
u8
otp_page_start
;
u8
otp_data_prot_addr
;
u8
otp_num_pages
;
u8
otp_feat_addr
;
u8
read_retry_options
;
u8
reserved
[
72
];
u8
param_revision
;
}
__packed
;
struct
micron_on_die_ecc
{
bool
forced
;
bool
enabled
;
void
*
rawbuf
;
};
struct
micron_nand
{
struct
micron_on_die_ecc
ecc
;
};
// Elphel begin
/*
Elphel - reintroducing mtd->_lock/_unlock as suggested in d4cb37e71662dac72049bf7c55a9038bd7d2bcb5
of the mainline kernel, which originally dropped these functions.
"
mtd: nand: Remove support for block locking/unlocking
Commit 7d70f33 ("mtd: nand: add lock/unlock routines") introduced
support for the Micron LOCK/UNLOCK commands but no one ever used the
nand_lock/unlock() functions.
Remove support for these vendor-specific operations from the core. If
one ever wants to add them back they should be put in nand_micron.c and
mtd->_lock/_unlock should be directly assigned from there instead of
exporting the functions.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
"
*/
#define NAND_CMD_LOCK 0x2a
#define NAND_CMD_UNLOCK1 0x23
#define NAND_CMD_UNLOCK2 0x24
/**
* nand_unlock_op - Do UNLOCK operation
* @chip: The NAND chip
* @ofs: offset to start unlock from
* @len: length to unlock
* @invert: when = 0, unlock the range of blocks within the lower and
* upper boundary address
* when = 1, unlock the range of blocks outside the boundaries
* of the lower and upper boundary address
*
* This function sends an UNLOCK1 and UNLOCK2 commands and checks status
* before returning.
*
* Elphel:
* * Note 1: see Micron MT29F8G08ADBDAH4
* * Note 2: datasheet: timings for LOCK/UNLOCK are not provided
* * Note 3: datasheet: mandatory status checking is not implied
*
* Returns 0 on success, a negative error code otherwise.
*/
static
int
nand_unlock_op
(
struct
nand_chip
*
chip
,
loff_t
ofs
,
uint64_t
len
,
int
invert
)
{
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
/* Address of the lower boundary */
unsigned
int
page_lo
=
ofs
>>
chip
->
page_shift
;
/* Address of the upper boundary */
unsigned
int
page_hi
=
(
ofs
+
len
)
>>
chip
->
page_shift
;
int
ret
;
u8
status
;
if
(
chip
->
exec_op
)
{
u8
addrs_lo
[
3
]
=
{
page_lo
,
page_lo
>>
8
,
page_lo
>>
16
};
u8
addrs_hi
[
3
]
=
{
page_hi
|
invert
,
page_hi
>>
8
,
page_hi
>>
16
};
struct
nand_op_instr
instrs
[]
=
{
NAND_OP_CMD
(
NAND_CMD_UNLOCK1
,
0
),
NAND_OP_ADDR
(
3
,
addrs_lo
,
0
),
NAND_OP_WAIT_RDY
(
0
,
0
),
NAND_OP_CMD
(
NAND_CMD_UNLOCK2
,
0
),
NAND_OP_ADDR
(
3
,
addrs_hi
,
0
),
NAND_OP_WAIT_RDY
(
0
,
0
),
};
struct
nand_operation
op
=
NAND_OPERATION
(
instrs
);
ret
=
nand_exec_op
(
chip
,
&
op
);
if
(
ret
)
return
ret
;
// not necessary
ret
=
nand_status_op
(
chip
,
&
status
);
if
(
ret
)
return
ret
;
}
else
{
chip
->
cmdfunc
(
mtd
,
NAND_CMD_UNLOCK1
,
-
1
,
page_lo
&
chip
->
pagemask
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_UNLOCK2
,
-
1
,
(
page_hi
|
invert
)
&
chip
->
pagemask
);
ret
=
chip
->
waitfunc
(
mtd
,
chip
);
if
(
ret
<
0
)
return
ret
;
status
=
ret
;
}
if
(
status
&
NAND_STATUS_FAIL
)
return
-
EIO
;
return
0
;
}
/**
* nand_unlock - unlocks specified locked blocks
* @mtd: mtd info
* @ofs: offset to start unlock from
* @len: length to unlock
*
* Returns unlock status.
*/
static
int
micron_nand_unlock
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
uint64_t
len
)
{
int
ret
=
0
;
int
chipnr
;
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
__func__
,
(
unsigned
long
long
)
ofs
,
len
);
if
(
check_offs_len
(
mtd
,
ofs
,
len
))
return
-
EINVAL
;
/* Align to last block address if size addresses end of the device */
if
(
ofs
+
len
==
mtd
->
size
)
len
-=
mtd
->
erasesize
;
nand_get_device
(
mtd
,
FL_UNLOCKING
);
/* Shift to get chip number */
chipnr
=
ofs
>>
chip
->
chip_shift
;
/*
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* we must reset the chip
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
nand_reset
(
chip
,
chipnr
);
chip
->
select_chip
(
mtd
,
chipnr
);
/* Check, if it is write protected */
if
(
nand_check_wp
(
mtd
))
{
pr_debug
(
"%s: device is write protected!
\n
"
,
__func__
);
ret
=
-
EIO
;
goto
out
;
}
ret
=
nand_unlock_op
(
chip
,
ofs
,
len
,
0x0
);
out:
chip
->
select_chip
(
mtd
,
-
1
);
nand_release_device
(
mtd
);
return
ret
;
}
/**
* micron_nand_lock - locks all blocks present in the device
* @mtd: mtd info
* @ofs: offset to start unlock from
* @len: length to unlock
*
* This feature is not supported in many NAND parts. 'Micron' NAND parts do
* have this feature, but it allows only to lock all blocks, not for specified
* range for block. Implementing 'lock' feature by making use of 'unlock', for
* now.
*
* Returns lock status.
*/
static
int
micron_nand_lock
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
uint64_t
len
)
{
int
ret
=
0
;
int
chipnr
,
status
,
page
;
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
__func__
,
(
unsigned
long
long
)
ofs
,
len
);
if
(
check_offs_len
(
mtd
,
ofs
,
len
))
return
-
EINVAL
;
nand_get_device
(
mtd
,
FL_LOCKING
);
/* Shift to get chip number */
chipnr
=
ofs
>>
chip
->
chip_shift
;
/*
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* we must reset the chip
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
nand_reset
(
chip
,
chipnr
);
chip
->
select_chip
(
mtd
,
chipnr
);
/* Check, if it is write protected */
if
(
nand_check_wp
(
mtd
))
{
pr_debug
(
"%s: device is write protected!
\n
"
,
__func__
);
ret
=
-
EIO
;
goto
out
;
}
if
(
chip
->
exec_op
)
{
u8
op_status
;
struct
nand_op_instr
instrs
[]
=
{
NAND_OP_CMD
(
NAND_CMD_LOCK
,
0
),
};
struct
nand_operation
op
=
NAND_OPERATION
(
instrs
);
ret
=
nand_exec_op
(
chip
,
&
op
);
if
(
ret
)
return
ret
;
ret
=
nand_status_op
(
chip
,
&
op_status
);
if
(
ret
)
return
ret
;
}
else
{
/* Submit address of first page to lock */
page
=
ofs
>>
chip
->
page_shift
;
chip
->
cmdfunc
(
mtd
,
NAND_CMD_LOCK
,
-
1
,
page
&
chip
->
pagemask
);
/* Call wait ready function */
status
=
chip
->
waitfunc
(
mtd
,
chip
);
/* See if device thinks it succeeded */
if
(
status
&
NAND_STATUS_FAIL
)
{
pr_debug
(
"%s: error status = 0x%08x
\n
"
,
__func__
,
status
);
ret
=
-
EIO
;
goto
out
;
}
}
// now unlock the outer range?
ret
=
nand_unlock_op
(
chip
,
ofs
,
len
,
0x1
);
out:
chip
->
select_chip
(
mtd
,
-
1
);
nand_release_device
(
mtd
);
return
ret
;
}
// Elphel end
static
int
micron_nand_setup_read_retry
(
struct
mtd_info
*
mtd
,
int
retry_mode
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
u8
feature
[
ONFI_SUBFEATURE_PARAM_LEN
]
=
{
retry_mode
};
return
nand_set_features
(
chip
,
ONFI_FEATURE_ADDR_READ_RETRY
,
feature
);
}
/*
* Configure chip properties from Micron vendor-specific ONFI table
*/
static
int
micron_nand_onfi_init
(
struct
nand_chip
*
chip
)
{
struct
nand_parameters
*
p
=
&
chip
->
parameters
;
if
(
p
->
onfi
)
{
struct
nand_onfi_vendor_micron
*
micron
=
(
void
*
)
p
->
onfi
->
vendor
;
chip
->
read_retries
=
micron
->
read_retry_options
;
chip
->
setup_read_retry
=
micron_nand_setup_read_retry
;
}
if
(
p
->
supports_set_get_features
)
{
set_bit
(
ONFI_FEATURE_ADDR_READ_RETRY
,
p
->
set_feature_list
);
set_bit
(
ONFI_FEATURE_ON_DIE_ECC
,
p
->
set_feature_list
);
set_bit
(
ONFI_FEATURE_ADDR_READ_RETRY
,
p
->
get_feature_list
);
set_bit
(
ONFI_FEATURE_ON_DIE_ECC
,
p
->
get_feature_list
);
}
return
0
;
}
static
int
micron_nand_on_die_4_ooblayout_ecc
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
if
(
section
>=
4
)
return
-
ERANGE
;
oobregion
->
offset
=
(
section
*
16
)
+
8
;
oobregion
->
length
=
8
;
return
0
;
}
static
int
micron_nand_on_die_4_ooblayout_free
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
if
(
section
>=
4
)
return
-
ERANGE
;
oobregion
->
offset
=
(
section
*
16
)
+
2
;
oobregion
->
length
=
6
;
return
0
;
}
static
const
struct
mtd_ooblayout_ops
micron_nand_on_die_4_ooblayout_ops
=
{
.
ecc
=
micron_nand_on_die_4_ooblayout_ecc
,
.
free
=
micron_nand_on_die_4_ooblayout_free
,
};
static
int
micron_nand_on_die_8_ooblayout_ecc
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
)
return
-
ERANGE
;
oobregion
->
offset
=
mtd
->
oobsize
-
chip
->
ecc
.
total
;
oobregion
->
length
=
chip
->
ecc
.
total
;
return
0
;
}
static
int
micron_nand_on_die_8_ooblayout_free
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
if
(
section
)
return
-
ERANGE
;
oobregion
->
offset
=
2
;
oobregion
->
length
=
mtd
->
oobsize
-
chip
->
ecc
.
total
-
2
;
return
0
;
}
static
const
struct
mtd_ooblayout_ops
micron_nand_on_die_8_ooblayout_ops
=
{
.
ecc
=
micron_nand_on_die_8_ooblayout_ecc
,
.
free
=
micron_nand_on_die_8_ooblayout_free
,
};
static
int
micron_nand_on_die_ecc_setup
(
struct
nand_chip
*
chip
,
bool
enable
)
{
struct
micron_nand
*
micron
=
nand_get_manufacturer_data
(
chip
);
u8
feature
[
ONFI_SUBFEATURE_PARAM_LEN
]
=
{
0
,
};
int
ret
;
if
(
micron
->
ecc
.
forced
)
return
0
;
if
(
micron
->
ecc
.
enabled
==
enable
)
return
0
;
if
(
enable
)
feature
[
0
]
|=
ONFI_FEATURE_ON_DIE_ECC_EN
;
ret
=
nand_set_features
(
chip
,
ONFI_FEATURE_ON_DIE_ECC
,
feature
);
if
(
!
ret
)
micron
->
ecc
.
enabled
=
enable
;
return
ret
;
}
static
int
micron_nand_on_die_ecc_status_4
(
struct
nand_chip
*
chip
,
u8
status
,
void
*
buf
,
int
page
,
int
oob_required
)
{
struct
micron_nand
*
micron
=
nand_get_manufacturer_data
(
chip
);
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
unsigned
int
step
,
max_bitflips
=
0
;
int
ret
;
if
(
!
(
status
&
NAND_ECC_STATUS_WRITE_RECOMMENDED
))
{
if
(
status
&
NAND_STATUS_FAIL
)
mtd
->
ecc_stats
.
failed
++
;
return
0
;
}
/*
* The internal ECC doesn't tell us the number of bitflips that have
* been corrected, but tells us if it recommends to rewrite the block.
* If it's the case, we need to read the page in raw mode and compare
* its content to the corrected version to extract the actual number of
* bitflips.
* But before we do that, we must make sure we have all OOB bytes read
* in non-raw mode, even if the user did not request those bytes.
*/
if
(
!
oob_required
)
{
ret
=
nand_read_data_op
(
chip
,
chip
->
oob_poi
,
mtd
->
oobsize
,
false
);
if
(
ret
)
return
ret
;
}
micron_nand_on_die_ecc_setup
(
chip
,
false
);
ret
=
nand_read_page_op
(
chip
,
page
,
0
,
micron
->
ecc
.
rawbuf
,
mtd
->
writesize
+
mtd
->
oobsize
);
if
(
ret
)
return
ret
;
for
(
step
=
0
;
step
<
chip
->
ecc
.
steps
;
step
++
)
{
unsigned
int
offs
,
i
,
nbitflips
=
0
;
u8
*
rawbuf
,
*
corrbuf
;
offs
=
step
*
chip
->
ecc
.
size
;
rawbuf
=
micron
->
ecc
.
rawbuf
+
offs
;
corrbuf
=
buf
+
offs
;
for
(
i
=
0
;
i
<
chip
->
ecc
.
size
;
i
++
)
nbitflips
+=
hweight8
(
corrbuf
[
i
]
^
rawbuf
[
i
]);
offs
=
(
step
*
16
)
+
4
;
rawbuf
=
micron
->
ecc
.
rawbuf
+
mtd
->
writesize
+
offs
;
corrbuf
=
chip
->
oob_poi
+
offs
;
for
(
i
=
0
;
i
<
chip
->
ecc
.
bytes
+
4
;
i
++
)
nbitflips
+=
hweight8
(
corrbuf
[
i
]
^
rawbuf
[
i
]);
if
(
WARN_ON
(
nbitflips
>
chip
->
ecc
.
strength
))
return
-
EINVAL
;
max_bitflips
=
max
(
nbitflips
,
max_bitflips
);
mtd
->
ecc_stats
.
corrected
+=
nbitflips
;
}
return
max_bitflips
;
}
static
int
micron_nand_on_die_ecc_status_8
(
struct
nand_chip
*
chip
,
u8
status
)
{
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
/*
* With 8/512 we have more information but still don't know precisely
* how many bit-flips were seen.
*/
switch
(
status
&
NAND_ECC_STATUS_MASK
)
{
case
NAND_ECC_STATUS_UNCORRECTABLE
:
mtd
->
ecc_stats
.
failed
++
;
return
0
;
case
NAND_ECC_STATUS_1_3_CORRECTED
:
mtd
->
ecc_stats
.
corrected
+=
3
;
return
3
;
case
NAND_ECC_STATUS_4_6_CORRECTED
:
mtd
->
ecc_stats
.
corrected
+=
6
;
/* rewrite recommended */
return
6
;
case
NAND_ECC_STATUS_7_8_CORRECTED
:
mtd
->
ecc_stats
.
corrected
+=
8
;
/* rewrite recommended */
return
8
;
default:
return
0
;
}
}
static
int
micron_nand_read_page_on_die_ecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
u8
status
;
int
ret
,
max_bitflips
=
0
;
ret
=
micron_nand_on_die_ecc_setup
(
chip
,
true
);
if
(
ret
)
return
ret
;
ret
=
nand_read_page_op
(
chip
,
page
,
0
,
NULL
,
0
);
if
(
ret
)
goto
out
;
ret
=
nand_status_op
(
chip
,
&
status
);
if
(
ret
)
goto
out
;
ret
=
nand_exit_status_op
(
chip
);
if
(
ret
)
goto
out
;
ret
=
nand_read_data_op
(
chip
,
buf
,
mtd
->
writesize
,
false
);
if
(
!
ret
&&
oob_required
)
ret
=
nand_read_data_op
(
chip
,
chip
->
oob_poi
,
mtd
->
oobsize
,
false
);
if
(
chip
->
ecc
.
strength
==
4
)
max_bitflips
=
micron_nand_on_die_ecc_status_4
(
chip
,
status
,
buf
,
page
,
oob_required
);
else
max_bitflips
=
micron_nand_on_die_ecc_status_8
(
chip
,
status
);
out:
micron_nand_on_die_ecc_setup
(
chip
,
false
);
return
ret
?
ret
:
max_bitflips
;
}
static
int
micron_nand_write_page_on_die_ecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
int
ret
;
ret
=
micron_nand_on_die_ecc_setup
(
chip
,
true
);
if
(
ret
)
return
ret
;
ret
=
nand_write_page_raw
(
mtd
,
chip
,
buf
,
oob_required
,
page
);
micron_nand_on_die_ecc_setup
(
chip
,
false
);
return
ret
;
}
enum
{
/* The NAND flash doesn't support on-die ECC */
MICRON_ON_DIE_UNSUPPORTED
,
/*
* The NAND flash supports on-die ECC and it can be
* enabled/disabled by a set features command.
*/
MICRON_ON_DIE_SUPPORTED
,
/*
* The NAND flash supports on-die ECC, and it cannot be
* disabled.
*/
MICRON_ON_DIE_MANDATORY
,
};
#define MICRON_ID_INTERNAL_ECC_MASK GENMASK(1, 0)
#define MICRON_ID_ECC_ENABLED BIT(7)
/*
* Try to detect if the NAND support on-die ECC. To do this, we enable
* the feature, and read back if it has been enabled as expected. We
* also check if it can be disabled, because some Micron NANDs do not
* allow disabling the on-die ECC and we don't support such NANDs for
* now.
*
* This function also has the side effect of disabling on-die ECC if
* it had been left enabled by the firmware/bootloader.
*/
static
int
micron_supports_on_die_ecc
(
struct
nand_chip
*
chip
)
{
u8
id
[
5
];
int
ret
;
if
(
!
chip
->
parameters
.
onfi
)
return
MICRON_ON_DIE_UNSUPPORTED
;
if
(
chip
->
bits_per_cell
!=
1
)
return
MICRON_ON_DIE_UNSUPPORTED
;
/*
* We only support on-die ECC of 4/512 or 8/512
*/
if
(
chip
->
ecc_strength_ds
!=
4
&&
chip
->
ecc_strength_ds
!=
8
)
return
MICRON_ON_DIE_UNSUPPORTED
;
/* 0x2 means on-die ECC is available. */
if
(
chip
->
id
.
len
!=
5
||
(
chip
->
id
.
data
[
4
]
&
MICRON_ID_INTERNAL_ECC_MASK
)
!=
0x2
)
return
MICRON_ON_DIE_UNSUPPORTED
;
ret
=
micron_nand_on_die_ecc_setup
(
chip
,
true
);
if
(
ret
)
return
MICRON_ON_DIE_UNSUPPORTED
;
ret
=
nand_readid_op
(
chip
,
0
,
id
,
sizeof
(
id
));
if
(
ret
)
return
MICRON_ON_DIE_UNSUPPORTED
;
if
(
!
(
id
[
4
]
&
MICRON_ID_ECC_ENABLED
))
return
MICRON_ON_DIE_UNSUPPORTED
;
ret
=
micron_nand_on_die_ecc_setup
(
chip
,
false
);
if
(
ret
)
return
MICRON_ON_DIE_UNSUPPORTED
;
ret
=
nand_readid_op
(
chip
,
0
,
id
,
sizeof
(
id
));
if
(
ret
)
return
MICRON_ON_DIE_UNSUPPORTED
;
if
(
id
[
4
]
&
MICRON_ID_ECC_ENABLED
)
return
MICRON_ON_DIE_MANDATORY
;
/*
* We only support on-die ECC of 4/512 or 8/512
*/
if
(
chip
->
ecc_strength_ds
!=
4
&&
chip
->
ecc_strength_ds
!=
8
)
return
MICRON_ON_DIE_UNSUPPORTED
;
return
MICRON_ON_DIE_SUPPORTED
;
}
static
int
micron_nand_init
(
struct
nand_chip
*
chip
)
{
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
struct
micron_nand
*
micron
;
int
ondie
;
int
ret
;
micron
=
kzalloc
(
sizeof
(
*
micron
),
GFP_KERNEL
);
if
(
!
micron
)
return
-
ENOMEM
;
nand_set_manufacturer_data
(
chip
,
micron
);
ret
=
micron_nand_onfi_init
(
chip
);
if
(
ret
)
goto
err_free_manuf_data
;
if
(
mtd
->
writesize
==
2048
)
chip
->
bbt_options
|=
NAND_BBT_SCAN2NDPAGE
;
ondie
=
micron_supports_on_die_ecc
(
chip
);
if
(
ondie
==
MICRON_ON_DIE_MANDATORY
&&
chip
->
ecc
.
mode
!=
NAND_ECC_ON_DIE
)
{
pr_err
(
"On-die ECC forcefully enabled, not supported
\n
"
);
ret
=
-
EINVAL
;
goto
err_free_manuf_data
;
}
if
(
chip
->
ecc
.
mode
==
NAND_ECC_ON_DIE
)
{
if
(
ondie
==
MICRON_ON_DIE_UNSUPPORTED
)
{
pr_err
(
"On-die ECC selected but not supported
\n
"
);
ret
=
-
EINVAL
;
goto
err_free_manuf_data
;
}
if
(
ondie
==
MICRON_ON_DIE_MANDATORY
)
{
micron
->
ecc
.
forced
=
true
;
micron
->
ecc
.
enabled
=
true
;
}
/*
* In case of 4bit on-die ECC, we need a buffer to store a
* page dumped in raw mode so that we can compare its content
* to the same page after ECC correction happened and extract
* the real number of bitflips from this comparison.
* That's not needed for 8-bit ECC, because the status expose
* a better approximation of the number of bitflips in a page.
*/
if
(
chip
->
ecc_strength_ds
==
4
)
{
micron
->
ecc
.
rawbuf
=
kmalloc
(
mtd
->
writesize
+
mtd
->
oobsize
,
GFP_KERNEL
);
if
(
!
micron
->
ecc
.
rawbuf
)
{
ret
=
-
ENOMEM
;
goto
err_free_manuf_data
;
}
}
if
(
chip
->
ecc_strength_ds
==
4
)
mtd_set_ooblayout
(
mtd
,
&
micron_nand_on_die_4_ooblayout_ops
);
else
mtd_set_ooblayout
(
mtd
,
&
micron_nand_on_die_8_ooblayout_ops
);
chip
->
ecc
.
bytes
=
chip
->
ecc_strength_ds
*
2
;
// this is already read from the device tree
if
(
!
chip
->
ecc
.
size
){
chip
->
ecc
.
size
=
512
;
}
chip
->
ecc
.
strength
=
chip
->
ecc_strength_ds
;
chip
->
ecc
.
algo
=
NAND_ECC_BCH
;
chip
->
ecc
.
read_page
=
micron_nand_read_page_on_die_ecc
;
chip
->
ecc
.
write_page
=
micron_nand_write_page_on_die_ecc
;
if
(
ondie
==
MICRON_ON_DIE_MANDATORY
)
{
chip
->
ecc
.
read_page_raw
=
nand_read_page_raw_notsupp
;
chip
->
ecc
.
write_page_raw
=
nand_write_page_raw_notsupp
;
}
else
{
chip
->
ecc
.
read_page_raw
=
nand_read_page_raw
;
chip
->
ecc
.
write_page_raw
=
nand_write_page_raw
;
}
}
// Elphel: functions to work with OTP area: *user_prot*
// chip->id.data[1] is dev_id (0xa3 in Elphel 10393)
nand_micron_mt29f_init
(
mtd
,
chip
->
id
.
data
[
1
]);
// Elphel: modification for Micron NAND chips
//TODO: add Micron chip ID checking
mtd
->
_unlock
=
micron_nand_unlock
;
mtd
->
_lock
=
micron_nand_lock
;
return
0
;
err_free_manuf_data:
kfree
(
micron
->
ecc
.
rawbuf
);
kfree
(
micron
);
return
ret
;
}
static
void
micron_nand_cleanup
(
struct
nand_chip
*
chip
)
{
struct
micron_nand
*
micron
=
nand_get_manufacturer_data
(
chip
);
kfree
(
micron
->
ecc
.
rawbuf
);
kfree
(
micron
);
}
static
void
micron_fixup_onfi_param_page
(
struct
nand_chip
*
chip
,
struct
nand_onfi_params
*
p
)
{
/*
* MT29F1G08ABAFAWP-ITE:F and possibly others report 00 00 for the
* revision number field of the ONFI parameter page. Assume ONFI
* version 1.0 if the revision number is 00 00.
*/
if
(
le16_to_cpu
(
p
->
revision
)
==
0
)
p
->
revision
=
cpu_to_le16
(
ONFI_VERSION_1_0
);
}
const
struct
nand_manufacturer_ops
micron_nand_manuf_ops
=
{
.
init
=
micron_nand_init
,
.
cleanup
=
micron_nand_cleanup
,
.
fixup_onfi_param_page
=
micron_fixup_onfi_param_page
,
};
src/drivers/mtd/nand/
nandchip-micron
.c
→
src/drivers/mtd/nand/
raw/nand_micron_mt29f
.c
View file @
1ac03d4a
...
@@ -48,7 +48,6 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
...
@@ -48,7 +48,6 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
mtd_oob_ops
ops
;
struct
mtd_oob_ops
ops
;
int
ret
;
int
ret
;
u8
get_feature
;
/* Valid pages in otp are 02h-1Fh. */
/* Valid pages in otp are 02h-1Fh. */
if
(
from
>
MICRON_NUM_OTP_PAGES
<<
chip
->
page_shift
)
if
(
from
>
MICRON_NUM_OTP_PAGES
<<
chip
->
page_shift
)
...
@@ -63,19 +62,18 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
...
@@ -63,19 +62,18 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
nand_get_device
(
mtd
,
FL_READING
);
nand_get_device
(
mtd
,
FL_READING
);
chip
->
select_chip
(
mtd
,
0
);
chip
->
select_chip
(
mtd
,
0
);
ret
=
chip
->
onfi_set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_OTP
);
ret
=
nand_set_features
(
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_OTP
);
ndelay
(
1000
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_GET_FEATURES
,
MICRON_SETFEATURE_ARRAYOP
,
-
1
);
get_feature
=
readb
(
chip
->
IO_ADDR_R
);
pr_debug
(
"Feature on: 0x%02x
\n
"
,
get_feature
);
if
(
ret
)
if
(
ret
)
goto
out
;
goto
out
;
ops
.
len
=
len
;
ops
.
len
=
len
;
ops
.
datbuf
=
buf
;
ops
.
datbuf
=
buf
;
ops
.
oobbuf
=
NULL
;
ops
.
oobbuf
=
NULL
;
ops
.
mode
=
0
;
// old, triggers chip->ecc.read_page() which enables/disables ondie ECC at each call
//ops.mode = 0;
// new, triggers chip->ecc.read_page_raw()
ops
.
mode
=
MTD_OPS_RAW
;
/*
/*
* XXX: some things in nand_do_read_ops might be wrong for OTP. e.g.
* XXX: some things in nand_do_read_ops might be wrong for OTP. e.g.
* chip->pagemask, chip->pagebuf handling, caching
* chip->pagemask, chip->pagebuf handling, caching
...
@@ -86,11 +84,7 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
...
@@ -86,11 +84,7 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
/* nand_do_read_ops deselects the chip so reselect here */
/* nand_do_read_ops deselects the chip so reselect here */
chip
->
select_chip
(
mtd
,
0
);
chip
->
select_chip
(
mtd
,
0
);
chip
->
onfi_set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_NORMAL
);
ret
=
nand_set_features
(
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_NORMAL
);
ndelay
(
1000
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_GET_FEATURES
,
MICRON_SETFEATURE_ARRAYOP
,
-
1
);
get_feature
=
readb
(
chip
->
IO_ADDR_R
);
pr_debug
(
"Feature off: 0x%02x
\n
"
,
get_feature
);
out:
out:
nand_release_device
(
mtd
);
nand_release_device
(
mtd
);
...
@@ -118,17 +112,19 @@ static int mt29f_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
...
@@ -118,17 +112,19 @@ static int mt29f_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
nand_get_device
(
mtd
,
FL_WRITING
);
nand_get_device
(
mtd
,
FL_WRITING
);
chip
->
select_chip
(
mtd
,
0
);
chip
->
select_chip
(
mtd
,
0
);
ret
=
nand_set_features
(
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_OTP
);
ret
=
chip
->
onfi_set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_OTP
);
if
(
ret
)
if
(
ret
)
goto
out
;
goto
out
;
ops
.
len
=
len
;
ops
.
len
=
len
;
ops
.
datbuf
=
buf
;
ops
.
datbuf
=
buf
;
ops
.
oobbuf
=
NULL
;
ops
.
oobbuf
=
NULL
;
ops
.
mode
=
0
;
// old
//ops.mode = 0;
// need raw mode
ops
.
mode
=
MTD_OPS_RAW
;
/*
/*
* some things in nand_do_write_ops might be wrong for OTP. e.g.
* some things in nand_do_write_ops might be wrong for OTP. e.g.
* chip->pagemask, chip->pagebuf handling
* chip->pagemask, chip->pagebuf handling
...
@@ -138,9 +134,7 @@ static int mt29f_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
...
@@ -138,9 +134,7 @@ static int mt29f_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
/* nand_do_write_ops deselects the chip so reselect here */
/* nand_do_write_ops deselects the chip so reselect here */
chip
->
select_chip
(
mtd
,
0
);
chip
->
select_chip
(
mtd
,
0
);
ret
=
nand_set_features
(
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_NORMAL
);
chip
->
onfi_set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_NORMAL
);
out:
out:
nand_release_device
(
mtd
);
nand_release_device
(
mtd
);
...
@@ -153,6 +147,7 @@ static int mt29f_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
...
@@ -153,6 +147,7 @@ static int mt29f_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
int
ret
;
int
ret
;
int
i
;
int
i
;
uint8_t
zerobuf
=
0
;
/* assert from and len are aligned */
/* assert from and len are aligned */
if
(
NOTALIGNED
(
from
)
||
NOTALIGNED
(
len
))
{
if
(
NOTALIGNED
(
from
)
||
NOTALIGNED
(
len
))
{
...
@@ -175,29 +170,36 @@ static int mt29f_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
...
@@ -175,29 +170,36 @@ static int mt29f_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
nand_get_device
(
mtd
,
FL_WRITING
);
nand_get_device
(
mtd
,
FL_WRITING
);
chip
->
select_chip
(
mtd
,
0
);
chip
->
select_chip
(
mtd
,
0
);
ret
=
chip
->
set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
ret
=
chip
->
onfi_set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_OTPPROTECT
);
MICRON_SETFEATURE_ARRAYOP_OTPPROTECT
);
if
(
ret
)
if
(
ret
)
goto
out
;
goto
out
;
// old
/*
for (i = 0; i < len << chip->page_shift; ++i) {
for (i = 0; i < len << chip->page_shift; ++i) {
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0,
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0,
(from << chip->page_shift) + i);
(from << chip->page_shift) + i);
chip->write_byte(mtd, 0);
chip->write_byte(mtd, 0);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
}
}
*/
chip
->
onfi_set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
// new
// TODO: Test this? Not critical. It's doubtful locking OTP will ever be needed.
for
(
i
=
0
;
i
<
len
<<
chip
->
page_shift
;
++
i
)
{
nand_prog_page_op
(
chip
,(
from
<<
chip
->
page_shift
)
+
i
,
0
,
&
zerobuf
,
1
);
}
chip
->
set_features
(
mtd
,
chip
,
MICRON_SETFEATURE_ARRAYOP
,
MICRON_SETFEATURE_ARRAYOP_NORMAL
);
MICRON_SETFEATURE_ARRAYOP_NORMAL
);
out:
out:
nand_release_device
(
mtd
);
nand_release_device
(
mtd
);
return
ret
;
return
ret
;
}
}
void
nand
chip_micron
_init
(
struct
mtd_info
*
mtd
,
int
dev_id
)
void
nand
_micron_mt29f
_init
(
struct
mtd_info
*
mtd
,
int
dev_id
)
{
{
/*
/*
* OTP is available on (at least) Micron's MT29F2G{08,16}AB[AB]EA,
* OTP is available on (at least) Micron's MT29F2G{08,16}AB[AB]EA,
...
@@ -209,9 +211,9 @@ void nandchip_micron_init(struct mtd_info *mtd, int dev_id)
...
@@ -209,9 +211,9 @@ void nandchip_micron_init(struct mtd_info *mtd, int dev_id)
if
(
IS_ENABLED
(
CONFIG_MTD_NAND_OTP
)
&&
if
(
IS_ENABLED
(
CONFIG_MTD_NAND_OTP
)
&&
((
dev_id
+
0x20
)
&
0xc0
)
==
0xc0
&&
((
dev_id
+
0x20
)
&
0xc0
)
==
0xc0
&&
((
dev_id
&
0x09
)
==
8
||
(
dev_id
&
0x0f
)
==
3
))
{
((
dev_id
&
0x09
)
==
8
||
(
dev_id
&
0x0f
)
==
3
))
{
mtd
->
_get_user_prot_info
=
mt29f_get_user_prot_info
;
mtd
->
_get_user_prot_info
=
mt29f_get_user_prot_info
;
mtd
->
_read_user_prot_reg
=
mt29f_read_user_prot_reg
;
mtd
->
_read_user_prot_reg
=
mt29f_read_user_prot_reg
;
mtd
->
_write_user_prot_reg
=
mt29f_write_user_prot_reg
;
mtd
->
_write_user_prot_reg
=
mt29f_write_user_prot_reg
;
mtd
->
_lock_user_prot_reg
=
mt29f_lock_user_prot_reg
;
mtd
->
_lock_user_prot_reg
=
mt29f_lock_user_prot_reg
;
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment