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
669ca34d
Commit
669ca34d
authored
Nov 22, 2017
by
Oleg Dzhimiev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
changes to nand_base
parent
e9a2179d
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1050 additions
and
436 deletions
+1050
-436
nand_base.c
src/drivers/mtd/nand/nand_base.c
+1050
-436
No files found.
src/drivers/mtd/nand/nand_base.c
View file @
669ca34d
/*
/*
* drivers/mtd/nand.c
*
* Overview:
* Overview:
* This is the generic MTD driver for NAND flash devices. It should be
* This is the generic MTD driver for NAND flash devices. It should be
* capable of working with almost all NAND chips currently available.
* capable of working with almost all NAND chips currently available.
...
@@ -39,77 +37,116 @@
...
@@ -39,77 +37,116 @@
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/nand_bch.h>
#include <linux/mtd/nand_bch.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/bitops.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
// Elphel
#include "nand.h"
#include "nand.h"
// Elphel: introduced before Rocko, used outside
// static int nand_get_device(struct mtd_info *mtd, int new_state);
int
nand_get_device
(
struct
mtd_info
*
mtd
,
int
new_state
);
static
int
nand_do_write_oob
(
struct
mtd_info
*
mtd
,
loff_t
to
,
struct
mtd_oob_ops
*
ops
);
/* Define default oob placement schemes for large and small page devices */
/* Define default oob placement schemes for large and small page devices */
static
struct
nand_ecclayout
nand_oob_8
=
{
static
int
nand_ooblayout_ecc_sp
(
struct
mtd_info
*
mtd
,
int
section
,
.
eccbytes
=
3
,
struct
mtd_oob_region
*
oobregion
)
.
eccpos
=
{
0
,
1
,
2
},
{
.
oobfree
=
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
{.
offset
=
3
,
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
.
length
=
2
},
{.
offset
=
6
,
.
length
=
2
}
}
};
static
struct
nand_ecclayout
nand_oob_16
=
{
if
(
section
>
1
)
.
eccbytes
=
6
,
return
-
ERANGE
;
.
eccpos
=
{
0
,
1
,
2
,
3
,
6
,
7
},
.
oobfree
=
{
{.
offset
=
8
,
.
length
=
8
}
}
};
static
struct
nand_ecclayout
nand_oob_64
=
{
if
(
!
section
)
{
.
eccbytes
=
24
,
oobregion
->
offset
=
0
;
.
eccpos
=
{
oobregion
->
length
=
4
;
40
,
41
,
42
,
43
,
44
,
45
,
46
,
47
,
}
else
{
48
,
49
,
50
,
51
,
52
,
53
,
54
,
55
,
oobregion
->
offset
=
6
;
56
,
57
,
58
,
59
,
60
,
61
,
62
,
63
},
oobregion
->
length
=
ecc
->
total
-
4
;
.
oobfree
=
{
}
{.
offset
=
2
,
.
length
=
38
}
}
return
0
;
};
}
static
int
nand_ooblayout_free_sp
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
if
(
section
>
1
)
return
-
ERANGE
;
if
(
mtd
->
oobsize
==
16
)
{
if
(
section
)
return
-
ERANGE
;
oobregion
->
length
=
8
;
oobregion
->
offset
=
8
;
}
else
{
oobregion
->
length
=
2
;
if
(
!
section
)
oobregion
->
offset
=
3
;
else
oobregion
->
offset
=
6
;
}
static
struct
nand_ecclayout
nand_oob_128
=
{
return
0
;
.
eccbytes
=
48
,
}
.
eccpos
=
{
80
,
81
,
82
,
83
,
84
,
85
,
86
,
87
,
const
struct
mtd_ooblayout_ops
nand_ooblayout_sp_ops
=
{
88
,
89
,
90
,
91
,
92
,
93
,
94
,
95
,
.
ecc
=
nand_ooblayout_ecc_sp
,
96
,
97
,
98
,
99
,
100
,
101
,
102
,
103
,
.
free
=
nand_ooblayout_free_sp
,
104
,
105
,
106
,
107
,
108
,
109
,
110
,
111
,
112
,
113
,
114
,
115
,
116
,
117
,
118
,
119
,
120
,
121
,
122
,
123
,
124
,
125
,
126
,
127
},
.
oobfree
=
{
{.
offset
=
2
,
.
length
=
78
}
}
};
};
EXPORT_SYMBOL_GPL
(
nand_ooblayout_sp_ops
);
int
nand_get_device
(
struct
mtd_info
*
mtd
,
int
new_state
);
static
int
nand_ooblayout_ecc_lp
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
static
int
nand_do_write_oob
(
struct
mtd_info
*
mtd
,
loff_t
to
,
if
(
section
)
struct
mtd_oob_ops
*
ops
)
;
return
-
ERANGE
;
/*
oobregion
->
length
=
ecc
->
total
;
* For devices which display every fart in the system on a separate LED. Is
oobregion
->
offset
=
mtd
->
oobsize
-
oobregion
->
length
;
* compiled away when LED support is disabled.
*/
return
0
;
DEFINE_LED_TRIGGER
(
nand_led_trigger
);
}
static
int
nand_ooblayout_free_lp
(
struct
mtd_info
*
mtd
,
int
section
,
struct
mtd_oob_region
*
oobregion
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
if
(
section
)
return
-
ERANGE
;
oobregion
->
length
=
mtd
->
oobsize
-
ecc
->
total
-
2
;
oobregion
->
offset
=
2
;
return
0
;
}
const
struct
mtd_ooblayout_ops
nand_ooblayout_lp_ops
=
{
.
ecc
=
nand_ooblayout_ecc_lp
,
.
free
=
nand_ooblayout_free_lp
,
};
EXPORT_SYMBOL_GPL
(
nand_ooblayout_lp_ops
);
static
int
check_offs_len
(
struct
mtd_info
*
mtd
,
static
int
check_offs_len
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
uint64_t
len
)
loff_t
ofs
,
uint64_t
len
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
int
ret
=
0
;
int
ret
=
0
;
/* Start address must align on block boundary */
/* Start address must align on block boundary */
...
@@ -133,9 +170,11 @@ static int check_offs_len(struct mtd_info *mtd,
...
@@ -133,9 +170,11 @@ static int check_offs_len(struct mtd_info *mtd,
*
*
* Release chip lock and wake up anyone waiting on the device.
* Release chip lock and wake up anyone waiting on the device.
*/
*/
//Elphel: this was introduced even earlier than Rocko - used outside
// static void nand_release_device(struct mtd_info *mtd)
void
nand_release_device
(
struct
mtd_info
*
mtd
)
void
nand_release_device
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
/* Release the controller and the chip */
/* Release the controller and the chip */
spin_lock
(
&
chip
->
controller
->
lock
);
spin_lock
(
&
chip
->
controller
->
lock
);
...
@@ -153,7 +192,7 @@ void nand_release_device(struct mtd_info *mtd)
...
@@ -153,7 +192,7 @@ void nand_release_device(struct mtd_info *mtd)
*/
*/
static
uint8_t
nand_read_byte
(
struct
mtd_info
*
mtd
)
static
uint8_t
nand_read_byte
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
return
readb
(
chip
->
IO_ADDR_R
);
return
readb
(
chip
->
IO_ADDR_R
);
}
}
...
@@ -166,7 +205,7 @@ static uint8_t nand_read_byte(struct mtd_info *mtd)
...
@@ -166,7 +205,7 @@ static uint8_t nand_read_byte(struct mtd_info *mtd)
*/
*/
static
uint8_t
nand_read_byte16
(
struct
mtd_info
*
mtd
)
static
uint8_t
nand_read_byte16
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
return
(
uint8_t
)
cpu_to_le16
(
readw
(
chip
->
IO_ADDR_R
));
return
(
uint8_t
)
cpu_to_le16
(
readw
(
chip
->
IO_ADDR_R
));
}
}
...
@@ -178,7 +217,7 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd)
...
@@ -178,7 +217,7 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd)
*/
*/
static
u16
nand_read_word
(
struct
mtd_info
*
mtd
)
static
u16
nand_read_word
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
return
readw
(
chip
->
IO_ADDR_R
);
return
readw
(
chip
->
IO_ADDR_R
);
}
}
...
@@ -191,7 +230,7 @@ static u16 nand_read_word(struct mtd_info *mtd)
...
@@ -191,7 +230,7 @@ static u16 nand_read_word(struct mtd_info *mtd)
*/
*/
static
void
nand_select_chip
(
struct
mtd_info
*
mtd
,
int
chipnr
)
static
void
nand_select_chip
(
struct
mtd_info
*
mtd
,
int
chipnr
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
switch
(
chipnr
)
{
switch
(
chipnr
)
{
case
-
1
:
case
-
1
:
...
@@ -214,7 +253,7 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
...
@@ -214,7 +253,7 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
*/
*/
static
void
nand_write_byte
(
struct
mtd_info
*
mtd
,
uint8_t
byte
)
static
void
nand_write_byte
(
struct
mtd_info
*
mtd
,
uint8_t
byte
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
chip
->
write_buf
(
mtd
,
&
byte
,
1
);
chip
->
write_buf
(
mtd
,
&
byte
,
1
);
}
}
...
@@ -228,7 +267,7 @@ static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
...
@@ -228,7 +267,7 @@ static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
*/
*/
static
void
nand_write_byte16
(
struct
mtd_info
*
mtd
,
uint8_t
byte
)
static
void
nand_write_byte16
(
struct
mtd_info
*
mtd
,
uint8_t
byte
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
uint16_t
word
=
byte
;
uint16_t
word
=
byte
;
/*
/*
...
@@ -260,7 +299,7 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
...
@@ -260,7 +299,7 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
*/
*/
static
void
nand_write_buf
(
struct
mtd_info
*
mtd
,
const
uint8_t
*
buf
,
int
len
)
static
void
nand_write_buf
(
struct
mtd_info
*
mtd
,
const
uint8_t
*
buf
,
int
len
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
iowrite8_rep
(
chip
->
IO_ADDR_W
,
buf
,
len
);
iowrite8_rep
(
chip
->
IO_ADDR_W
,
buf
,
len
);
}
}
...
@@ -275,7 +314,7 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
...
@@ -275,7 +314,7 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
*/
*/
static
void
nand_read_buf
(
struct
mtd_info
*
mtd
,
uint8_t
*
buf
,
int
len
)
static
void
nand_read_buf
(
struct
mtd_info
*
mtd
,
uint8_t
*
buf
,
int
len
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
ioread8_rep
(
chip
->
IO_ADDR_R
,
buf
,
len
);
ioread8_rep
(
chip
->
IO_ADDR_R
,
buf
,
len
);
}
}
...
@@ -290,7 +329,7 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
...
@@ -290,7 +329,7 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
*/
*/
static
void
nand_write_buf16
(
struct
mtd_info
*
mtd
,
const
uint8_t
*
buf
,
int
len
)
static
void
nand_write_buf16
(
struct
mtd_info
*
mtd
,
const
uint8_t
*
buf
,
int
len
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
u16
*
p
=
(
u16
*
)
buf
;
u16
*
p
=
(
u16
*
)
buf
;
iowrite16_rep
(
chip
->
IO_ADDR_W
,
p
,
len
>>
1
);
iowrite16_rep
(
chip
->
IO_ADDR_W
,
p
,
len
>>
1
);
...
@@ -306,7 +345,7 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
...
@@ -306,7 +345,7 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
*/
*/
static
void
nand_read_buf16
(
struct
mtd_info
*
mtd
,
uint8_t
*
buf
,
int
len
)
static
void
nand_read_buf16
(
struct
mtd_info
*
mtd
,
uint8_t
*
buf
,
int
len
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
u16
*
p
=
(
u16
*
)
buf
;
u16
*
p
=
(
u16
*
)
buf
;
ioread16_rep
(
chip
->
IO_ADDR_R
,
p
,
len
>>
1
);
ioread16_rep
(
chip
->
IO_ADDR_R
,
p
,
len
>>
1
);
...
@@ -316,14 +355,13 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
...
@@ -316,14 +355,13 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
* @mtd: MTD device structure
* @mtd: MTD device structure
* @ofs: offset from device start
* @ofs: offset from device start
* @getchip: 0, if the chip is already selected
*
*
* Check, if the block is bad.
* Check, if the block is bad.
*/
*/
static
int
nand_block_bad
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
int
getchip
)
static
int
nand_block_bad
(
struct
mtd_info
*
mtd
,
loff_t
ofs
)
{
{
int
page
,
chipnr
,
res
=
0
,
i
=
0
;
int
page
,
res
=
0
,
i
=
0
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
u16
bad
;
u16
bad
;
if
(
chip
->
bbt_options
&
NAND_BBT_SCANLASTPAGE
)
if
(
chip
->
bbt_options
&
NAND_BBT_SCANLASTPAGE
)
...
@@ -331,15 +369,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
...
@@ -331,15 +369,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
page
=
(
int
)(
ofs
>>
chip
->
page_shift
)
&
chip
->
pagemask
;
page
=
(
int
)(
ofs
>>
chip
->
page_shift
)
&
chip
->
pagemask
;
if
(
getchip
)
{
chipnr
=
(
int
)(
ofs
>>
chip
->
chip_shift
);
nand_get_device
(
mtd
,
FL_READING
);
/* Select the NAND device */
chip
->
select_chip
(
mtd
,
chipnr
);
}
do
{
do
{
if
(
chip
->
options
&
NAND_BUSWIDTH_16
)
{
if
(
chip
->
options
&
NAND_BUSWIDTH_16
)
{
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READOOB
,
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READOOB
,
...
@@ -364,11 +393,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
...
@@ -364,11 +393,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
i
++
;
i
++
;
}
while
(
!
res
&&
i
<
2
&&
(
chip
->
bbt_options
&
NAND_BBT_SCAN2NDPAGE
));
}
while
(
!
res
&&
i
<
2
&&
(
chip
->
bbt_options
&
NAND_BBT_SCAN2NDPAGE
));
if
(
getchip
)
{
chip
->
select_chip
(
mtd
,
-
1
);
nand_release_device
(
mtd
);
}
return
res
;
return
res
;
}
}
...
@@ -383,12 +407,12 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
...
@@ -383,12 +407,12 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
*/
*/
static
int
nand_default_block_markbad
(
struct
mtd_info
*
mtd
,
loff_t
ofs
)
static
int
nand_default_block_markbad
(
struct
mtd_info
*
mtd
,
loff_t
ofs
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
struct
mtd_oob_ops
ops
;
struct
mtd_oob_ops
ops
;
uint8_t
buf
[
2
]
=
{
0
,
0
};
uint8_t
buf
[
2
]
=
{
0
,
0
};
int
ret
=
0
,
res
,
i
=
0
;
int
ret
=
0
,
res
,
i
=
0
;
ops
.
datbuf
=
NULL
;
memset
(
&
ops
,
0
,
sizeof
(
ops
))
;
ops
.
oobbuf
=
buf
;
ops
.
oobbuf
=
buf
;
ops
.
ooboffs
=
chip
->
badblockpos
;
ops
.
ooboffs
=
chip
->
badblockpos
;
if
(
chip
->
options
&
NAND_BUSWIDTH_16
)
{
if
(
chip
->
options
&
NAND_BUSWIDTH_16
)
{
...
@@ -433,7 +457,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
...
@@ -433,7 +457,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
*/
*/
static
int
nand_block_markbad_lowlevel
(
struct
mtd_info
*
mtd
,
loff_t
ofs
)
static
int
nand_block_markbad_lowlevel
(
struct
mtd_info
*
mtd
,
loff_t
ofs
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
int
res
,
ret
=
0
;
int
res
,
ret
=
0
;
if
(
!
(
chip
->
bbt_options
&
NAND_BBT_NO_OOB_BBM
))
{
if
(
!
(
chip
->
bbt_options
&
NAND_BBT_NO_OOB_BBM
))
{
...
@@ -474,7 +498,7 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
...
@@ -474,7 +498,7 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
*/
*/
static
int
nand_check_wp
(
struct
mtd_info
*
mtd
)
static
int
nand_check_wp
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
/* Broken xD cards report WP despite being writable */
/* Broken xD cards report WP despite being writable */
if
(
chip
->
options
&
NAND_BROKEN_XD
)
if
(
chip
->
options
&
NAND_BROKEN_XD
)
...
@@ -494,7 +518,7 @@ static int nand_check_wp(struct mtd_info *mtd)
...
@@ -494,7 +518,7 @@ static int nand_check_wp(struct mtd_info *mtd)
*/
*/
static
int
nand_block_isreserved
(
struct
mtd_info
*
mtd
,
loff_t
ofs
)
static
int
nand_block_isreserved
(
struct
mtd_info
*
mtd
,
loff_t
ofs
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
if
(
!
chip
->
bbt
)
if
(
!
chip
->
bbt
)
return
0
;
return
0
;
...
@@ -506,19 +530,17 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
...
@@ -506,19 +530,17 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
* @mtd: MTD device structure
* @mtd: MTD device structure
* @ofs: offset from device start
* @ofs: offset from device start
* @getchip: 0, if the chip is already selected
* @allowbbt: 1, if its allowed to access the bbt area
* @allowbbt: 1, if its allowed to access the bbt area
*
*
* Check, if the block is bad. Either by reading the bad block table or
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
* calling of the scan function.
*/
*/
static
int
nand_block_checkbad
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
int
getchip
,
static
int
nand_block_checkbad
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
int
allowbbt
)
int
allowbbt
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
if
(
!
chip
->
bbt
)
if
(
!
chip
->
bbt
)
return
chip
->
block_bad
(
mtd
,
ofs
,
getchip
);
return
chip
->
block_bad
(
mtd
,
ofs
);
/* Return info from the table */
/* Return info from the table */
return
nand_isbad_bbt
(
mtd
,
ofs
,
allowbbt
);
return
nand_isbad_bbt
(
mtd
,
ofs
,
allowbbt
);
...
@@ -534,7 +556,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
...
@@ -534,7 +556,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
*/
*/
static
void
panic_nand_wait_ready
(
struct
mtd_info
*
mtd
,
unsigned
long
timeo
)
static
void
panic_nand_wait_ready
(
struct
mtd_info
*
mtd
,
unsigned
long
timeo
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
int
i
;
int
i
;
/* Wait for the device to get ready */
/* Wait for the device to get ready */
...
@@ -546,27 +568,52 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
...
@@ -546,27 +568,52 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
}
}
}
}
/* Wait for the ready pin, after a command. The timeout is caught later. */
/**
* nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
* @mtd: MTD device structure
*
* Wait for the ready pin after a command, and warn if a timeout occurs.
*/
void
nand_wait_ready
(
struct
mtd_info
*
mtd
)
void
nand_wait_ready
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
unsigned
long
timeo
=
jiffies
+
msecs_to_jiffies
(
20
)
;
unsigned
long
timeo
=
400
;
/* 400ms timeout */
if
(
in_interrupt
()
||
oops_in_progress
)
if
(
in_interrupt
()
||
oops_in_progress
)
return
panic_nand_wait_ready
(
mtd
,
400
);
return
panic_nand_wait_ready
(
mtd
,
timeo
);
led_trigger_event
(
nand_led_trigger
,
LED_FULL
);
/* Wait until command is processed or timeout occurs */
/* Wait until command is processed or timeout occurs */
timeo
=
jiffies
+
msecs_to_jiffies
(
timeo
);
do
{
do
{
if
(
chip
->
dev_ready
(
mtd
))
if
(
chip
->
dev_ready
(
mtd
))
break
;
return
;
touch_softlockup_watchdog
();
cond_resched
();
}
while
(
time_before
(
jiffies
,
timeo
));
}
while
(
time_before
(
jiffies
,
timeo
));
led_trigger_event
(
nand_led_trigger
,
LED_OFF
);
if
(
!
chip
->
dev_ready
(
mtd
))
pr_warn_ratelimited
(
"timeout while waiting for chip to become ready
\n
"
);
}
}
EXPORT_SYMBOL_GPL
(
nand_wait_ready
);
EXPORT_SYMBOL_GPL
(
nand_wait_ready
);
/**
* nand_wait_status_ready - [GENERIC] Wait for the ready status after commands.
* @mtd: MTD device structure
* @timeo: Timeout in ms
*
* Wait for status ready (i.e. command done) or timeout.
*/
static
void
nand_wait_status_ready
(
struct
mtd_info
*
mtd
,
unsigned
long
timeo
)
{
register
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
timeo
=
jiffies
+
msecs_to_jiffies
(
timeo
);
do
{
if
((
chip
->
read_byte
(
mtd
)
&
NAND_STATUS_READY
))
break
;
touch_softlockup_watchdog
();
}
while
(
time_before
(
jiffies
,
timeo
));
};
/**
/**
* nand_command - [DEFAULT] Send command to NAND device
* nand_command - [DEFAULT] Send command to NAND device
* @mtd: MTD device structure
* @mtd: MTD device structure
...
@@ -580,7 +627,7 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);
...
@@ -580,7 +627,7 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);
static
void
nand_command
(
struct
mtd_info
*
mtd
,
unsigned
int
command
,
static
void
nand_command
(
struct
mtd_info
*
mtd
,
unsigned
int
command
,
int
column
,
int
page_addr
)
int
column
,
int
page_addr
)
{
{
register
struct
nand_chip
*
chip
=
mtd
->
priv
;
register
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
int
ctrl
=
NAND_CTRL_CLE
|
NAND_CTRL_CHANGE
;
int
ctrl
=
NAND_CTRL_CLE
|
NAND_CTRL_CHANGE
;
/* Write out the command to the device */
/* Write out the command to the device */
...
@@ -645,8 +692,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
...
@@ -645,8 +692,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
NAND_CTRL_CLE
|
NAND_CTRL_CHANGE
);
NAND_CTRL_CLE
|
NAND_CTRL_CHANGE
);
chip
->
cmd_ctrl
(
mtd
,
chip
->
cmd_ctrl
(
mtd
,
NAND_CMD_NONE
,
NAND_NCE
|
NAND_CTRL_CHANGE
);
NAND_CMD_NONE
,
NAND_NCE
|
NAND_CTRL_CHANGE
);
while
(
!
(
chip
->
read_byte
(
mtd
)
&
NAND_STATUS_READY
))
/* EZ-NAND can take upto 250ms as per ONFi v4.0 */
;
nand_wait_status_ready
(
mtd
,
250
)
;
return
;
return
;
/* This applies to read commands */
/* This applies to read commands */
...
@@ -683,7 +730,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
...
@@ -683,7 +730,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
static
void
nand_command_lp
(
struct
mtd_info
*
mtd
,
unsigned
int
command
,
static
void
nand_command_lp
(
struct
mtd_info
*
mtd
,
unsigned
int
command
,
int
column
,
int
page_addr
)
int
column
,
int
page_addr
)
{
{
register
struct
nand_chip
*
chip
=
mtd
->
priv
;
register
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
/* Emulate NAND_CMD_READOOB */
/* Emulate NAND_CMD_READOOB */
if
(
command
==
NAND_CMD_READOOB
)
{
if
(
command
==
NAND_CMD_READOOB
)
{
...
@@ -705,7 +752,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
...
@@ -705,7 +752,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
column
>>=
1
;
column
>>=
1
;
chip
->
cmd_ctrl
(
mtd
,
column
,
ctrl
);
chip
->
cmd_ctrl
(
mtd
,
column
,
ctrl
);
ctrl
&=
~
NAND_CTRL_CHANGE
;
ctrl
&=
~
NAND_CTRL_CHANGE
;
chip
->
cmd_ctrl
(
mtd
,
column
>>
8
,
ctrl
);
/* Only output a single addr cycle for 8bits opcodes. */
if
(
!
nand_opcode_8bits
(
command
))
chip
->
cmd_ctrl
(
mtd
,
column
>>
8
,
ctrl
);
}
}
if
(
page_addr
!=
-
1
)
{
if
(
page_addr
!=
-
1
)
{
chip
->
cmd_ctrl
(
mtd
,
page_addr
,
ctrl
);
chip
->
cmd_ctrl
(
mtd
,
page_addr
,
ctrl
);
...
@@ -742,8 +792,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
...
@@ -742,8 +792,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
NAND_NCE
|
NAND_CLE
|
NAND_CTRL_CHANGE
);
NAND_NCE
|
NAND_CLE
|
NAND_CTRL_CHANGE
);
chip
->
cmd_ctrl
(
mtd
,
NAND_CMD_NONE
,
chip
->
cmd_ctrl
(
mtd
,
NAND_CMD_NONE
,
NAND_NCE
|
NAND_CTRL_CHANGE
);
NAND_NCE
|
NAND_CTRL_CHANGE
);
while
(
!
(
chip
->
read_byte
(
mtd
)
&
NAND_STATUS_READY
))
/* EZ-NAND can take upto 250ms as per ONFi v4.0 */
;
nand_wait_status_ready
(
mtd
,
250
)
;
return
;
return
;
case
NAND_CMD_RNDOUT
:
case
NAND_CMD_RNDOUT
:
...
@@ -804,9 +854,12 @@ static void panic_nand_get_device(struct nand_chip *chip,
...
@@ -804,9 +854,12 @@ static void panic_nand_get_device(struct nand_chip *chip,
*
*
* Get the device and lock it for exclusive access
* Get the device and lock it for exclusive access
*/
*/
int
nand_get_device
(
struct
mtd_info
*
mtd
,
int
new_state
)
// Elphel:
//static int
int
nand_get_device
(
struct
mtd_info
*
mtd
,
int
new_state
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
spinlock_t
*
lock
=
&
chip
->
controller
->
lock
;
spinlock_t
*
lock
=
&
chip
->
controller
->
lock
;
wait_queue_head_t
*
wq
=
&
chip
->
controller
->
wq
;
wait_queue_head_t
*
wq
=
&
chip
->
controller
->
wq
;
DECLARE_WAITQUEUE
(
wait
,
current
);
DECLARE_WAITQUEUE
(
wait
,
current
);
...
@@ -868,17 +921,13 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -868,17 +921,13 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
* @mtd: MTD device structure
* @mtd: MTD device structure
* @chip: NAND chip structure
* @chip: NAND chip structure
*
*
* Wait for command done. This applies to erase and program only. Erase can
* Wait for command done. This applies to erase and program only.
* take up to 400ms and program up to 20ms according to general NAND and
* SmartMedia specs.
*/
*/
static
int
nand_wait
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
)
static
int
nand_wait
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
)
{
{
int
status
,
state
=
chip
->
state
;
int
status
;
unsigned
long
timeo
=
(
state
==
FL_ERASING
?
400
:
20
);
unsigned
long
timeo
=
400
;
led_trigger_event
(
nand_led_trigger
,
LED_FULL
);
/*
/*
* Apply this short delay always to ensure that we do wait tWB in any
* Apply this short delay always to ensure that we do wait tWB in any
...
@@ -892,7 +941,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
...
@@ -892,7 +941,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
panic_nand_wait
(
mtd
,
chip
,
timeo
);
panic_nand_wait
(
mtd
,
chip
,
timeo
);
else
{
else
{
timeo
=
jiffies
+
msecs_to_jiffies
(
timeo
);
timeo
=
jiffies
+
msecs_to_jiffies
(
timeo
);
while
(
time_before
(
jiffies
,
timeo
))
{
do
{
if
(
chip
->
dev_ready
)
{
if
(
chip
->
dev_ready
)
{
if
(
chip
->
dev_ready
(
mtd
))
if
(
chip
->
dev_ready
(
mtd
))
break
;
break
;
...
@@ -901,9 +950,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
...
@@ -901,9 +950,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
break
;
break
;
}
}
cond_resched
();
cond_resched
();
}
}
while
(
time_before
(
jiffies
,
timeo
));
}
}
led_trigger_event
(
nand_led_trigger
,
LED_OFF
);
status
=
(
int
)
chip
->
read_byte
(
mtd
);
status
=
(
int
)
chip
->
read_byte
(
mtd
);
/* This can happen if in case of timeout or buggy dev_ready */
/* This can happen if in case of timeout or buggy dev_ready */
...
@@ -911,6 +959,181 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
...
@@ -911,6 +959,181 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
return
status
;
return
status
;
}
}
/**
* nand_reset_data_interface - Reset data interface and timings
* @chip: The NAND chip
*
* Reset the Data interface and timings to ONFI mode 0.
*
* Returns 0 for success or negative error code otherwise.
*/
static
int
nand_reset_data_interface
(
struct
nand_chip
*
chip
)
{
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
const
struct
nand_data_interface
*
conf
;
int
ret
;
if
(
!
chip
->
setup_data_interface
)
return
0
;
/*
* The ONFI specification says:
* "
* To transition from NV-DDR or NV-DDR2 to the SDR data
* interface, the host shall use the Reset (FFh) command
* using SDR timing mode 0. A device in any timing mode is
* required to recognize Reset (FFh) command issued in SDR
* timing mode 0.
* "
*
* Configure the data interface in SDR mode and set the
* timings to timing mode 0.
*/
conf
=
nand_get_default_data_interface
();
ret
=
chip
->
setup_data_interface
(
mtd
,
conf
,
false
);
if
(
ret
)
pr_err
(
"Failed to configure data interface to SDR timing mode 0
\n
"
);
return
ret
;
}
/**
* nand_setup_data_interface - Setup the best data interface and timings
* @chip: The NAND chip
*
* Find and configure the best data interface and NAND timings supported by
* the chip and the driver.
* First tries to retrieve supported timing modes from ONFI information,
* and if the NAND chip does not support ONFI, relies on the
* ->onfi_timing_mode_default specified in the nand_ids table.
*
* Returns 0 for success or negative error code otherwise.
*/
static
int
nand_setup_data_interface
(
struct
nand_chip
*
chip
)
{
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
int
ret
;
if
(
!
chip
->
setup_data_interface
||
!
chip
->
data_interface
)
return
0
;
/*
* Ensure the timing mode has been changed on the chip side
* before changing timings on the controller side.
*/
if
(
chip
->
onfi_version
)
{
u8
tmode_param
[
ONFI_SUBFEATURE_PARAM_LEN
]
=
{
chip
->
onfi_timing_mode_default
,
};
ret
=
chip
->
onfi_set_features
(
mtd
,
chip
,
ONFI_FEATURE_ADDR_TIMING_MODE
,
tmode_param
);
if
(
ret
)
goto
err
;
}
ret
=
chip
->
setup_data_interface
(
mtd
,
chip
->
data_interface
,
false
);
err:
return
ret
;
}
/**
* nand_init_data_interface - find the best data interface and timings
* @chip: The NAND chip
*
* Find the best data interface and NAND timings supported by the chip
* and the driver.
* First tries to retrieve supported timing modes from ONFI information,
* and if the NAND chip does not support ONFI, relies on the
* ->onfi_timing_mode_default specified in the nand_ids table. After this
* function nand_chip->data_interface is initialized with the best timing mode
* available.
*
* Returns 0 for success or negative error code otherwise.
*/
static
int
nand_init_data_interface
(
struct
nand_chip
*
chip
)
{
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
int
modes
,
mode
,
ret
;
if
(
!
chip
->
setup_data_interface
)
return
0
;
/*
* First try to identify the best timings from ONFI parameters and
* if the NAND does not support ONFI, fallback to the default ONFI
* timing mode.
*/
modes
=
onfi_get_async_timing_mode
(
chip
);
if
(
modes
==
ONFI_TIMING_MODE_UNKNOWN
)
{
if
(
!
chip
->
onfi_timing_mode_default
)
return
0
;
modes
=
GENMASK
(
chip
->
onfi_timing_mode_default
,
0
);
}
chip
->
data_interface
=
kzalloc
(
sizeof
(
*
chip
->
data_interface
),
GFP_KERNEL
);
if
(
!
chip
->
data_interface
)
return
-
ENOMEM
;
for
(
mode
=
fls
(
modes
)
-
1
;
mode
>=
0
;
mode
--
)
{
ret
=
onfi_init_data_interface
(
chip
,
chip
->
data_interface
,
NAND_SDR_IFACE
,
mode
);
if
(
ret
)
continue
;
ret
=
chip
->
setup_data_interface
(
mtd
,
chip
->
data_interface
,
true
);
if
(
!
ret
)
{
chip
->
onfi_timing_mode_default
=
mode
;
break
;
}
}
return
0
;
}
static
void
nand_release_data_interface
(
struct
nand_chip
*
chip
)
{
kfree
(
chip
->
data_interface
);
}
/**
* nand_reset - Reset and initialize a NAND device
* @chip: The NAND chip
* @chipnr: Internal die id
*
* Returns 0 for success or negative error code otherwise
*/
int
nand_reset
(
struct
nand_chip
*
chip
,
int
chipnr
)
{
struct
mtd_info
*
mtd
=
nand_to_mtd
(
chip
);
int
ret
;
ret
=
nand_reset_data_interface
(
chip
);
if
(
ret
)
return
ret
;
/*
* The CS line has to be released before we can apply the new NAND
* interface settings, hence this weird ->select_chip() dance.
*/
chip
->
select_chip
(
mtd
,
chipnr
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
chip
->
select_chip
(
mtd
,
-
1
);
chip
->
select_chip
(
mtd
,
chipnr
);
ret
=
nand_setup_data_interface
(
chip
);
chip
->
select_chip
(
mtd
,
-
1
);
if
(
ret
)
return
ret
;
return
0
;
}
/**
/**
* __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
* __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
* @mtd: mtd info
* @mtd: mtd info
...
@@ -928,7 +1151,7 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
...
@@ -928,7 +1151,7 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
{
{
int
ret
=
0
;
int
ret
=
0
;
int
status
,
page
;
int
status
,
page
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
/* Submit address of first page to unlock */
/* Submit address of first page to unlock */
page
=
ofs
>>
chip
->
page_shift
;
page
=
ofs
>>
chip
->
page_shift
;
...
@@ -963,13 +1186,13 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
...
@@ -963,13 +1186,13 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
{
int
ret
=
0
;
int
ret
=
0
;
int
chipnr
;
int
chipnr
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
__func__
,
(
unsigned
long
long
)
ofs
,
len
);
__func__
,
(
unsigned
long
long
)
ofs
,
len
);
if
(
check_offs_len
(
mtd
,
ofs
,
len
))
if
(
check_offs_len
(
mtd
,
ofs
,
len
))
ret
=
-
EINVAL
;
ret
urn
-
EINVAL
;
/* Align to last block address if size addresses end of the device */
/* Align to last block address if size addresses end of the device */
if
(
ofs
+
len
==
mtd
->
size
)
if
(
ofs
+
len
==
mtd
->
size
)
...
@@ -980,8 +1203,6 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
...
@@ -980,8 +1203,6 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
/* Shift to get chip number */
/* Shift to get chip number */
chipnr
=
ofs
>>
chip
->
chip_shift
;
chipnr
=
ofs
>>
chip
->
chip_shift
;
chip
->
select_chip
(
mtd
,
chipnr
);
/*
/*
* Reset the chip.
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* If we want to check the WP through READ STATUS and check the bit 7
...
@@ -989,7 +1210,9 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
...
@@ -989,7 +1210,9 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
* some operation can also clear the bit 7 of status register
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
* eg. erase/program a locked block
*/
*/
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
nand_reset
(
chip
,
chipnr
);
chip
->
select_chip
(
mtd
,
chipnr
);
/* Check, if it is write protected */
/* Check, if it is write protected */
if
(
nand_check_wp
(
mtd
))
{
if
(
nand_check_wp
(
mtd
))
{
...
@@ -1026,21 +1249,19 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
...
@@ -1026,21 +1249,19 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
{
int
ret
=
0
;
int
ret
=
0
;
int
chipnr
,
status
,
page
;
int
chipnr
,
status
,
page
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
__func__
,
(
unsigned
long
long
)
ofs
,
len
);
__func__
,
(
unsigned
long
long
)
ofs
,
len
);
if
(
check_offs_len
(
mtd
,
ofs
,
len
))
if
(
check_offs_len
(
mtd
,
ofs
,
len
))
ret
=
-
EINVAL
;
ret
urn
-
EINVAL
;
nand_get_device
(
mtd
,
FL_LOCKING
);
nand_get_device
(
mtd
,
FL_LOCKING
);
/* Shift to get chip number */
/* Shift to get chip number */
chipnr
=
ofs
>>
chip
->
chip_shift
;
chipnr
=
ofs
>>
chip
->
chip_shift
;
chip
->
select_chip
(
mtd
,
chipnr
);
/*
/*
* Reset the chip.
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* If we want to check the WP through READ STATUS and check the bit 7
...
@@ -1048,7 +1269,9 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
...
@@ -1048,7 +1269,9 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
* some operation can also clear the bit 7 of status register
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
* eg. erase/program a locked block
*/
*/
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
nand_reset
(
chip
,
chipnr
);
chip
->
select_chip
(
mtd
,
chipnr
);
/* Check, if it is write protected */
/* Check, if it is write protected */
if
(
nand_check_wp
(
mtd
))
{
if
(
nand_check_wp
(
mtd
))
{
...
@@ -1083,6 +1306,134 @@ out:
...
@@ -1083,6 +1306,134 @@ out:
}
}
EXPORT_SYMBOL
(
nand_lock
);
EXPORT_SYMBOL
(
nand_lock
);
/**
* nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
* @buf: buffer to test
* @len: buffer length
* @bitflips_threshold: maximum number of bitflips
*
* Check if a buffer contains only 0xff, which means the underlying region
* has been erased and is ready to be programmed.
* The bitflips_threshold specify the maximum number of bitflips before
* considering the region is not erased.
* Note: The logic of this function has been extracted from the memweight
* implementation, except that nand_check_erased_buf function exit before
* testing the whole buffer if the number of bitflips exceed the
* bitflips_threshold value.
*
* Returns a positive number of bitflips less than or equal to
* bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
* threshold.
*/
static
int
nand_check_erased_buf
(
void
*
buf
,
int
len
,
int
bitflips_threshold
)
{
const
unsigned
char
*
bitmap
=
buf
;
int
bitflips
=
0
;
int
weight
;
for
(;
len
&&
((
uintptr_t
)
bitmap
)
%
sizeof
(
long
);
len
--
,
bitmap
++
)
{
weight
=
hweight8
(
*
bitmap
);
bitflips
+=
BITS_PER_BYTE
-
weight
;
if
(
unlikely
(
bitflips
>
bitflips_threshold
))
return
-
EBADMSG
;
}
for
(;
len
>=
sizeof
(
long
);
len
-=
sizeof
(
long
),
bitmap
+=
sizeof
(
long
))
{
weight
=
hweight_long
(
*
((
unsigned
long
*
)
bitmap
));
bitflips
+=
BITS_PER_LONG
-
weight
;
if
(
unlikely
(
bitflips
>
bitflips_threshold
))
return
-
EBADMSG
;
}
for
(;
len
>
0
;
len
--
,
bitmap
++
)
{
weight
=
hweight8
(
*
bitmap
);
bitflips
+=
BITS_PER_BYTE
-
weight
;
if
(
unlikely
(
bitflips
>
bitflips_threshold
))
return
-
EBADMSG
;
}
return
bitflips
;
}
/**
* nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
* 0xff data
* @data: data buffer to test
* @datalen: data length
* @ecc: ECC buffer
* @ecclen: ECC length
* @extraoob: extra OOB buffer
* @extraooblen: extra OOB length
* @bitflips_threshold: maximum number of bitflips
*
* Check if a data buffer and its associated ECC and OOB data contains only
* 0xff pattern, which means the underlying region has been erased and is
* ready to be programmed.
* The bitflips_threshold specify the maximum number of bitflips before
* considering the region as not erased.
*
* Note:
* 1/ ECC algorithms are working on pre-defined block sizes which are usually
* different from the NAND page size. When fixing bitflips, ECC engines will
* report the number of errors per chunk, and the NAND core infrastructure
* expect you to return the maximum number of bitflips for the whole page.
* This is why you should always use this function on a single chunk and
* not on the whole page. After checking each chunk you should update your
* max_bitflips value accordingly.
* 2/ When checking for bitflips in erased pages you should not only check
* the payload data but also their associated ECC data, because a user might
* have programmed almost all bits to 1 but a few. In this case, we
* shouldn't consider the chunk as erased, and checking ECC bytes prevent
* this case.
* 3/ The extraoob argument is optional, and should be used if some of your OOB
* data are protected by the ECC engine.
* It could also be used if you support subpages and want to attach some
* extra OOB data to an ECC chunk.
*
* Returns a positive number of bitflips less than or equal to
* bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
* threshold. In case of success, the passed buffers are filled with 0xff.
*/
int
nand_check_erased_ecc_chunk
(
void
*
data
,
int
datalen
,
void
*
ecc
,
int
ecclen
,
void
*
extraoob
,
int
extraooblen
,
int
bitflips_threshold
)
{
int
data_bitflips
=
0
,
ecc_bitflips
=
0
,
extraoob_bitflips
=
0
;
data_bitflips
=
nand_check_erased_buf
(
data
,
datalen
,
bitflips_threshold
);
if
(
data_bitflips
<
0
)
return
data_bitflips
;
bitflips_threshold
-=
data_bitflips
;
ecc_bitflips
=
nand_check_erased_buf
(
ecc
,
ecclen
,
bitflips_threshold
);
if
(
ecc_bitflips
<
0
)
return
ecc_bitflips
;
bitflips_threshold
-=
ecc_bitflips
;
extraoob_bitflips
=
nand_check_erased_buf
(
extraoob
,
extraooblen
,
bitflips_threshold
);
if
(
extraoob_bitflips
<
0
)
return
extraoob_bitflips
;
if
(
data_bitflips
)
memset
(
data
,
0xff
,
datalen
);
if
(
ecc_bitflips
)
memset
(
ecc
,
0xff
,
ecclen
);
if
(
extraoob_bitflips
)
memset
(
extraoob
,
0xff
,
extraooblen
);
return
data_bitflips
+
ecc_bitflips
+
extraoob_bitflips
;
}
EXPORT_SYMBOL
(
nand_check_erased_ecc_chunk
);
/**
/**
* nand_read_page_raw - [INTERN] read raw page data without ecc
* nand_read_page_raw - [INTERN] read raw page data without ecc
* @mtd: mtd info structure
* @mtd: mtd info structure
...
@@ -1157,13 +1508,12 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
...
@@ -1157,13 +1508,12 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
static
int
nand_read_page_swecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
static
int
nand_read_page_swecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
i
,
eccsize
=
chip
->
ecc
.
size
,
ret
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
p
=
buf
;
uint8_t
*
p
=
buf
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
uint32_t
*
eccpos
=
chip
->
ecc
.
layout
->
eccpos
;
unsigned
int
max_bitflips
=
0
;
unsigned
int
max_bitflips
=
0
;
chip
->
ecc
.
read_page_raw
(
mtd
,
chip
,
buf
,
1
,
page
);
chip
->
ecc
.
read_page_raw
(
mtd
,
chip
,
buf
,
1
,
page
);
...
@@ -1171,8 +1521,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1171,8 +1521,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
for
(
i
=
0
;
i
<
chip
->
ecc
.
total
;
i
++
)
ret
=
mtd_ooblayout_get_eccbytes
(
mtd
,
ecc_code
,
chip
->
oob_poi
,
0
,
ecc_code
[
i
]
=
chip
->
oob_poi
[
eccpos
[
i
]];
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
eccsteps
=
chip
->
ecc
.
steps
;
eccsteps
=
chip
->
ecc
.
steps
;
p
=
buf
;
p
=
buf
;
...
@@ -1204,14 +1556,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1204,14 +1556,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t
data_offs
,
uint32_t
readlen
,
uint8_t
*
bufpoi
,
uint32_t
data_offs
,
uint32_t
readlen
,
uint8_t
*
bufpoi
,
int
page
)
int
page
)
{
{
int
start_step
,
end_step
,
num_steps
;
int
start_step
,
end_step
,
num_steps
,
ret
;
uint32_t
*
eccpos
=
chip
->
ecc
.
layout
->
eccpos
;
uint8_t
*
p
;
uint8_t
*
p
;
int
data_col_addr
,
i
,
gaps
=
0
;
int
data_col_addr
,
i
,
gaps
=
0
;
int
datafrag_len
,
eccfrag_len
,
aligned_len
,
aligned_pos
;
int
datafrag_len
,
eccfrag_len
,
aligned_len
,
aligned_pos
;
int
busw
=
(
chip
->
options
&
NAND_BUSWIDTH_16
)
?
2
:
1
;
int
busw
=
(
chip
->
options
&
NAND_BUSWIDTH_16
)
?
2
:
1
;
int
index
;
int
index
,
section
=
0
;
unsigned
int
max_bitflips
=
0
;
unsigned
int
max_bitflips
=
0
;
struct
mtd_oob_region
oobregion
=
{
};
/* Column address within the page aligned to ECC size (256bytes) */
/* Column address within the page aligned to ECC size (256bytes) */
start_step
=
data_offs
/
chip
->
ecc
.
size
;
start_step
=
data_offs
/
chip
->
ecc
.
size
;
...
@@ -1239,12 +1591,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1239,12 +1591,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
* The performance is faster if we position offsets according to
* The performance is faster if we position offsets according to
* ecc.pos. Let's make sure that there are no gaps in ECC positions.
* ecc.pos. Let's make sure that there are no gaps in ECC positions.
*/
*/
for
(
i
=
0
;
i
<
eccfrag_len
-
1
;
i
++
)
{
ret
=
mtd_ooblayout_find_eccregion
(
mtd
,
index
,
&
section
,
&
oobregion
);
if
(
eccpos
[
i
+
index
]
+
1
!=
eccpos
[
i
+
index
+
1
])
{
if
(
ret
)
gaps
=
1
;
return
ret
;
break
;
}
if
(
oobregion
.
length
<
eccfrag_len
)
}
gaps
=
1
;
if
(
gaps
)
{
if
(
gaps
)
{
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RNDOUT
,
mtd
->
writesize
,
-
1
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RNDOUT
,
mtd
->
writesize
,
-
1
);
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
...
@@ -1253,20 +1606,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1253,20 +1606,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
* Send the command to read the particular ECC bytes take care
* Send the command to read the particular ECC bytes take care
* about buswidth alignment in read_buf.
* about buswidth alignment in read_buf.
*/
*/
aligned_pos
=
eccpos
[
index
]
&
~
(
busw
-
1
);
aligned_pos
=
oobregion
.
offset
&
~
(
busw
-
1
);
aligned_len
=
eccfrag_len
;
aligned_len
=
eccfrag_len
;
if
(
eccpos
[
index
]
&
(
busw
-
1
))
if
(
oobregion
.
offset
&
(
busw
-
1
))
aligned_len
++
;
aligned_len
++
;
if
(
eccpos
[
index
+
(
num_steps
*
chip
->
ecc
.
bytes
)]
&
(
busw
-
1
))
if
((
oobregion
.
offset
+
(
num_steps
*
chip
->
ecc
.
bytes
))
&
(
busw
-
1
))
aligned_len
++
;
aligned_len
++
;
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RNDOUT
,
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RNDOUT
,
mtd
->
writesize
+
aligned_pos
,
-
1
);
mtd
->
writesize
+
aligned_pos
,
-
1
);
chip
->
read_buf
(
mtd
,
&
chip
->
oob_poi
[
aligned_pos
],
aligned_len
);
chip
->
read_buf
(
mtd
,
&
chip
->
oob_poi
[
aligned_pos
],
aligned_len
);
}
}
for
(
i
=
0
;
i
<
eccfrag_len
;
i
++
)
ret
=
mtd_ooblayout_get_eccbytes
(
mtd
,
chip
->
buffers
->
ecccode
,
chip
->
buffers
->
ecccode
[
i
]
=
chip
->
oob_poi
[
eccpos
[
i
+
index
]];
chip
->
oob_poi
,
index
,
eccfrag_len
);
if
(
ret
)
return
ret
;
p
=
bufpoi
+
data_col_addr
;
p
=
bufpoi
+
data_col_addr
;
for
(
i
=
0
;
i
<
eccfrag_len
;
i
+=
chip
->
ecc
.
bytes
,
p
+=
chip
->
ecc
.
size
)
{
for
(
i
=
0
;
i
<
eccfrag_len
;
i
+=
chip
->
ecc
.
bytes
,
p
+=
chip
->
ecc
.
size
)
{
...
@@ -1274,6 +1630,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1274,6 +1630,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
&
chip
->
buffers
->
ecccode
[
i
],
&
chip
->
buffers
->
ecccalc
[
i
]);
&
chip
->
buffers
->
ecccode
[
i
],
&
chip
->
buffers
->
ecccalc
[
i
]);
if
(
stat
==
-
EBADMSG
&&
(
chip
->
ecc
.
options
&
NAND_ECC_GENERIC_ERASED_CHECK
))
{
/* check for empty pages with bitflips */
stat
=
nand_check_erased_ecc_chunk
(
p
,
chip
->
ecc
.
size
,
&
chip
->
buffers
->
ecccode
[
i
],
chip
->
ecc
.
bytes
,
NULL
,
0
,
chip
->
ecc
.
strength
);
}
if
(
stat
<
0
)
{
if
(
stat
<
0
)
{
mtd
->
ecc_stats
.
failed
++
;
mtd
->
ecc_stats
.
failed
++
;
}
else
{
}
else
{
...
@@ -1297,13 +1663,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1297,13 +1663,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
static
int
nand_read_page_hwecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
static
int
nand_read_page_hwecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
i
,
eccsize
=
chip
->
ecc
.
size
,
ret
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
p
=
buf
;
uint8_t
*
p
=
buf
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
uint32_t
*
eccpos
=
chip
->
ecc
.
layout
->
eccpos
;
unsigned
int
max_bitflips
=
0
;
unsigned
int
max_bitflips
=
0
;
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
...
@@ -1313,8 +1678,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1313,8 +1678,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
}
}
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
for
(
i
=
0
;
i
<
chip
->
ecc
.
total
;
i
++
)
ret
=
mtd_ooblayout_get_eccbytes
(
mtd
,
ecc_code
,
chip
->
oob_poi
,
0
,
ecc_code
[
i
]
=
chip
->
oob_poi
[
eccpos
[
i
]];
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
eccsteps
=
chip
->
ecc
.
steps
;
eccsteps
=
chip
->
ecc
.
steps
;
p
=
buf
;
p
=
buf
;
...
@@ -1323,6 +1690,15 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1323,6 +1690,15 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
int
stat
;
int
stat
;
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
&
ecc_code
[
i
],
&
ecc_calc
[
i
]);
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
&
ecc_code
[
i
],
&
ecc_calc
[
i
]);
if
(
stat
==
-
EBADMSG
&&
(
chip
->
ecc
.
options
&
NAND_ECC_GENERIC_ERASED_CHECK
))
{
/* check for empty pages with bitflips */
stat
=
nand_check_erased_ecc_chunk
(
p
,
eccsize
,
&
ecc_code
[
i
],
eccbytes
,
NULL
,
0
,
chip
->
ecc
.
strength
);
}
if
(
stat
<
0
)
{
if
(
stat
<
0
)
{
mtd
->
ecc_stats
.
failed
++
;
mtd
->
ecc_stats
.
failed
++
;
}
else
{
}
else
{
...
@@ -1350,12 +1726,11 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1350,12 +1726,11 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
static
int
nand_read_page_hwecc_oob_first
(
struct
mtd_info
*
mtd
,
static
int
nand_read_page_hwecc_oob_first
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
struct
nand_chip
*
chip
,
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
i
,
eccsize
=
chip
->
ecc
.
size
,
ret
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
p
=
buf
;
uint8_t
*
p
=
buf
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
uint8_t
*
ecc_code
=
chip
->
buffers
->
ecccode
;
uint32_t
*
eccpos
=
chip
->
ecc
.
layout
->
eccpos
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
unsigned
int
max_bitflips
=
0
;
unsigned
int
max_bitflips
=
0
;
...
@@ -1364,8 +1739,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
...
@@ -1364,8 +1739,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READ0
,
0
,
page
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READ0
,
0
,
page
);
for
(
i
=
0
;
i
<
chip
->
ecc
.
total
;
i
++
)
ret
=
mtd_ooblayout_get_eccbytes
(
mtd
,
ecc_code
,
chip
->
oob_poi
,
0
,
ecc_code
[
i
]
=
chip
->
oob_poi
[
eccpos
[
i
]];
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
int
stat
;
int
stat
;
...
@@ -1375,6 +1752,15 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
...
@@ -1375,6 +1752,15 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
&
ecc_code
[
i
],
NULL
);
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
&
ecc_code
[
i
],
NULL
);
if
(
stat
==
-
EBADMSG
&&
(
chip
->
ecc
.
options
&
NAND_ECC_GENERIC_ERASED_CHECK
))
{
/* check for empty pages with bitflips */
stat
=
nand_check_erased_ecc_chunk
(
p
,
eccsize
,
&
ecc_code
[
i
],
eccbytes
,
NULL
,
0
,
chip
->
ecc
.
strength
);
}
if
(
stat
<
0
)
{
if
(
stat
<
0
)
{
mtd
->
ecc_stats
.
failed
++
;
mtd
->
ecc_stats
.
failed
++
;
}
else
{
}
else
{
...
@@ -1402,6 +1788,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1402,6 +1788,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
int
eccsteps
=
chip
->
ecc
.
steps
;
int
eccpadbytes
=
eccbytes
+
chip
->
ecc
.
prepad
+
chip
->
ecc
.
postpad
;
uint8_t
*
p
=
buf
;
uint8_t
*
p
=
buf
;
uint8_t
*
oob
=
chip
->
oob_poi
;
uint8_t
*
oob
=
chip
->
oob_poi
;
unsigned
int
max_bitflips
=
0
;
unsigned
int
max_bitflips
=
0
;
...
@@ -1421,19 +1808,29 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1421,19 +1808,29 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
chip
->
read_buf
(
mtd
,
oob
,
eccbytes
);
chip
->
read_buf
(
mtd
,
oob
,
eccbytes
);
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
oob
,
NULL
);
stat
=
chip
->
ecc
.
correct
(
mtd
,
p
,
oob
,
NULL
);
if
(
stat
<
0
)
{
mtd
->
ecc_stats
.
failed
++
;
}
else
{
mtd
->
ecc_stats
.
corrected
+=
stat
;
max_bitflips
=
max_t
(
unsigned
int
,
max_bitflips
,
stat
);
}
oob
+=
eccbytes
;
oob
+=
eccbytes
;
if
(
chip
->
ecc
.
postpad
)
{
if
(
chip
->
ecc
.
postpad
)
{
chip
->
read_buf
(
mtd
,
oob
,
chip
->
ecc
.
postpad
);
chip
->
read_buf
(
mtd
,
oob
,
chip
->
ecc
.
postpad
);
oob
+=
chip
->
ecc
.
postpad
;
oob
+=
chip
->
ecc
.
postpad
;
}
}
if
(
stat
==
-
EBADMSG
&&
(
chip
->
ecc
.
options
&
NAND_ECC_GENERIC_ERASED_CHECK
))
{
/* check for empty pages with bitflips */
stat
=
nand_check_erased_ecc_chunk
(
p
,
chip
->
ecc
.
size
,
oob
-
eccpadbytes
,
eccpadbytes
,
NULL
,
0
,
chip
->
ecc
.
strength
);
}
if
(
stat
<
0
)
{
mtd
->
ecc_stats
.
failed
++
;
}
else
{
mtd
->
ecc_stats
.
corrected
+=
stat
;
max_bitflips
=
max_t
(
unsigned
int
,
max_bitflips
,
stat
);
}
}
}
/* Calculate remaining oob bytes */
/* Calculate remaining oob bytes */
...
@@ -1446,14 +1843,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1446,14 +1843,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
/**
/**
* nand_transfer_oob - [INTERN] Transfer oob to client buffer
* nand_transfer_oob - [INTERN] Transfer oob to client buffer
* @
chip: nand chip
structure
* @
mtd: mtd info
structure
* @oob: oob destination address
* @oob: oob destination address
* @ops: oob ops structure
* @ops: oob ops structure
* @len: size of oob to transfer
* @len: size of oob to transfer
*/
*/
static
uint8_t
*
nand_transfer_oob
(
struct
nand_chip
*
chip
,
uint8_t
*
oob
,
static
uint8_t
*
nand_transfer_oob
(
struct
mtd_info
*
mtd
,
uint8_t
*
oob
,
struct
mtd_oob_ops
*
ops
,
size_t
len
)
struct
mtd_oob_ops
*
ops
,
size_t
len
)
{
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
int
ret
;
switch
(
ops
->
mode
)
{
switch
(
ops
->
mode
)
{
case
MTD_OPS_PLACE_OOB
:
case
MTD_OPS_PLACE_OOB
:
...
@@ -1461,31 +1861,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
...
@@ -1461,31 +1861,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
memcpy
(
oob
,
chip
->
oob_poi
+
ops
->
ooboffs
,
len
);
memcpy
(
oob
,
chip
->
oob_poi
+
ops
->
ooboffs
,
len
);
return
oob
+
len
;
return
oob
+
len
;
case
MTD_OPS_AUTO_OOB
:
{
case
MTD_OPS_AUTO_OOB
:
struct
nand_oobfree
*
free
=
chip
->
ecc
.
layout
->
oobfree
;
ret
=
mtd_ooblayout_get_databytes
(
mtd
,
oob
,
chip
->
oob_poi
,
uint32_t
boffs
=
0
,
roffs
=
ops
->
ooboffs
;
ops
->
ooboffs
,
len
);
size_t
bytes
=
0
;
BUG_ON
(
ret
);
return
oob
+
len
;
for
(;
free
->
length
&&
len
;
free
++
,
len
-=
bytes
)
{
/* Read request not from offset 0? */
if
(
unlikely
(
roffs
))
{
if
(
roffs
>=
free
->
length
)
{
roffs
-=
free
->
length
;
continue
;
}
boffs
=
free
->
offset
+
roffs
;
bytes
=
min_t
(
size_t
,
len
,
(
free
->
length
-
roffs
));
roffs
=
0
;
}
else
{
bytes
=
min_t
(
size_t
,
len
,
free
->
length
);
boffs
=
free
->
offset
;
}
memcpy
(
oob
,
chip
->
oob_poi
+
boffs
,
bytes
);
oob
+=
bytes
;
}
return
oob
;
}
default:
default:
BUG
();
BUG
();
}
}
...
@@ -1503,7 +1884,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
...
@@ -1503,7 +1884,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
*/
*/
static
int
nand_setup_read_retry
(
struct
mtd_info
*
mtd
,
int
retry_mode
)
static
int
nand_setup_read_retry
(
struct
mtd_info
*
mtd
,
int
retry_mode
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
pr_debug
(
"setting READ RETRY mode %d
\n
"
,
retry_mode
);
pr_debug
(
"setting READ RETRY mode %d
\n
"
,
retry_mode
);
...
@@ -1524,16 +1905,17 @@ static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
...
@@ -1524,16 +1905,17 @@ static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
*
*
* Internal function. Called with chip held.
* Internal function. Called with chip held.
*/
*/
// Elphel: was before Rocko, used outside
//static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
int
nand_do_read_ops
(
struct
mtd_info
*
mtd
,
loff_t
from
,
int
nand_do_read_ops
(
struct
mtd_info
*
mtd
,
loff_t
from
,
struct
mtd_oob_ops
*
ops
)
struct
mtd_oob_ops
*
ops
)
{
{
int
chipnr
,
page
,
realpage
,
col
,
bytes
,
aligned
,
oob_required
;
int
chipnr
,
page
,
realpage
,
col
,
bytes
,
aligned
,
oob_required
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
int
ret
=
0
;
int
ret
=
0
;
uint32_t
readlen
=
ops
->
len
;
uint32_t
readlen
=
ops
->
len
;
uint32_t
oobreadlen
=
ops
->
ooblen
;
uint32_t
oobreadlen
=
ops
->
ooblen
;
uint32_t
max_oobsize
=
ops
->
mode
==
MTD_OPS_AUTO_OOB
?
uint32_t
max_oobsize
=
mtd_oobavail
(
mtd
,
ops
);
mtd
->
oobavail
:
mtd
->
oobsize
;
uint8_t
*
bufpoi
,
*
oob
,
*
buf
;
uint8_t
*
bufpoi
,
*
oob
,
*
buf
;
int
use_bufpoi
;
int
use_bufpoi
;
...
@@ -1620,7 +2002,7 @@ read_retry:
...
@@ -1620,7 +2002,7 @@ read_retry:
int
toread
=
min
(
oobreadlen
,
max_oobsize
);
int
toread
=
min
(
oobreadlen
,
max_oobsize
);
if
(
toread
)
{
if
(
toread
)
{
oob
=
nand_transfer_oob
(
chip
,
oob
=
nand_transfer_oob
(
mtd
,
oob
,
ops
,
toread
);
oob
,
ops
,
toread
);
oobreadlen
-=
toread
;
oobreadlen
-=
toread
;
}
}
...
@@ -1717,9 +2099,9 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
...
@@ -1717,9 +2099,9 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
int
ret
;
int
ret
;
nand_get_device
(
mtd
,
FL_READING
);
nand_get_device
(
mtd
,
FL_READING
);
memset
(
&
ops
,
0
,
sizeof
(
ops
));
ops
.
len
=
len
;
ops
.
len
=
len
;
ops
.
datbuf
=
buf
;
ops
.
datbuf
=
buf
;
ops
.
oobbuf
=
NULL
;
ops
.
mode
=
MTD_OPS_PLACE_OOB
;
ops
.
mode
=
MTD_OPS_PLACE_OOB
;
ret
=
nand_do_read_ops
(
mtd
,
from
,
&
ops
);
ret
=
nand_do_read_ops
(
mtd
,
from
,
&
ops
);
*
retlen
=
ops
.
retlen
;
*
retlen
=
ops
.
retlen
;
...
@@ -1733,13 +2115,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
...
@@ -1733,13 +2115,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @page: page number to read
* @page: page number to read
*/
*/
static
int
nand_read_oob_std
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
nand_read_oob_std
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
page
)
int
page
)
{
{
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READOOB
,
0
,
page
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READOOB
,
0
,
page
);
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
chip
->
read_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
return
0
;
return
0
;
}
}
EXPORT_SYMBOL
(
nand_read_oob_std
);
/**
/**
* nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
* nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
...
@@ -1748,8 +2130,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1748,8 +2130,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @page: page number to read
* @page: page number to read
*/
*/
static
int
nand_read_oob_syndrome
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
nand_read_oob_syndrome
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
page
)
int
page
)
{
{
int
length
=
mtd
->
oobsize
;
int
length
=
mtd
->
oobsize
;
int
chunk
=
chip
->
ecc
.
bytes
+
chip
->
ecc
.
prepad
+
chip
->
ecc
.
postpad
;
int
chunk
=
chip
->
ecc
.
bytes
+
chip
->
ecc
.
prepad
+
chip
->
ecc
.
postpad
;
...
@@ -1777,6 +2159,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1777,6 +2159,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
return
0
;
return
0
;
}
}
EXPORT_SYMBOL
(
nand_read_oob_syndrome
);
/**
/**
* nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
* nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
...
@@ -1784,8 +2167,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1784,8 +2167,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @page: page number to write
* @page: page number to write
*/
*/
static
int
nand_write_oob_std
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
nand_write_oob_std
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
int
page
)
int
page
)
{
{
int
status
=
0
;
int
status
=
0
;
const
uint8_t
*
buf
=
chip
->
oob_poi
;
const
uint8_t
*
buf
=
chip
->
oob_poi
;
...
@@ -1800,6 +2182,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1800,6 +2182,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
return
status
&
NAND_STATUS_FAIL
?
-
EIO
:
0
;
return
status
&
NAND_STATUS_FAIL
?
-
EIO
:
0
;
}
}
EXPORT_SYMBOL
(
nand_write_oob_std
);
/**
/**
* nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
* nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
...
@@ -1808,8 +2191,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -1808,8 +2191,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @page: page number to write
* @page: page number to write
*/
*/
static
int
nand_write_oob_syndrome
(
struct
mtd_info
*
mtd
,
int
nand_write_oob_syndrome
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
struct
nand_chip
*
chip
,
int
page
)
int
page
)
{
{
int
chunk
=
chip
->
ecc
.
bytes
+
chip
->
ecc
.
prepad
+
chip
->
ecc
.
postpad
;
int
chunk
=
chip
->
ecc
.
bytes
+
chip
->
ecc
.
prepad
+
chip
->
ecc
.
postpad
;
int
eccsize
=
chip
->
ecc
.
size
,
length
=
mtd
->
oobsize
;
int
eccsize
=
chip
->
ecc
.
size
,
length
=
mtd
->
oobsize
;
...
@@ -1859,6 +2242,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
...
@@ -1859,6 +2242,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
return
status
&
NAND_STATUS_FAIL
?
-
EIO
:
0
;
return
status
&
NAND_STATUS_FAIL
?
-
EIO
:
0
;
}
}
EXPORT_SYMBOL
(
nand_write_oob_syndrome
);
/**
/**
* nand_do_read_oob - [INTERN] NAND read out-of-band
* nand_do_read_oob - [INTERN] NAND read out-of-band
...
@@ -1872,7 +2256,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
...
@@ -1872,7 +2256,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
struct
mtd_oob_ops
*
ops
)
struct
mtd_oob_ops
*
ops
)
{
{
int
page
,
realpage
,
chipnr
;
int
page
,
realpage
,
chipnr
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
struct
mtd_ecc_stats
stats
;
struct
mtd_ecc_stats
stats
;
int
readlen
=
ops
->
ooblen
;
int
readlen
=
ops
->
ooblen
;
int
len
;
int
len
;
...
@@ -1884,10 +2268,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
...
@@ -1884,10 +2268,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
stats
=
mtd
->
ecc_stats
;
stats
=
mtd
->
ecc_stats
;
if
(
ops
->
mode
==
MTD_OPS_AUTO_OOB
)
len
=
mtd_oobavail
(
mtd
,
ops
);
len
=
chip
->
ecc
.
layout
->
oobavail
;
else
len
=
mtd
->
oobsize
;
if
(
unlikely
(
ops
->
ooboffs
>=
len
))
{
if
(
unlikely
(
ops
->
ooboffs
>=
len
))
{
pr_debug
(
"%s: attempt to start read outside oob
\n
"
,
pr_debug
(
"%s: attempt to start read outside oob
\n
"
,
...
@@ -1921,7 +2302,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
...
@@ -1921,7 +2302,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
break
;
break
;
len
=
min
(
len
,
readlen
);
len
=
min
(
len
,
readlen
);
buf
=
nand_transfer_oob
(
chip
,
buf
,
ops
,
len
);
buf
=
nand_transfer_oob
(
mtd
,
buf
,
ops
,
len
);
if
(
chip
->
options
&
NAND_NEED_READRDY
)
{
if
(
chip
->
options
&
NAND_NEED_READRDY
)
{
/* Apply delay or wait for ready/busy pin */
/* Apply delay or wait for ready/busy pin */
...
@@ -1970,7 +2351,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
...
@@ -1970,7 +2351,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
static
int
nand_read_oob
(
struct
mtd_info
*
mtd
,
loff_t
from
,
static
int
nand_read_oob
(
struct
mtd_info
*
mtd
,
loff_t
from
,
struct
mtd_oob_ops
*
ops
)
struct
mtd_oob_ops
*
ops
)
{
{
int
ret
=
-
ENOTSUPP
;
int
ret
;
ops
->
retlen
=
0
;
ops
->
retlen
=
0
;
...
@@ -1981,24 +2362,18 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
...
@@ -1981,24 +2362,18 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
return
-
EINVAL
;
return
-
EINVAL
;
}
}
nand_get_device
(
mtd
,
FL_READING
);
if
(
ops
->
mode
!=
MTD_OPS_PLACE_OOB
&&
ops
->
mode
!=
MTD_OPS_AUTO_OOB
&&
switch
(
ops
->
mode
)
{
ops
->
mode
!=
MTD_OPS_RAW
)
case
MTD_OPS_PLACE_OOB
:
return
-
ENOTSUPP
;
case
MTD_OPS_AUTO_OOB
:
case
MTD_OPS_RAW
:
break
;
default:
nand_get_device
(
mtd
,
FL_READING
);
goto
out
;
}
if
(
!
ops
->
datbuf
)
if
(
!
ops
->
datbuf
)
ret
=
nand_do_read_oob
(
mtd
,
from
,
ops
);
ret
=
nand_do_read_oob
(
mtd
,
from
,
ops
);
else
else
ret
=
nand_do_read_ops
(
mtd
,
from
,
ops
);
ret
=
nand_do_read_ops
(
mtd
,
from
,
ops
);
out:
nand_release_device
(
mtd
);
nand_release_device
(
mtd
);
return
ret
;
return
ret
;
}
}
...
@@ -2010,11 +2385,12 @@ out:
...
@@ -2010,11 +2385,12 @@ out:
* @chip: nand chip info structure
* @chip: nand chip info structure
* @buf: data buffer
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
* @oob_required: must write chip->oob_poi to OOB
* @page: page number to write
*
*
* Not for syndrome calculating ECC controllers, which use a special oob layout.
* Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
*/
static
int
nand_write_page_raw
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
static
int
nand_write_page_raw
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
)
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
chip
->
write_buf
(
mtd
,
buf
,
mtd
->
writesize
);
chip
->
write_buf
(
mtd
,
buf
,
mtd
->
writesize
);
if
(
oob_required
)
if
(
oob_required
)
...
@@ -2029,12 +2405,14 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -2029,12 +2405,14 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @buf: data buffer
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
* @oob_required: must write chip->oob_poi to OOB
* @page: page number to write
*
*
* We need a special oob layout and handling even when ECC isn't checked.
* We need a special oob layout and handling even when ECC isn't checked.
*/
*/
static
int
nand_write_page_raw_syndrome
(
struct
mtd_info
*
mtd
,
static
int
nand_write_page_raw_syndrome
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
)
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
int
eccsize
=
chip
->
ecc
.
size
;
int
eccsize
=
chip
->
ecc
.
size
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
...
@@ -2071,25 +2449,28 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
...
@@ -2071,25 +2449,28 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @buf: data buffer
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
* @oob_required: must write chip->oob_poi to OOB
* @page: page number to write
*/
*/
static
int
nand_write_page_swecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
static
int
nand_write_page_swecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
)
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
i
,
eccsize
=
chip
->
ecc
.
size
,
ret
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
const
uint8_t
*
p
=
buf
;
const
uint8_t
*
p
=
buf
;
uint32_t
*
eccpos
=
chip
->
ecc
.
layout
->
eccpos
;
/* Software ECC calculation */
/* Software ECC calculation */
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
for
(
i
=
0
;
i
<
chip
->
ecc
.
total
;
i
++
)
ret
=
mtd_ooblayout_set_eccbytes
(
mtd
,
ecc_calc
,
chip
->
oob_poi
,
0
,
chip
->
oob_poi
[
eccpos
[
i
]]
=
ecc_calc
[
i
];
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
return
chip
->
ecc
.
write_page_raw
(
mtd
,
chip
,
buf
,
1
);
return
chip
->
ecc
.
write_page_raw
(
mtd
,
chip
,
buf
,
1
,
page
);
}
}
/**
/**
...
@@ -2098,16 +2479,17 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -2098,16 +2479,17 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @buf: data buffer
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
* @oob_required: must write chip->oob_poi to OOB
* @page: page number to write
*/
*/
static
int
nand_write_page_hwecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
static
int
nand_write_page_hwecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
)
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
i
,
eccsize
=
chip
->
ecc
.
size
,
ret
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccsteps
=
chip
->
ecc
.
steps
;
int
eccsteps
=
chip
->
ecc
.
steps
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
const
uint8_t
*
p
=
buf
;
const
uint8_t
*
p
=
buf
;
uint32_t
*
eccpos
=
chip
->
ecc
.
layout
->
eccpos
;
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
for
(
i
=
0
;
eccsteps
;
eccsteps
--
,
i
+=
eccbytes
,
p
+=
eccsize
)
{
chip
->
ecc
.
hwctl
(
mtd
,
NAND_ECC_WRITE
);
chip
->
ecc
.
hwctl
(
mtd
,
NAND_ECC_WRITE
);
...
@@ -2115,8 +2497,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -2115,8 +2497,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
chip
->
ecc
.
calculate
(
mtd
,
p
,
&
ecc_calc
[
i
]);
}
}
for
(
i
=
0
;
i
<
chip
->
ecc
.
total
;
i
++
)
ret
=
mtd_ooblayout_set_eccbytes
(
mtd
,
ecc_calc
,
chip
->
oob_poi
,
0
,
chip
->
oob_poi
[
eccpos
[
i
]]
=
ecc_calc
[
i
];
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
chip
->
write_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
chip
->
write_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
...
@@ -2125,29 +2509,29 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -2125,29 +2509,29 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
/**
/**
* nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
* nand_write_subpage_hwecc - [REPLAC
E
ABLE] hardware ECC based subpage write
* @mtd: mtd info structure
* @mtd: mtd info structure
* @chip: nand chip info structure
* @chip: nand chip info structure
* @offset: column address of subpage within the page
* @offset: column address of subpage within the page
* @data_len: data length
* @data_len: data length
* @buf: data buffer
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
* @oob_required: must write chip->oob_poi to OOB
* @page: page number to write
*/
*/
static
int
nand_write_subpage_hwecc
(
struct
mtd_info
*
mtd
,
static
int
nand_write_subpage_hwecc
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
uint32_t
offset
,
struct
nand_chip
*
chip
,
uint32_t
offset
,
uint32_t
data_len
,
const
uint8_t
*
buf
,
uint32_t
data_len
,
const
uint8_t
*
buf
,
int
oob_required
)
int
oob_required
,
int
page
)
{
{
uint8_t
*
oob_buf
=
chip
->
oob_poi
;
uint8_t
*
oob_buf
=
chip
->
oob_poi
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
uint8_t
*
ecc_calc
=
chip
->
buffers
->
ecccalc
;
int
ecc_size
=
chip
->
ecc
.
size
;
int
ecc_size
=
chip
->
ecc
.
size
;
int
ecc_bytes
=
chip
->
ecc
.
bytes
;
int
ecc_bytes
=
chip
->
ecc
.
bytes
;
int
ecc_steps
=
chip
->
ecc
.
steps
;
int
ecc_steps
=
chip
->
ecc
.
steps
;
uint32_t
*
eccpos
=
chip
->
ecc
.
layout
->
eccpos
;
uint32_t
start_step
=
offset
/
ecc_size
;
uint32_t
start_step
=
offset
/
ecc_size
;
uint32_t
end_step
=
(
offset
+
data_len
-
1
)
/
ecc_size
;
uint32_t
end_step
=
(
offset
+
data_len
-
1
)
/
ecc_size
;
int
oob_bytes
=
mtd
->
oobsize
/
ecc_steps
;
int
oob_bytes
=
mtd
->
oobsize
/
ecc_steps
;
int
step
,
i
;
int
step
,
ret
;
for
(
step
=
0
;
step
<
ecc_steps
;
step
++
)
{
for
(
step
=
0
;
step
<
ecc_steps
;
step
++
)
{
/* configure controller for WRITE access */
/* configure controller for WRITE access */
...
@@ -2175,8 +2559,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
...
@@ -2175,8 +2559,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
/* copy calculated ECC for whole page to chip->buffer->oob */
/* copy calculated ECC for whole page to chip->buffer->oob */
/* this include masked-value(0xFF) for unwritten subpages */
/* this include masked-value(0xFF) for unwritten subpages */
ecc_calc
=
chip
->
buffers
->
ecccalc
;
ecc_calc
=
chip
->
buffers
->
ecccalc
;
for
(
i
=
0
;
i
<
chip
->
ecc
.
total
;
i
++
)
ret
=
mtd_ooblayout_set_eccbytes
(
mtd
,
ecc_calc
,
chip
->
oob_poi
,
0
,
chip
->
oob_poi
[
eccpos
[
i
]]
=
ecc_calc
[
i
];
chip
->
ecc
.
total
);
if
(
ret
)
return
ret
;
/* write OOB buffer to NAND device */
/* write OOB buffer to NAND device */
chip
->
write_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
chip
->
write_buf
(
mtd
,
chip
->
oob_poi
,
mtd
->
oobsize
);
...
@@ -2191,13 +2577,15 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
...
@@ -2191,13 +2577,15 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
* @chip: nand chip info structure
* @chip: nand chip info structure
* @buf: data buffer
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
* @oob_required: must write chip->oob_poi to OOB
* @page: page number to write
*
*
* The hw generator calculates the error syndrome automatically. Therefore we
* The hw generator calculates the error syndrome automatically. Therefore we
* need a special oob layout and handling.
* need a special oob layout and handling.
*/
*/
static
int
nand_write_page_syndrome
(
struct
mtd_info
*
mtd
,
static
int
nand_write_page_syndrome
(
struct
mtd_info
*
mtd
,
struct
nand_chip
*
chip
,
struct
nand_chip
*
chip
,
const
uint8_t
*
buf
,
int
oob_required
)
const
uint8_t
*
buf
,
int
oob_required
,
int
page
)
{
{
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
i
,
eccsize
=
chip
->
ecc
.
size
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
int
eccbytes
=
chip
->
ecc
.
bytes
;
...
@@ -2261,12 +2649,13 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -2261,12 +2649,13 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
if
(
unlikely
(
raw
))
if
(
unlikely
(
raw
))
status
=
chip
->
ecc
.
write_page_raw
(
mtd
,
chip
,
buf
,
status
=
chip
->
ecc
.
write_page_raw
(
mtd
,
chip
,
buf
,
oob_required
);
oob_required
,
page
);
else
if
(
subpage
)
else
if
(
subpage
)
status
=
chip
->
ecc
.
write_subpage
(
mtd
,
chip
,
offset
,
data_len
,
status
=
chip
->
ecc
.
write_subpage
(
mtd
,
chip
,
offset
,
data_len
,
buf
,
oob_required
);
buf
,
oob_required
,
page
);
else
else
status
=
chip
->
ecc
.
write_page
(
mtd
,
chip
,
buf
,
oob_required
);
status
=
chip
->
ecc
.
write_page
(
mtd
,
chip
,
buf
,
oob_required
,
page
);
if
(
status
<
0
)
if
(
status
<
0
)
return
status
;
return
status
;
...
@@ -2309,7 +2698,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -2309,7 +2698,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
static
uint8_t
*
nand_fill_oob
(
struct
mtd_info
*
mtd
,
uint8_t
*
oob
,
size_t
len
,
static
uint8_t
*
nand_fill_oob
(
struct
mtd_info
*
mtd
,
uint8_t
*
oob
,
size_t
len
,
struct
mtd_oob_ops
*
ops
)
struct
mtd_oob_ops
*
ops
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
int
ret
;
/*
/*
* Initialise to all 0xFF, to avoid the possibility of left over OOB
* Initialise to all 0xFF, to avoid the possibility of left over OOB
...
@@ -2324,37 +2714,20 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
...
@@ -2324,37 +2714,20 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
memcpy
(
chip
->
oob_poi
+
ops
->
ooboffs
,
oob
,
len
);
memcpy
(
chip
->
oob_poi
+
ops
->
ooboffs
,
oob
,
len
);
return
oob
+
len
;
return
oob
+
len
;
case
MTD_OPS_AUTO_OOB
:
{
case
MTD_OPS_AUTO_OOB
:
struct
nand_oobfree
*
free
=
chip
->
ecc
.
layout
->
oobfree
;
ret
=
mtd_ooblayout_set_databytes
(
mtd
,
oob
,
chip
->
oob_poi
,
uint32_t
boffs
=
0
,
woffs
=
ops
->
ooboffs
;
ops
->
ooboffs
,
len
);
size_t
bytes
=
0
;
BUG_ON
(
ret
);
return
oob
+
len
;
for
(;
free
->
length
&&
len
;
free
++
,
len
-=
bytes
)
{
/* Write request not from offset 0? */
if
(
unlikely
(
woffs
))
{
if
(
woffs
>=
free
->
length
)
{
woffs
-=
free
->
length
;
continue
;
}
boffs
=
free
->
offset
+
woffs
;
bytes
=
min_t
(
size_t
,
len
,
(
free
->
length
-
woffs
));
woffs
=
0
;
}
else
{
bytes
=
min_t
(
size_t
,
len
,
free
->
length
);
boffs
=
free
->
offset
;
}
memcpy
(
chip
->
oob_poi
+
boffs
,
oob
,
bytes
);
oob
+=
bytes
;
}
return
oob
;
}
default:
default:
BUG
();
BUG
();
}
}
return
NULL
;
return
NULL
;
}
}
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
/**
/**
* nand_do_write_ops - [INTERN] NAND write with ECC
* nand_do_write_ops - [INTERN] NAND write with ECC
* @mtd: MTD device structure
* @mtd: MTD device structure
...
@@ -2363,16 +2736,18 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
...
@@ -2363,16 +2736,18 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
*
*
* NAND write with ECC.
* NAND write with ECC.
*/
*/
// Elphel: was before Rocko, used outside
//
//static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
int
nand_do_write_ops
(
struct
mtd_info
*
mtd
,
loff_t
to
,
int
nand_do_write_ops
(
struct
mtd_info
*
mtd
,
loff_t
to
,
struct
mtd_oob_ops
*
ops
)
struct
mtd_oob_ops
*
ops
)
{
{
int
chipnr
,
realpage
,
page
,
blockmask
,
column
;
int
chipnr
,
realpage
,
page
,
blockmask
,
column
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
uint32_t
writelen
=
ops
->
len
;
uint32_t
writelen
=
ops
->
len
;
uint32_t
oobwritelen
=
ops
->
ooblen
;
uint32_t
oobwritelen
=
ops
->
ooblen
;
uint32_t
oobmaxlen
=
ops
->
mode
==
MTD_OPS_AUTO_OOB
?
uint32_t
oobmaxlen
=
mtd_oobavail
(
mtd
,
ops
);
mtd
->
oobavail
:
mtd
->
oobsize
;
uint8_t
*
oob
=
ops
->
oobbuf
;
uint8_t
*
oob
=
ops
->
oobbuf
;
uint8_t
*
buf
=
ops
->
datbuf
;
uint8_t
*
buf
=
ops
->
datbuf
;
...
@@ -2421,7 +2796,7 @@ int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
...
@@ -2421,7 +2796,7 @@ int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
int
cached
=
writelen
>
bytes
&&
page
!=
blockmask
;
int
cached
=
writelen
>
bytes
&&
page
!=
blockmask
;
uint8_t
*
wbuf
=
buf
;
uint8_t
*
wbuf
=
buf
;
int
use_bufpoi
;
int
use_bufpoi
;
int
part_pagewr
=
(
column
||
writelen
<
(
mtd
->
writesize
-
1
)
);
int
part_pagewr
=
(
column
||
writelen
<
mtd
->
writesize
);
if
(
part_pagewr
)
if
(
part_pagewr
)
use_bufpoi
=
1
;
use_bufpoi
=
1
;
...
@@ -2497,7 +2872,7 @@ err_out:
...
@@ -2497,7 +2872,7 @@ err_out:
static
int
panic_nand_write
(
struct
mtd_info
*
mtd
,
loff_t
to
,
size_t
len
,
static
int
panic_nand_write
(
struct
mtd_info
*
mtd
,
loff_t
to
,
size_t
len
,
size_t
*
retlen
,
const
uint8_t
*
buf
)
size_t
*
retlen
,
const
uint8_t
*
buf
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
struct
mtd_oob_ops
ops
;
struct
mtd_oob_ops
ops
;
int
ret
;
int
ret
;
...
@@ -2507,9 +2882,9 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
...
@@ -2507,9 +2882,9 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
/* Grab the device */
/* Grab the device */
panic_nand_get_device
(
chip
,
mtd
,
FL_WRITING
);
panic_nand_get_device
(
chip
,
mtd
,
FL_WRITING
);
memset
(
&
ops
,
0
,
sizeof
(
ops
));
ops
.
len
=
len
;
ops
.
len
=
len
;
ops
.
datbuf
=
(
uint8_t
*
)
buf
;
ops
.
datbuf
=
(
uint8_t
*
)
buf
;
ops
.
oobbuf
=
NULL
;
ops
.
mode
=
MTD_OPS_PLACE_OOB
;
ops
.
mode
=
MTD_OPS_PLACE_OOB
;
ret
=
nand_do_write_ops
(
mtd
,
to
,
&
ops
);
ret
=
nand_do_write_ops
(
mtd
,
to
,
&
ops
);
...
@@ -2535,9 +2910,9 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
...
@@ -2535,9 +2910,9 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
int
ret
;
int
ret
;
nand_get_device
(
mtd
,
FL_WRITING
);
nand_get_device
(
mtd
,
FL_WRITING
);
memset
(
&
ops
,
0
,
sizeof
(
ops
));
ops
.
len
=
len
;
ops
.
len
=
len
;
ops
.
datbuf
=
(
uint8_t
*
)
buf
;
ops
.
datbuf
=
(
uint8_t
*
)
buf
;
ops
.
oobbuf
=
NULL
;
ops
.
mode
=
MTD_OPS_PLACE_OOB
;
ops
.
mode
=
MTD_OPS_PLACE_OOB
;
ret
=
nand_do_write_ops
(
mtd
,
to
,
&
ops
);
ret
=
nand_do_write_ops
(
mtd
,
to
,
&
ops
);
*
retlen
=
ops
.
retlen
;
*
retlen
=
ops
.
retlen
;
...
@@ -2557,15 +2932,12 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
...
@@ -2557,15 +2932,12 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct
mtd_oob_ops
*
ops
)
struct
mtd_oob_ops
*
ops
)
{
{
int
chipnr
,
page
,
status
,
len
;
int
chipnr
,
page
,
status
,
len
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
pr_debug
(
"%s: to = 0x%08x, len = %i
\n
"
,
pr_debug
(
"%s: to = 0x%08x, len = %i
\n
"
,
__func__
,
(
unsigned
int
)
to
,
(
int
)
ops
->
ooblen
);
__func__
,
(
unsigned
int
)
to
,
(
int
)
ops
->
ooblen
);
if
(
ops
->
mode
==
MTD_OPS_AUTO_OOB
)
len
=
mtd_oobavail
(
mtd
,
ops
);
len
=
chip
->
ecc
.
layout
->
oobavail
;
else
len
=
mtd
->
oobsize
;
/* Do not allow write past end of page */
/* Do not allow write past end of page */
if
((
ops
->
ooboffs
+
ops
->
ooblen
)
>
len
)
{
if
((
ops
->
ooboffs
+
ops
->
ooblen
)
>
len
)
{
...
@@ -2591,10 +2963,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
...
@@ -2591,10 +2963,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
}
}
chipnr
=
(
int
)(
to
>>
chip
->
chip_shift
);
chipnr
=
(
int
)(
to
>>
chip
->
chip_shift
);
chip
->
select_chip
(
mtd
,
chipnr
);
/* Shift to get page */
page
=
(
int
)(
to
>>
chip
->
page_shift
);
/*
/*
* Reset the chip. Some chips (like the Toshiba TC5832DC found in one
* Reset the chip. Some chips (like the Toshiba TC5832DC found in one
...
@@ -2609,8 +2977,15 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
...
@@ -2609,8 +2977,15 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
* interface mode. This will be the problem for devices configured for
* interface mode. This will be the problem for devices configured for
* NVDDR modes. So, limiting the reset operation to Toshiba devices.
* NVDDR modes. So, limiting the reset operation to Toshiba devices.
*/
*/
if
(
chip
->
onfi_params
.
jedec_id
==
NAND_MFR_TOSHIBA
)
if
(
chip
->
onfi_params
.
jedec_id
==
NAND_MFR_TOSHIBA
)
{
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
nand_reset
(
chip
,
chipnr
);
}
chip
->
select_chip
(
mtd
,
chipnr
);
/* Shift to get page */
page
=
(
int
)(
to
>>
chip
->
page_shift
);
/* Check, if it is write protected */
/* Check, if it is write protected */
if
(
nand_check_wp
(
mtd
))
{
if
(
nand_check_wp
(
mtd
))
{
...
@@ -2690,7 +3065,7 @@ out:
...
@@ -2690,7 +3065,7 @@ out:
*/
*/
static
int
single_erase
(
struct
mtd_info
*
mtd
,
int
page
)
static
int
single_erase
(
struct
mtd_info
*
mtd
,
int
page
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
/* Send commands to erase a block */
/* Send commands to erase a block */
chip
->
cmdfunc
(
mtd
,
NAND_CMD_ERASE1
,
-
1
,
page
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_ERASE1
,
-
1
,
page
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_ERASE2
,
-
1
,
-
1
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_ERASE2
,
-
1
,
-
1
);
...
@@ -2722,7 +3097,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
...
@@ -2722,7 +3097,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int
allowbbt
)
int
allowbbt
)
{
{
int
page
,
status
,
pages_per_block
,
ret
,
chipnr
;
int
page
,
status
,
pages_per_block
,
ret
,
chipnr
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
loff_t
len
;
loff_t
len
;
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
pr_debug
(
"%s: start = 0x%012llx, len = %llu
\n
"
,
...
@@ -2761,7 +3136,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
...
@@ -2761,7 +3136,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
while
(
len
)
{
while
(
len
)
{
/* Check if we have a bad block, we do not erase bad blocks! */
/* Check if we have a bad block, we do not erase bad blocks! */
if
(
nand_block_checkbad
(
mtd
,
((
loff_t
)
page
)
<<
if
(
nand_block_checkbad
(
mtd
,
((
loff_t
)
page
)
<<
chip
->
page_shift
,
0
,
allowbbt
))
{
chip
->
page_shift
,
allowbbt
))
{
pr_warn
(
"%s: attempt to erase a bad block at page 0x%08x
\n
"
,
pr_warn
(
"%s: attempt to erase a bad block at page 0x%08x
\n
"
,
__func__
,
page
);
__func__
,
page
);
instr
->
state
=
MTD_ERASE_FAILED
;
instr
->
state
=
MTD_ERASE_FAILED
;
...
@@ -2848,7 +3223,20 @@ static void nand_sync(struct mtd_info *mtd)
...
@@ -2848,7 +3223,20 @@ static void nand_sync(struct mtd_info *mtd)
*/
*/
static
int
nand_block_isbad
(
struct
mtd_info
*
mtd
,
loff_t
offs
)
static
int
nand_block_isbad
(
struct
mtd_info
*
mtd
,
loff_t
offs
)
{
{
return
nand_block_checkbad
(
mtd
,
offs
,
1
,
0
);
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
int
chipnr
=
(
int
)(
offs
>>
chip
->
chip_shift
);
int
ret
;
/* Select the NAND device */
nand_get_device
(
mtd
,
FL_READING
);
chip
->
select_chip
(
mtd
,
chipnr
);
ret
=
nand_block_checkbad
(
mtd
,
offs
,
0
);
chip
->
select_chip
(
mtd
,
-
1
);
nand_release_device
(
mtd
);
return
ret
;
}
}
/**
/**
...
@@ -2916,9 +3304,6 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
...
@@ -2916,9 +3304,6 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
&
ONFI_OPT_CMD_SET_GET_FEATURES
))
&
ONFI_OPT_CMD_SET_GET_FEATURES
))
return
-
EINVAL
;
return
-
EINVAL
;
/* clear the sub feature parameters */
memset
(
subfeature_param
,
0
,
ONFI_SUBFEATURE_PARAM_LEN
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_GET_FEATURES
,
addr
,
-
1
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_GET_FEATURES
,
addr
,
-
1
);
for
(
i
=
0
;
i
<
ONFI_SUBFEATURE_PARAM_LEN
;
++
i
)
for
(
i
=
0
;
i
<
ONFI_SUBFEATURE_PARAM_LEN
;
++
i
)
*
subfeature_param
++
=
chip
->
read_byte
(
mtd
);
*
subfeature_param
++
=
chip
->
read_byte
(
mtd
);
...
@@ -2940,7 +3325,7 @@ static int nand_suspend(struct mtd_info *mtd)
...
@@ -2940,7 +3325,7 @@ static int nand_suspend(struct mtd_info *mtd)
*/
*/
static
void
nand_resume
(
struct
mtd_info
*
mtd
)
static
void
nand_resume
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
if
(
chip
->
state
==
FL_PM_SUSPENDED
)
if
(
chip
->
state
==
FL_PM_SUSPENDED
)
nand_release_device
(
mtd
);
nand_release_device
(
mtd
);
...
@@ -2956,7 +3341,7 @@ static void nand_resume(struct mtd_info *mtd)
...
@@ -2956,7 +3341,7 @@ static void nand_resume(struct mtd_info *mtd)
*/
*/
static
void
nand_shutdown
(
struct
mtd_info
*
mtd
)
static
void
nand_shutdown
(
struct
mtd_info
*
mtd
)
{
{
nand_get_device
(
mtd
,
FL_
SHUTDOWN
);
nand_get_device
(
mtd
,
FL_
PM_SUSPENDED
);
}
}
/* Set default functions */
/* Set default functions */
...
@@ -3003,8 +3388,7 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
...
@@ -3003,8 +3388,7 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
if
(
!
chip
->
controller
)
{
if
(
!
chip
->
controller
)
{
chip
->
controller
=
&
chip
->
hwcontrol
;
chip
->
controller
=
&
chip
->
hwcontrol
;
spin_lock_init
(
&
chip
->
controller
->
lock
);
nand_hw_control_init
(
chip
->
controller
);
init_waitqueue_head
(
&
chip
->
controller
->
wq
);
}
}
}
}
...
@@ -3112,7 +3496,7 @@ ext_out:
...
@@ -3112,7 +3496,7 @@ ext_out:
static
int
nand_setup_read_retry_micron
(
struct
mtd_info
*
mtd
,
int
retry_mode
)
static
int
nand_setup_read_retry_micron
(
struct
mtd_info
*
mtd
,
int
retry_mode
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
uint8_t
feature
[
ONFI_SUBFEATURE_PARAM_LEN
]
=
{
retry_mode
};
uint8_t
feature
[
ONFI_SUBFEATURE_PARAM_LEN
]
=
{
retry_mode
};
return
chip
->
onfi_set_features
(
mtd
,
chip
,
ONFI_FEATURE_ADDR_READ_RETRY
,
return
chip
->
onfi_set_features
(
mtd
,
chip
,
ONFI_FEATURE_ADDR_READ_RETRY
,
...
@@ -3640,14 +4024,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
...
@@ -3640,14 +4024,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
int
i
,
maf_idx
;
int
i
,
maf_idx
;
u8
id_data
[
8
];
u8
id_data
[
8
];
/* Select the device */
chip
->
select_chip
(
mtd
,
0
);
/*
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up.
* after power-up.
*/
*/
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
nand_reset
(
chip
,
0
);
/* Select the device */
chip
->
select_chip
(
mtd
,
0
);
/* Send the command for reading device ID */
/* Send the command for reading device ID */
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READID
,
0x00
,
-
1
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READID
,
0x00
,
-
1
);
...
@@ -3683,7 +4067,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
...
@@ -3683,7 +4067,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
if
(
find_full_id_nand
(
mtd
,
chip
,
type
,
id_data
,
&
busw
))
if
(
find_full_id_nand
(
mtd
,
chip
,
type
,
id_data
,
&
busw
))
goto
ident_done
;
goto
ident_done
;
}
else
if
(
*
dev_id
==
type
->
dev_id
)
{
}
else
if
(
*
dev_id
==
type
->
dev_id
)
{
break
;
break
;
}
}
}
}
...
@@ -3706,10 +4090,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
...
@@ -3706,10 +4090,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip
->
chipsize
=
(
uint64_t
)
type
->
chipsize
<<
20
;
chip
->
chipsize
=
(
uint64_t
)
type
->
chipsize
<<
20
;
if
(
!
type
->
pagesize
&&
chip
->
init_size
)
{
if
(
!
type
->
pagesize
)
{
/* Set the pagesize, oobsize, erasesize by the driver */
busw
=
chip
->
init_size
(
mtd
,
chip
,
id_data
);
}
else
if
(
!
type
->
pagesize
)
{
/* Decode parameters from extended ID */
/* Decode parameters from extended ID */
nand_decode_ext_id
(
mtd
,
chip
,
id_data
,
&
busw
);
nand_decode_ext_id
(
mtd
,
chip
,
id_data
,
&
busw
);
}
else
{
}
else
{
...
@@ -3731,7 +4112,7 @@ ident_done:
...
@@ -3731,7 +4112,7 @@ ident_done:
if
(
nand_manuf_ids
[
maf_idx
].
id
==
*
maf_id
)
if
(
nand_manuf_ids
[
maf_idx
].
id
==
*
maf_id
)
break
;
break
;
}
}
if
(
chip
->
options
&
NAND_BUSWIDTH_AUTO
)
{
if
(
chip
->
options
&
NAND_BUSWIDTH_AUTO
)
{
WARN_ON
(
chip
->
options
&
NAND_BUSWIDTH_16
);
WARN_ON
(
chip
->
options
&
NAND_BUSWIDTH_16
);
chip
->
options
|=
busw
;
chip
->
options
|=
busw
;
...
@@ -3773,8 +4154,8 @@ ident_done:
...
@@ -3773,8 +4154,8 @@ ident_done:
if
(
mtd
->
writesize
>
512
&&
chip
->
cmdfunc
==
nand_command
)
if
(
mtd
->
writesize
>
512
&&
chip
->
cmdfunc
==
nand_command
)
chip
->
cmdfunc
=
nand_command_lp
;
chip
->
cmdfunc
=
nand_command_lp
;
if
(
*
maf_id
==
NAND_MFR_MICRON
)
nandchip_micron_init
(
mtd
,
*
dev_id
);
if
(
*
maf_id
==
NAND_MFR_MICRON
)
nandchip_micron_init
(
mtd
,
*
dev_id
);
pr_info
(
"device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x
\n
"
,
pr_info
(
"device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x
\n
"
,
*
maf_id
,
*
dev_id
);
*
maf_id
,
*
dev_id
);
...
@@ -3794,6 +4175,154 @@ ident_done:
...
@@ -3794,6 +4175,154 @@ ident_done:
return
type
;
return
type
;
}
}
static
const
char
*
const
nand_ecc_modes
[]
=
{
[
NAND_ECC_NONE
]
=
"none"
,
[
NAND_ECC_SOFT
]
=
"soft"
,
[
NAND_ECC_HW
]
=
"hw"
,
[
NAND_ECC_HW_SYNDROME
]
=
"hw_syndrome"
,
[
NAND_ECC_HW_OOB_FIRST
]
=
"hw_oob_first"
,
};
static
int
of_get_nand_ecc_mode
(
struct
device_node
*
np
)
{
const
char
*
pm
;
int
err
,
i
;
err
=
of_property_read_string
(
np
,
"nand-ecc-mode"
,
&
pm
);
if
(
err
<
0
)
return
err
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
nand_ecc_modes
);
i
++
)
if
(
!
strcasecmp
(
pm
,
nand_ecc_modes
[
i
]))
return
i
;
/*
* For backward compatibility we support few obsoleted values that don't
* have their mappings into nand_ecc_modes_t anymore (they were merged
* with other enums).
*/
if
(
!
strcasecmp
(
pm
,
"soft_bch"
))
return
NAND_ECC_SOFT
;
return
-
ENODEV
;
}
static
const
char
*
const
nand_ecc_algos
[]
=
{
[
NAND_ECC_HAMMING
]
=
"hamming"
,
[
NAND_ECC_BCH
]
=
"bch"
,
};
static
int
of_get_nand_ecc_algo
(
struct
device_node
*
np
)
{
const
char
*
pm
;
int
err
,
i
;
err
=
of_property_read_string
(
np
,
"nand-ecc-algo"
,
&
pm
);
if
(
!
err
)
{
for
(
i
=
NAND_ECC_HAMMING
;
i
<
ARRAY_SIZE
(
nand_ecc_algos
);
i
++
)
if
(
!
strcasecmp
(
pm
,
nand_ecc_algos
[
i
]))
return
i
;
return
-
ENODEV
;
}
/*
* For backward compatibility we also read "nand-ecc-mode" checking
* for some obsoleted values that were specifying ECC algorithm.
*/
err
=
of_property_read_string
(
np
,
"nand-ecc-mode"
,
&
pm
);
if
(
err
<
0
)
return
err
;
if
(
!
strcasecmp
(
pm
,
"soft"
))
return
NAND_ECC_HAMMING
;
else
if
(
!
strcasecmp
(
pm
,
"soft_bch"
))
return
NAND_ECC_BCH
;
return
-
ENODEV
;
}
static
int
of_get_nand_ecc_step_size
(
struct
device_node
*
np
)
{
int
ret
;
u32
val
;
ret
=
of_property_read_u32
(
np
,
"nand-ecc-step-size"
,
&
val
);
return
ret
?
ret
:
val
;
}
static
int
of_get_nand_ecc_strength
(
struct
device_node
*
np
)
{
int
ret
;
u32
val
;
ret
=
of_property_read_u32
(
np
,
"nand-ecc-strength"
,
&
val
);
return
ret
?
ret
:
val
;
}
static
int
of_get_nand_bus_width
(
struct
device_node
*
np
)
{
u32
val
;
if
(
of_property_read_u32
(
np
,
"nand-bus-width"
,
&
val
))
return
8
;
switch
(
val
)
{
case
8
:
case
16
:
return
val
;
default:
return
-
EIO
;
}
}
static
bool
of_get_nand_on_flash_bbt
(
struct
device_node
*
np
)
{
return
of_property_read_bool
(
np
,
"nand-on-flash-bbt"
);
}
static
int
nand_dt_init
(
struct
nand_chip
*
chip
)
{
struct
device_node
*
dn
=
nand_get_flash_node
(
chip
);
int
ecc_mode
,
ecc_algo
,
ecc_strength
,
ecc_step
;
if
(
!
dn
)
return
0
;
if
(
of_get_nand_bus_width
(
dn
)
==
16
)
chip
->
options
|=
NAND_BUSWIDTH_16
;
if
(
of_get_nand_on_flash_bbt
(
dn
))
chip
->
bbt_options
|=
NAND_BBT_USE_FLASH
;
ecc_mode
=
of_get_nand_ecc_mode
(
dn
);
ecc_algo
=
of_get_nand_ecc_algo
(
dn
);
ecc_strength
=
of_get_nand_ecc_strength
(
dn
);
ecc_step
=
of_get_nand_ecc_step_size
(
dn
);
if
((
ecc_step
>=
0
&&
!
(
ecc_strength
>=
0
))
||
(
!
(
ecc_step
>=
0
)
&&
ecc_strength
>=
0
))
{
pr_err
(
"must set both strength and step size in DT
\n
"
);
return
-
EINVAL
;
}
if
(
ecc_mode
>=
0
)
chip
->
ecc
.
mode
=
ecc_mode
;
if
(
ecc_algo
>=
0
)
chip
->
ecc
.
algo
=
ecc_algo
;
if
(
ecc_strength
>=
0
)
chip
->
ecc
.
strength
=
ecc_strength
;
if
(
ecc_step
>
0
)
chip
->
ecc
.
size
=
ecc_step
;
if
(
of_property_read_bool
(
dn
,
"nand-ecc-maximize"
))
chip
->
ecc
.
options
|=
NAND_ECC_MAXIMIZE
;
return
0
;
}
/**
/**
* nand_scan_ident - [NAND Interface] Scan for the NAND device
* nand_scan_ident - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @mtd: MTD device structure
...
@@ -3803,15 +4332,31 @@ ident_done:
...
@@ -3803,15 +4332,31 @@ ident_done:
* This is the first phase of the normal nand_scan() function. It reads the
* This is the first phase of the normal nand_scan() function. It reads the
* flash ID and sets up MTD fields accordingly.
* flash ID and sets up MTD fields accordingly.
*
*
* The mtd->owner field must be set to the module of the caller.
*/
*/
int
nand_scan_ident
(
struct
mtd_info
*
mtd
,
int
maxchips
,
int
nand_scan_ident
(
struct
mtd_info
*
mtd
,
int
maxchips
,
struct
nand_flash_dev
*
table
)
struct
nand_flash_dev
*
table
)
{
{
int
i
,
nand_maf_id
,
nand_dev_id
;
int
i
,
nand_maf_id
,
nand_dev_id
;
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
struct
nand_flash_dev
*
type
;
struct
nand_flash_dev
*
type
;
int
ret
;
ret
=
nand_dt_init
(
chip
);
if
(
ret
)
return
ret
;
if
(
!
mtd
->
name
&&
mtd
->
dev
.
parent
)
mtd
->
name
=
dev_name
(
mtd
->
dev
.
parent
);
if
((
!
chip
->
cmdfunc
||
!
chip
->
select_chip
)
&&
!
chip
->
cmd_ctrl
)
{
/*
* Default functions assigned for chip_select() and
* cmdfunc() both expect cmd_ctrl() to be populated,
* so we need to check that that's the case
*/
pr_err
(
"chip.cmd_ctrl() callback is not provided"
);
return
-
EINVAL
;
}
/* Set the default functions */
/* Set the default functions */
nand_set_defaults
(
chip
,
chip
->
options
&
NAND_BUSWIDTH_16
);
nand_set_defaults
(
chip
,
chip
->
options
&
NAND_BUSWIDTH_16
);
...
@@ -3826,13 +4371,31 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
...
@@ -3826,13 +4371,31 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
return
PTR_ERR
(
type
);
return
PTR_ERR
(
type
);
}
}
/* Initialize the ->data_interface field. */
ret
=
nand_init_data_interface
(
chip
);
if
(
ret
)
return
ret
;
/*
* Setup the data interface correctly on the chip and controller side.
* This explicit call to nand_setup_data_interface() is only required
* for the first die, because nand_reset() has been called before
* ->data_interface and ->default_onfi_timing_mode were set.
* For the other dies, nand_reset() will automatically switch to the
* best mode for us.
*/
ret
=
nand_setup_data_interface
(
chip
);
if
(
ret
)
return
ret
;
chip
->
select_chip
(
mtd
,
-
1
);
chip
->
select_chip
(
mtd
,
-
1
);
/* Check for a chip array */
/* Check for a chip array */
for
(
i
=
1
;
i
<
maxchips
;
i
++
)
{
for
(
i
=
1
;
i
<
maxchips
;
i
++
)
{
chip
->
select_chip
(
mtd
,
i
);
/* See comment in nand_get_flash_type for reset */
/* See comment in nand_get_flash_type for reset */
chip
->
cmdfunc
(
mtd
,
NAND_CMD_RESET
,
-
1
,
-
1
);
nand_reset
(
chip
,
i
);
chip
->
select_chip
(
mtd
,
i
);
/* Send the command for reading device ID */
/* Send the command for reading device ID */
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READID
,
0x00
,
-
1
);
chip
->
cmdfunc
(
mtd
,
NAND_CMD_READID
,
0x00
,
-
1
);
/* Read manufacturer and device IDs */
/* Read manufacturer and device IDs */
...
@@ -3854,6 +4417,102 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
...
@@ -3854,6 +4417,102 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
}
}
EXPORT_SYMBOL
(
nand_scan_ident
);
EXPORT_SYMBOL
(
nand_scan_ident
);
static
int
nand_set_ecc_soft_ops
(
struct
mtd_info
*
mtd
)
{
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
if
(
WARN_ON
(
ecc
->
mode
!=
NAND_ECC_SOFT
))
return
-
EINVAL
;
switch
(
ecc
->
algo
)
{
case
NAND_ECC_HAMMING
:
ecc
->
calculate
=
nand_calculate_ecc
;
ecc
->
correct
=
nand_correct_data
;
ecc
->
read_page
=
nand_read_page_swecc
;
ecc
->
read_subpage
=
nand_read_subpage
;
ecc
->
write_page
=
nand_write_page_swecc
;
ecc
->
read_page_raw
=
nand_read_page_raw
;
ecc
->
write_page_raw
=
nand_write_page_raw
;
ecc
->
read_oob
=
nand_read_oob_std
;
ecc
->
write_oob
=
nand_write_oob_std
;
if
(
!
ecc
->
size
)
ecc
->
size
=
256
;
ecc
->
bytes
=
3
;
ecc
->
strength
=
1
;
return
0
;
case
NAND_ECC_BCH
:
if
(
!
mtd_nand_has_bch
())
{
WARN
(
1
,
"CONFIG_MTD_NAND_ECC_BCH not enabled
\n
"
);
return
-
EINVAL
;
}
ecc
->
calculate
=
nand_bch_calculate_ecc
;
ecc
->
correct
=
nand_bch_correct_data
;
ecc
->
read_page
=
nand_read_page_swecc
;
ecc
->
read_subpage
=
nand_read_subpage
;
ecc
->
write_page
=
nand_write_page_swecc
;
ecc
->
read_page_raw
=
nand_read_page_raw
;
ecc
->
write_page_raw
=
nand_write_page_raw
;
ecc
->
read_oob
=
nand_read_oob_std
;
ecc
->
write_oob
=
nand_write_oob_std
;
/*
* Board driver should supply ecc.size and ecc.strength
* values to select how many bits are correctable.
* Otherwise, default to 4 bits for large page devices.
*/
if
(
!
ecc
->
size
&&
(
mtd
->
oobsize
>=
64
))
{
ecc
->
size
=
512
;
ecc
->
strength
=
4
;
}
/*
* if no ecc placement scheme was provided pickup the default
* large page one.
*/
if
(
!
mtd
->
ooblayout
)
{
/* handle large page devices only */
if
(
mtd
->
oobsize
<
64
)
{
WARN
(
1
,
"OOB layout is required when using software BCH on small pages
\n
"
);
return
-
EINVAL
;
}
mtd_set_ooblayout
(
mtd
,
&
nand_ooblayout_lp_ops
);
}
/*
* We can only maximize ECC config when the default layout is
* used, otherwise we don't know how many bytes can really be
* used.
*/
if
(
mtd
->
ooblayout
==
&
nand_ooblayout_lp_ops
&&
ecc
->
options
&
NAND_ECC_MAXIMIZE
)
{
int
steps
,
bytes
;
/* Always prefer 1k blocks over 512bytes ones */
ecc
->
size
=
1024
;
steps
=
mtd
->
writesize
/
ecc
->
size
;
/* Reserve 2 bytes for the BBM */
bytes
=
(
mtd
->
oobsize
-
2
)
/
steps
;
ecc
->
strength
=
bytes
*
8
/
fls
(
8
*
ecc
->
size
);
}
/* See nand_bch_init() for details. */
ecc
->
bytes
=
0
;
ecc
->
priv
=
nand_bch_init
(
mtd
);
if
(
!
ecc
->
priv
)
{
WARN
(
1
,
"BCH ECC initialization failed!
\n
"
);
return
-
EINVAL
;
}
return
0
;
default:
WARN
(
1
,
"Unsupported ECC algorithm!
\n
"
);
return
-
EINVAL
;
}
}
/*
/*
* Check if the chip configuration meet the datasheet requirements.
* Check if the chip configuration meet the datasheet requirements.
...
@@ -3870,7 +4529,7 @@ EXPORT_SYMBOL(nand_scan_ident);
...
@@ -3870,7 +4529,7 @@ EXPORT_SYMBOL(nand_scan_ident);
*/
*/
static
bool
nand_ecc_strength_good
(
struct
mtd_info
*
mtd
)
static
bool
nand_ecc_strength_good
(
struct
mtd_info
*
mtd
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_chip
*
chip
=
mtd
_to_nand
(
mtd
)
;
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
int
corr
,
ds_corr
;
int
corr
,
ds_corr
;
...
@@ -3898,14 +4557,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
...
@@ -3898,14 +4557,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
*/
*/
int
nand_scan_tail
(
struct
mtd_info
*
mtd
)
int
nand_scan_tail
(
struct
mtd_info
*
mtd
)
{
{
int
i
;
struct
nand_chip
*
chip
=
mtd_to_nand
(
mtd
);
struct
nand_chip
*
chip
=
mtd
->
priv
;
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
struct
nand_ecc_ctrl
*
ecc
=
&
chip
->
ecc
;
struct
nand_buffers
*
nbuf
;
struct
nand_buffers
*
nbuf
;
int
ret
;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
BUG_ON
((
chip
->
bbt_options
&
NAND_BBT_NO_OOB_BBM
)
&&
if
(
WARN_ON
((
chip
->
bbt_options
&
NAND_BBT_NO_OOB_BBM
)
&&
!
(
chip
->
bbt_options
&
NAND_BBT_USE_FLASH
));
!
(
chip
->
bbt_options
&
NAND_BBT_USE_FLASH
)))
return
-
EINVAL
;
if
(
!
(
chip
->
options
&
NAND_OWN_BUFFERS
))
{
if
(
!
(
chip
->
options
&
NAND_OWN_BUFFERS
))
{
nbuf
=
kzalloc
(
sizeof
(
*
nbuf
)
+
mtd
->
writesize
nbuf
=
kzalloc
(
sizeof
(
*
nbuf
)
+
mtd
->
writesize
...
@@ -3928,24 +4588,22 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -3928,24 +4588,22 @@ int nand_scan_tail(struct mtd_info *mtd)
/*
/*
* If no default placement scheme is given, select an appropriate one.
* If no default placement scheme is given, select an appropriate one.
*/
*/
if
(
!
ecc
->
layout
&&
(
ecc
->
mode
!=
NAND_ECC_SOFT_BCH
))
{
if
(
!
mtd
->
ooblayout
&&
!
(
ecc
->
mode
==
NAND_ECC_SOFT
&&
ecc
->
algo
==
NAND_ECC_BCH
))
{
switch
(
mtd
->
oobsize
)
{
switch
(
mtd
->
oobsize
)
{
case
8
:
case
8
:
ecc
->
layout
=
&
nand_oob_8
;
break
;
case
16
:
case
16
:
ecc
->
layout
=
&
nand_oob_16
;
mtd_set_ooblayout
(
mtd
,
&
nand_ooblayout_sp_ops
)
;
break
;
break
;
case
64
:
case
64
:
ecc
->
layout
=
&
nand_oob_64
;
break
;
case
128
:
case
128
:
ecc
->
layout
=
&
nand_oob_128
;
mtd_set_ooblayout
(
mtd
,
&
nand_ooblayout_lp_ops
)
;
break
;
break
;
default:
default:
pr_warn
(
"No oob scheme defined for oobsize %d
\n
"
,
WARN
(
1
,
"No oob scheme defined for oobsize %d
\n
"
,
mtd
->
oobsize
);
mtd
->
oobsize
);
BUG
();
ret
=
-
EINVAL
;
goto
err_free
;
}
}
}
}
...
@@ -3961,8 +4619,9 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -3961,8 +4619,9 @@ int nand_scan_tail(struct mtd_info *mtd)
case
NAND_ECC_HW_OOB_FIRST
:
case
NAND_ECC_HW_OOB_FIRST
:
/* Similar to NAND_ECC_HW, but a separate read_page handle */
/* Similar to NAND_ECC_HW, but a separate read_page handle */
if
(
!
ecc
->
calculate
||
!
ecc
->
correct
||
!
ecc
->
hwctl
)
{
if
(
!
ecc
->
calculate
||
!
ecc
->
correct
||
!
ecc
->
hwctl
)
{
pr_warn
(
"No ECC functions supplied; hardware ECC not possible
\n
"
);
WARN
(
1
,
"No ECC functions supplied; hardware ECC not possible
\n
"
);
BUG
();
ret
=
-
EINVAL
;
goto
err_free
;
}
}
if
(
!
ecc
->
read_page
)
if
(
!
ecc
->
read_page
)
ecc
->
read_page
=
nand_read_page_hwecc_oob_first
;
ecc
->
read_page
=
nand_read_page_hwecc_oob_first
;
...
@@ -3983,7 +4642,7 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -3983,7 +4642,7 @@ int nand_scan_tail(struct mtd_info *mtd)
ecc
->
write_oob
=
nand_write_oob_std
;
ecc
->
write_oob
=
nand_write_oob_std
;
if
(
!
ecc
->
read_subpage
)
if
(
!
ecc
->
read_subpage
)
ecc
->
read_subpage
=
nand_read_subpage
;
ecc
->
read_subpage
=
nand_read_subpage
;
if
(
!
ecc
->
write_subpage
)
if
(
!
ecc
->
write_subpage
&&
ecc
->
hwctl
&&
ecc
->
calculate
)
ecc
->
write_subpage
=
nand_write_subpage_hwecc
;
ecc
->
write_subpage
=
nand_write_subpage_hwecc
;
case
NAND_ECC_HW_SYNDROME
:
case
NAND_ECC_HW_SYNDROME
:
...
@@ -3992,8 +4651,9 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -3992,8 +4651,9 @@ int nand_scan_tail(struct mtd_info *mtd)
ecc
->
read_page
==
nand_read_page_hwecc
||
ecc
->
read_page
==
nand_read_page_hwecc
||
!
ecc
->
write_page
||
!
ecc
->
write_page
||
ecc
->
write_page
==
nand_write_page_hwecc
))
{
ecc
->
write_page
==
nand_write_page_hwecc
))
{
pr_warn
(
"No ECC functions supplied; hardware ECC not possible
\n
"
);
WARN
(
1
,
"No ECC functions supplied; hardware ECC not possible
\n
"
);
BUG
();
ret
=
-
EINVAL
;
goto
err_free
;
}
}
/* Use standard syndrome read/write page function? */
/* Use standard syndrome read/write page function? */
if
(
!
ecc
->
read_page
)
if
(
!
ecc
->
read_page
)
...
@@ -4011,63 +4671,22 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -4011,63 +4671,22 @@ int nand_scan_tail(struct mtd_info *mtd)
if
(
mtd
->
writesize
>=
ecc
->
size
)
{
if
(
mtd
->
writesize
>=
ecc
->
size
)
{
if
(
!
ecc
->
strength
)
{
if
(
!
ecc
->
strength
)
{
pr_warn
(
"Driver must set ecc.strength when using hardware ECC
\n
"
);
WARN
(
1
,
"Driver must set ecc.strength when using hardware ECC
\n
"
);
BUG
();
ret
=
-
EINVAL
;
goto
err_free
;
}
}
break
;
break
;
}
}
pr_warn
(
"%d byte HW ECC not possible on %d byte page size, fallback to SW ECC
\n
"
,
pr_warn
(
"%d byte HW ECC not possible on %d byte page size, fallback to SW ECC
\n
"
,
ecc
->
size
,
mtd
->
writesize
);
ecc
->
size
,
mtd
->
writesize
);
ecc
->
mode
=
NAND_ECC_SOFT
;
ecc
->
mode
=
NAND_ECC_SOFT
;
ecc
->
algo
=
NAND_ECC_HAMMING
;
case
NAND_ECC_SOFT
:
case
NAND_ECC_SOFT
:
ecc
->
calculate
=
nand_calculate_ecc
;
ret
=
nand_set_ecc_soft_ops
(
mtd
);
ecc
->
correct
=
nand_correct_data
;
if
(
ret
)
{
ecc
->
read_page
=
nand_read_page_swecc
;
ret
=
-
EINVAL
;
ecc
->
read_subpage
=
nand_read_subpage
;
goto
err_free
;
ecc
->
write_page
=
nand_write_page_swecc
;
ecc
->
read_page_raw
=
nand_read_page_raw
;
ecc
->
write_page_raw
=
nand_write_page_raw
;
ecc
->
read_oob
=
nand_read_oob_std
;
ecc
->
write_oob
=
nand_write_oob_std
;
if
(
!
ecc
->
size
)
ecc
->
size
=
256
;
ecc
->
bytes
=
3
;
ecc
->
strength
=
1
;
break
;
case
NAND_ECC_SOFT_BCH
:
if
(
!
mtd_nand_has_bch
())
{
pr_warn
(
"CONFIG_MTD_NAND_ECC_BCH not enabled
\n
"
);
BUG
();
}
ecc
->
calculate
=
nand_bch_calculate_ecc
;
ecc
->
correct
=
nand_bch_correct_data
;
ecc
->
read_page
=
nand_read_page_swecc
;
ecc
->
read_subpage
=
nand_read_subpage
;
ecc
->
write_page
=
nand_write_page_swecc
;
ecc
->
read_page_raw
=
nand_read_page_raw
;
ecc
->
write_page_raw
=
nand_write_page_raw
;
ecc
->
read_oob
=
nand_read_oob_std
;
ecc
->
write_oob
=
nand_write_oob_std
;
/*
* Board driver should supply ecc.size and ecc.strength values
* to select how many bits are correctable. Otherwise, default
* to 4 bits for large page devices.
*/
if
(
!
ecc
->
size
&&
(
mtd
->
oobsize
>=
64
))
{
ecc
->
size
=
512
;
ecc
->
strength
=
4
;
}
/* See nand_bch_init() for details. */
ecc
->
bytes
=
DIV_ROUND_UP
(
ecc
->
strength
*
fls
(
8
*
ecc
->
size
),
8
);
ecc
->
priv
=
nand_bch_init
(
mtd
,
ecc
->
size
,
ecc
->
bytes
,
&
ecc
->
layout
);
if
(
!
ecc
->
priv
)
{
pr_warn
(
"BCH ECC initialization failed!
\n
"
);
BUG
();
}
}
break
;
break
;
...
@@ -4085,8 +4704,9 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -4085,8 +4704,9 @@ int nand_scan_tail(struct mtd_info *mtd)
break
;
break
;
default:
default:
pr_warn
(
"Invalid NAND_ECC_MODE %d
\n
"
,
ecc
->
mode
);
WARN
(
1
,
"Invalid NAND_ECC_MODE %d
\n
"
,
ecc
->
mode
);
BUG
();
ret
=
-
EINVAL
;
goto
err_free
;
}
}
/* For many systems, the standard OOB write also works for raw */
/* For many systems, the standard OOB write also works for raw */
...
@@ -4095,20 +4715,9 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -4095,20 +4715,9 @@ int nand_scan_tail(struct mtd_info *mtd)
if
(
!
ecc
->
write_oob_raw
)
if
(
!
ecc
->
write_oob_raw
)
ecc
->
write_oob_raw
=
ecc
->
write_oob
;
ecc
->
write_oob_raw
=
ecc
->
write_oob
;
/*
/* propagate ecc info to mtd_info */
* The number of bytes available for a client to place data into
mtd
->
ecc_strength
=
ecc
->
strength
;
* the out of band area.
mtd
->
ecc_step_size
=
ecc
->
size
;
*/
ecc
->
layout
->
oobavail
=
0
;
for
(
i
=
0
;
ecc
->
layout
->
oobfree
[
i
].
length
&&
i
<
ARRAY_SIZE
(
ecc
->
layout
->
oobfree
);
i
++
)
ecc
->
layout
->
oobavail
+=
ecc
->
layout
->
oobfree
[
i
].
length
;
mtd
->
oobavail
=
ecc
->
layout
->
oobavail
;
/* ECC sanity check: warn if it's too weak */
if
(
!
nand_ecc_strength_good
(
mtd
))
pr_warn
(
"WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip
\n
"
,
mtd
->
name
);
/*
/*
* Set the number of read / write steps for one page depending on ECC
* Set the number of read / write steps for one page depending on ECC
...
@@ -4116,11 +4725,27 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -4116,11 +4725,27 @@ int nand_scan_tail(struct mtd_info *mtd)
*/
*/
ecc
->
steps
=
mtd
->
writesize
/
ecc
->
size
;
ecc
->
steps
=
mtd
->
writesize
/
ecc
->
size
;
if
(
ecc
->
steps
*
ecc
->
size
!=
mtd
->
writesize
)
{
if
(
ecc
->
steps
*
ecc
->
size
!=
mtd
->
writesize
)
{
pr_warn
(
"Invalid ECC parameters
\n
"
);
WARN
(
1
,
"Invalid ECC parameters
\n
"
);
BUG
();
ret
=
-
EINVAL
;
goto
err_free
;
}
}
ecc
->
total
=
ecc
->
steps
*
ecc
->
bytes
;
ecc
->
total
=
ecc
->
steps
*
ecc
->
bytes
;
/*
* The number of bytes available for a client to place data into
* the out of band area.
*/
ret
=
mtd_ooblayout_count_freebytes
(
mtd
);
if
(
ret
<
0
)
ret
=
0
;
mtd
->
oobavail
=
ret
;
/* ECC sanity check: warn if it's too weak */
if
(
!
nand_ecc_strength_good
(
mtd
))
pr_warn
(
"WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip
\n
"
,
mtd
->
name
);
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if
(
!
(
chip
->
options
&
NAND_NO_SUBPAGE_WRITE
)
&&
nand_is_slc
(
chip
))
{
if
(
!
(
chip
->
options
&
NAND_NO_SUBPAGE_WRITE
)
&&
nand_is_slc
(
chip
))
{
switch
(
ecc
->
steps
)
{
switch
(
ecc
->
steps
)
{
...
@@ -4145,7 +4770,6 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -4145,7 +4770,6 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Large page NAND with SOFT_ECC should support subpage reads */
/* Large page NAND with SOFT_ECC should support subpage reads */
switch
(
ecc
->
mode
)
{
switch
(
ecc
->
mode
)
{
case
NAND_ECC_SOFT
:
case
NAND_ECC_SOFT
:
case
NAND_ECC_SOFT_BCH
:
if
(
chip
->
page_shift
>
9
)
if
(
chip
->
page_shift
>
9
)
chip
->
options
|=
NAND_SUBPAGE_READ
;
chip
->
options
|=
NAND_SUBPAGE_READ
;
break
;
break
;
...
@@ -4177,10 +4801,6 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -4177,10 +4801,6 @@ int nand_scan_tail(struct mtd_info *mtd)
mtd
->
_block_markbad
=
nand_block_markbad
;
mtd
->
_block_markbad
=
nand_block_markbad
;
mtd
->
writebufsize
=
mtd
->
writesize
;
mtd
->
writebufsize
=
mtd
->
writesize
;
/* propagate ecc info to mtd_info */
mtd
->
ecclayout
=
ecc
->
layout
;
mtd
->
ecc_strength
=
ecc
->
strength
;
mtd
->
ecc_step_size
=
ecc
->
size
;
/*
/*
* Initialize bitflip_threshold to its default prior scan_bbt() call.
* Initialize bitflip_threshold to its default prior scan_bbt() call.
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
...
@@ -4195,6 +4815,10 @@ int nand_scan_tail(struct mtd_info *mtd)
...
@@ -4195,6 +4815,10 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Build bad block table */
/* Build bad block table */
return
chip
->
scan_bbt
(
mtd
);
return
chip
->
scan_bbt
(
mtd
);
err_free:
if
(
!
(
chip
->
options
&
NAND_OWN_BUFFERS
))
kfree
(
chip
->
buffers
);
return
ret
;
}
}
EXPORT_SYMBOL
(
nand_scan_tail
);
EXPORT_SYMBOL
(
nand_scan_tail
);
...
@@ -4217,19 +4841,12 @@ EXPORT_SYMBOL(nand_scan_tail);
...
@@ -4217,19 +4841,12 @@ EXPORT_SYMBOL(nand_scan_tail);
*
*
* This fills out all the uninitialized function pointers with the defaults.
* This fills out all the uninitialized function pointers with the defaults.
* The flash ID is read and the mtd/chip structures are filled with the
* The flash ID is read and the mtd/chip structures are filled with the
* appropriate values. The mtd->owner field must be set to the module of the
* appropriate values.
* caller.
*/
*/
int
nand_scan
(
struct
mtd_info
*
mtd
,
int
maxchips
)
int
nand_scan
(
struct
mtd_info
*
mtd
,
int
maxchips
)
{
{
int
ret
;
int
ret
;
/* Many callers got this wrong, so check for it for a while... */
if
(
!
mtd
->
owner
&&
caller_is_module
())
{
pr_crit
(
"%s called with NULL mtd->owner!
\n
"
,
__func__
);
BUG
();
}
ret
=
nand_scan_ident
(
mtd
,
maxchips
,
NULL
);
ret
=
nand_scan_ident
(
mtd
,
maxchips
,
NULL
);
if
(
!
ret
)
if
(
!
ret
)
ret
=
nand_scan_tail
(
mtd
);
ret
=
nand_scan_tail
(
mtd
);
...
@@ -4238,17 +4855,16 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
...
@@ -4238,17 +4855,16 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
EXPORT_SYMBOL
(
nand_scan
);
EXPORT_SYMBOL
(
nand_scan
);
/**
/**
* nand_
release
- [NAND Interface] Free resources held by the NAND device
* nand_
cleanup
- [NAND Interface] Free resources held by the NAND device
* @
mtd: MTD device structure
* @
chip: NAND chip object
*/
*/
void
nand_
release
(
struct
mtd_info
*
mtd
)
void
nand_
cleanup
(
struct
nand_chip
*
chip
)
{
{
struct
nand_chip
*
chip
=
mtd
->
priv
;
if
(
chip
->
ecc
.
mode
==
NAND_ECC_SOFT
&&
chip
->
ecc
.
algo
==
NAND_ECC_BCH
)
if
(
chip
->
ecc
.
mode
==
NAND_ECC_SOFT_BCH
)
nand_bch_free
((
struct
nand_bch_control
*
)
chip
->
ecc
.
priv
);
nand_bch_free
((
struct
nand_bch_control
*
)
chip
->
ecc
.
priv
);
mtd_device_unregister
(
mtd
);
nand_release_data_interface
(
chip
);
/* Free bad block table memory */
/* Free bad block table memory */
kfree
(
chip
->
bbt
);
kfree
(
chip
->
bbt
);
...
@@ -4260,21 +4876,19 @@ void nand_release(struct mtd_info *mtd)
...
@@ -4260,21 +4876,19 @@ void nand_release(struct mtd_info *mtd)
&
NAND_BBT_DYNAMICSTRUCT
)
&
NAND_BBT_DYNAMICSTRUCT
)
kfree
(
chip
->
badblock_pattern
);
kfree
(
chip
->
badblock_pattern
);
}
}
EXPORT_SYMBOL_GPL
(
nand_release
);
EXPORT_SYMBOL_GPL
(
nand_cleanup
);
static
int
__init
nand_base_init
(
void
)
{
led_trigger_register_simple
(
"nand-disk"
,
&
nand_led_trigger
);
return
0
;
}
static
void
__exit
nand_base_exit
(
void
)
/**
* nand_release - [NAND Interface] Unregister the MTD device and free resources
* held by the NAND device
* @mtd: MTD device structure
*/
void
nand_release
(
struct
mtd_info
*
mtd
)
{
{
led_trigger_unregister_simple
(
nand_led_trigger
);
mtd_device_unregister
(
mtd
);
nand_cleanup
(
mtd_to_nand
(
mtd
));
}
}
EXPORT_SYMBOL_GPL
(
nand_release
);
module_init
(
nand_base_init
);
module_exit
(
nand_base_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Steven J. Hill <sjhill@realitydiluted.com>"
);
MODULE_AUTHOR
(
"Steven J. Hill <sjhill@realitydiluted.com>"
);
...
...
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