This pull request contains the following notable changes:
- new tango NAND controller driver - new ox820 NAND controller driver - addition of a new full-ID entry in the nand_ids table - rework of the s3c240 driver to support DT - extension of the nand_sdr_timings to expose tCCS, tPROG and tR - addition of a new flag to ask the core to wait for tCCS when sending a RNDIN/RNDOUT command - addition of a new flag to ask the core to let the controller driver send the READ/PROGPAGE command This pull request also contains minor fixes/cleanup/cosmetic changes: - properly support 512 ECC step size in the sunxi driver - improve the error messages in the pxa probe path - fix module autoload in the omap2 driver - cleanup of several nand drivers to return nand_scan{_tail}() error code instead of returning -EIO - various cleanups in the denali driver - cleanups in the ooblayout handling (MTD core) - fix an error check in nandsim -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJYOu/YAAoJEGXtNgF+CLcAVZ4P/0Vi50SSLZ2EUOQqAbiQcUPV iO/20OPkTAXz6+Mi/csKZEnPGqpy2srn8MOgJx0gVO7b7hrhli6aBQJDxHLhGHVl N1hpTkZewZisLC1ewc9SA2SF/6zs93Bfk9cw0i4YxgE2gKPvZWT89Y9aZ/m/OWyW su0kis5YhMcAz2Oqq6oaCfRmbcMR62KkcjfB4U17FgXK56C5ziO7MoLjeAyYpi+f vHGzWFoDKHLfo4ISFq5inqB8pk6nQtkaERhx+y4WCwOcvMoOjOU9gWN1yyLyLsdp jimqdJegRlC63RkLoO5KNJqa3AlSTIpccIJaL2p8yHf/dtJQmhHXOl/gKgiO217n LMmeVJhMqUA652BJeXnyqg4VScTQFhwITccUFFauMEiRCzJWM6W1uzJZiGoWsrLG aKBYlWv+Z1dPGILf5AQobz16FuEsdVc60Fc4M02sL4QiH9TO2mg103licgiYdPIw 0YcJ23t7KDf66uyGKvZwcDJSkJEKPkf5YEDi7VOFZF4CuaYKMqMo7oCb8PTKNMBP tMQpQ9cjaVBGv/vXHneBsyin/1wsKN0tAnbuh/cUT1sGa+JiEjGdeCm9o30G3334 9YUZrPCwcN6mw5rOrD7Ts33OYONDAqCHJoP5IZovD8pbguEYAYZ2KFyHgu2KRQa3 kMVq7QE0jvn956SVw1uD =og/Y -----END PGP SIGNATURE----- Merge tag 'nand/for-4.10' of github.com:linux-nand/linux From Boris Brezillon: """ This pull request contains the following notable changes: - new tango NAND controller driver - new ox820 NAND controller driver - addition of a new full-ID entry in the nand_ids table - rework of the s3c240 driver to support DT - extension of the nand_sdr_timings to expose tCCS, tPROG and tR - addition of a new flag to ask the core to wait for tCCS when sending a RNDIN/RNDOUT command - addition of a new flag to ask the core to let the controller driver send the READ/PROGPAGE command This pull request also contains minor fixes/cleanup/cosmetic changes: - properly support 512 ECC step size in the sunxi driver - improve the error messages in the pxa probe path - fix module autoload in the omap2 driver - cleanup of several nand drivers to return nand_scan{_tail}() error code instead of returning -EIO - various cleanups in the denali driver - cleanups in the ooblayout handling (MTD core) - fix an error check in nandsim """
This commit is contained in:
commit
0989b0909c
|
@ -0,0 +1,41 @@
|
||||||
|
* Oxford Semiconductor OXNAS NAND Controller
|
||||||
|
|
||||||
|
Please refer to nand.txt for generic information regarding MTD NAND bindings.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "oxsemi,ox820-nand"
|
||||||
|
- reg: Base address and length for NAND mapped memory.
|
||||||
|
|
||||||
|
Optional Properties:
|
||||||
|
- clocks: phandle to the NAND gate clock if needed.
|
||||||
|
- resets: phandle to the NAND reset control if needed.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
nandc: nand-controller@41000000 {
|
||||||
|
compatible = "oxsemi,ox820-nand";
|
||||||
|
reg = <0x41000000 0x100000>;
|
||||||
|
clocks = <&stdclk CLK_820_NAND>;
|
||||||
|
resets = <&reset RESET_NAND>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
nand@0 {
|
||||||
|
reg = <0>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
nand-ecc-mode = "soft";
|
||||||
|
nand-ecc-algo = "hamming";
|
||||||
|
|
||||||
|
partition@0 {
|
||||||
|
label = "boot";
|
||||||
|
reg = <0x00000000 0x00e00000>;
|
||||||
|
read-only;
|
||||||
|
};
|
||||||
|
|
||||||
|
partition@e00000 {
|
||||||
|
label = "ubi";
|
||||||
|
reg = <0x00e00000 0x07200000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,56 @@
|
||||||
|
* Samsung S3C2410 and compatible NAND flash controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : The possible values are:
|
||||||
|
"samsung,s3c2410-nand"
|
||||||
|
"samsung,s3c2412-nand"
|
||||||
|
"samsung,s3c2440-nand"
|
||||||
|
- reg : register's location and length.
|
||||||
|
- #address-cells, #size-cells : see nand.txt
|
||||||
|
- clocks : phandle to the nand controller clock
|
||||||
|
- clock-names : must contain "nand"
|
||||||
|
|
||||||
|
Optional child nodes:
|
||||||
|
Child nodes representing the available nand chips.
|
||||||
|
|
||||||
|
Optional child properties:
|
||||||
|
- nand-ecc-mode : see nand.txt
|
||||||
|
- nand-on-flash-bbt : see nand.txt
|
||||||
|
|
||||||
|
Each child device node may optionally contain a 'partitions' sub-node,
|
||||||
|
which further contains sub-nodes describing the flash partition mapping.
|
||||||
|
See partition.txt for more detail.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
nand-controller@4e000000 {
|
||||||
|
compatible = "samsung,s3c2440-nand";
|
||||||
|
reg = <0x4e000000 0x40>;
|
||||||
|
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
clocks = <&clocks HCLK_NAND>;
|
||||||
|
clock-names = "nand";
|
||||||
|
|
||||||
|
nand {
|
||||||
|
nand-ecc-mode = "soft";
|
||||||
|
nand-on-flash-bbt;
|
||||||
|
|
||||||
|
partitions {
|
||||||
|
compatible = "fixed-partitions";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
partition@0 {
|
||||||
|
label = "u-boot";
|
||||||
|
reg = <0 0x040000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
partition@40000 {
|
||||||
|
label = "kernel";
|
||||||
|
reg = <0x040000 0x500000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,38 @@
|
||||||
|
Sigma Designs Tango4 NAND Flash Controller (NFC)
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible: "sigma,smp8758-nand"
|
||||||
|
- reg: address/size of nfc_reg, nfc_mem, and pbus_reg
|
||||||
|
- dmas: reference to the DMA channel used by the controller
|
||||||
|
- dma-names: "nfc_sbox"
|
||||||
|
- clocks: reference to the system clock
|
||||||
|
- #address-cells: <1>
|
||||||
|
- #size-cells: <0>
|
||||||
|
|
||||||
|
Children nodes represent the available NAND chips.
|
||||||
|
See Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
nandc: nand-controller@2c000 {
|
||||||
|
compatible = "sigma,smp8758-nand";
|
||||||
|
reg = <0x2c000 0x30 0x2d000 0x800 0x20000 0x1000>;
|
||||||
|
dmas = <&dma0 3>;
|
||||||
|
dma-names = "nfc_sbox";
|
||||||
|
clocks = <&clkgen SYS_CLK>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
nand@0 {
|
||||||
|
reg = <0>; /* CS0 */
|
||||||
|
nand-ecc-strength = <14>;
|
||||||
|
nand-ecc-step-size = <1024>;
|
||||||
|
};
|
||||||
|
|
||||||
|
nand@1 {
|
||||||
|
reg = <1>; /* CS1 */
|
||||||
|
nand-ecc-strength = <14>;
|
||||||
|
nand-ecc-step-size = <1024>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -171,6 +171,7 @@ static struct s3c2410_platform_nand smdk_nand_info = {
|
||||||
.twrph1 = 20,
|
.twrph1 = 20,
|
||||||
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
|
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
|
||||||
.sets = smdk_nand_sets,
|
.sets = smdk_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* devices we initialise */
|
/* devices we initialise */
|
||||||
|
|
|
@ -223,6 +223,7 @@ static struct s3c2410_platform_nand __initdata anubis_nand_info = {
|
||||||
.nr_sets = ARRAY_SIZE(anubis_nand_sets),
|
.nr_sets = ARRAY_SIZE(anubis_nand_sets),
|
||||||
.sets = anubis_nand_sets,
|
.sets = anubis_nand_sets,
|
||||||
.select_chip = anubis_nand_select,
|
.select_chip = anubis_nand_select,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* IDE channels */
|
/* IDE channels */
|
||||||
|
|
|
@ -114,6 +114,7 @@ static struct s3c2410_platform_nand __initdata at2440evb_nand_info = {
|
||||||
.twrph1 = 40,
|
.twrph1 = 40,
|
||||||
.nr_sets = ARRAY_SIZE(at2440evb_nand_sets),
|
.nr_sets = ARRAY_SIZE(at2440evb_nand_sets),
|
||||||
.sets = at2440evb_nand_sets,
|
.sets = at2440evb_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DM9000AEP 10/100 ethernet controller */
|
/* DM9000AEP 10/100 ethernet controller */
|
||||||
|
|
|
@ -299,6 +299,7 @@ static struct s3c2410_platform_nand __initdata bast_nand_info = {
|
||||||
.nr_sets = ARRAY_SIZE(bast_nand_sets),
|
.nr_sets = ARRAY_SIZE(bast_nand_sets),
|
||||||
.sets = bast_nand_sets,
|
.sets = bast_nand_sets,
|
||||||
.select_chip = bast_nand_select,
|
.select_chip = bast_nand_select,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DM9000 */
|
/* DM9000 */
|
||||||
|
|
|
@ -443,6 +443,7 @@ static struct s3c2410_platform_nand __initdata gta02_nand_info = {
|
||||||
.twrph1 = 15,
|
.twrph1 = 15,
|
||||||
.nr_sets = ARRAY_SIZE(gta02_nand_sets),
|
.nr_sets = ARRAY_SIZE(gta02_nand_sets),
|
||||||
.sets = gta02_nand_sets,
|
.sets = gta02_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -232,6 +232,7 @@ static struct s3c2410_platform_nand __initdata jive_nand_info = {
|
||||||
.twrph1 = 40,
|
.twrph1 = 40,
|
||||||
.sets = jive_nand_sets,
|
.sets = jive_nand_sets,
|
||||||
.nr_sets = ARRAY_SIZE(jive_nand_sets),
|
.nr_sets = ARRAY_SIZE(jive_nand_sets),
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init jive_mtdset(char *options)
|
static int __init jive_mtdset(char *options)
|
||||||
|
|
|
@ -287,6 +287,7 @@ static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
|
||||||
.nr_sets = ARRAY_SIZE(mini2440_nand_sets),
|
.nr_sets = ARRAY_SIZE(mini2440_nand_sets),
|
||||||
.sets = mini2440_nand_sets,
|
.sets = mini2440_nand_sets,
|
||||||
.ignore_unset_ecc = 1,
|
.ignore_unset_ecc = 1,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DM9000AEP 10/100 ethernet controller */
|
/* DM9000AEP 10/100 ethernet controller */
|
||||||
|
|
|
@ -238,6 +238,7 @@ static struct s3c2410_platform_nand __initdata osiris_nand_info = {
|
||||||
.nr_sets = ARRAY_SIZE(osiris_nand_sets),
|
.nr_sets = ARRAY_SIZE(osiris_nand_sets),
|
||||||
.sets = osiris_nand_sets,
|
.sets = osiris_nand_sets,
|
||||||
.select_chip = osiris_nand_select,
|
.select_chip = osiris_nand_select,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* PCMCIA control and configuration */
|
/* PCMCIA control and configuration */
|
||||||
|
|
|
@ -284,6 +284,7 @@ static struct s3c2410_platform_nand __initdata qt2410_nand_info = {
|
||||||
.twrph1 = 20,
|
.twrph1 = 20,
|
||||||
.nr_sets = ARRAY_SIZE(qt2410_nand_sets),
|
.nr_sets = ARRAY_SIZE(qt2410_nand_sets),
|
||||||
.sets = qt2410_nand_sets,
|
.sets = qt2410_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* UDC */
|
/* UDC */
|
||||||
|
|
|
@ -611,6 +611,7 @@ static struct s3c2410_platform_nand rx1950_nand_info = {
|
||||||
.twrph1 = 15,
|
.twrph1 = 15,
|
||||||
.nr_sets = ARRAY_SIZE(rx1950_nand_sets),
|
.nr_sets = ARRAY_SIZE(rx1950_nand_sets),
|
||||||
.sets = rx1950_nand_sets,
|
.sets = rx1950_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct s3c2410_udc_mach_info rx1950_udc_cfg __initdata = {
|
static struct s3c2410_udc_mach_info rx1950_udc_cfg __initdata = {
|
||||||
|
|
|
@ -164,6 +164,7 @@ static struct s3c2410_platform_nand __initdata rx3715_nand_info = {
|
||||||
.twrph1 = 15,
|
.twrph1 = 15,
|
||||||
.nr_sets = ARRAY_SIZE(rx3715_nand_sets),
|
.nr_sets = ARRAY_SIZE(rx3715_nand_sets),
|
||||||
.sets = rx3715_nand_sets,
|
.sets = rx3715_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *rx3715_devices[] __initdata = {
|
static struct platform_device *rx3715_devices[] __initdata = {
|
||||||
|
|
|
@ -117,6 +117,7 @@ static struct s3c2410_platform_nand __initdata vstms_nand_info = {
|
||||||
.twrph1 = 20,
|
.twrph1 = 20,
|
||||||
.nr_sets = ARRAY_SIZE(vstms_nand_sets),
|
.nr_sets = ARRAY_SIZE(vstms_nand_sets),
|
||||||
.sets = vstms_nand_sets,
|
.sets = vstms_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *vstms_devices[] __initdata = {
|
static struct platform_device *vstms_devices[] __initdata = {
|
||||||
|
|
|
@ -204,6 +204,7 @@ static struct s3c2410_platform_nand hmt_nand_info = {
|
||||||
.twrph1 = 40,
|
.twrph1 = 40,
|
||||||
.nr_sets = ARRAY_SIZE(hmt_nand_sets),
|
.nr_sets = ARRAY_SIZE(hmt_nand_sets),
|
||||||
.sets = hmt_nand_sets,
|
.sets = hmt_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct gpio_led hmt_leds[] = {
|
static struct gpio_led hmt_leds[] = {
|
||||||
|
|
|
@ -142,6 +142,7 @@ static struct s3c2410_platform_nand mini6410_nand_info = {
|
||||||
.twrph1 = 40,
|
.twrph1 = 40,
|
||||||
.nr_sets = ARRAY_SIZE(mini6410_nand_sets),
|
.nr_sets = ARRAY_SIZE(mini6410_nand_sets),
|
||||||
.sets = mini6410_nand_sets,
|
.sets = mini6410_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct s3c_fb_pd_win mini6410_lcd_type0_fb_win = {
|
static struct s3c_fb_pd_win mini6410_lcd_type0_fb_win = {
|
||||||
|
|
|
@ -194,6 +194,7 @@ static struct s3c2410_platform_nand real6410_nand_info = {
|
||||||
.twrph1 = 40,
|
.twrph1 = 40,
|
||||||
.nr_sets = ARRAY_SIZE(real6410_nand_sets),
|
.nr_sets = ARRAY_SIZE(real6410_nand_sets),
|
||||||
.sets = real6410_nand_sets,
|
.sets = real6410_nand_sets,
|
||||||
|
.ecc_mode = NAND_ECC_SOFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *real6410_devices[] __initdata = {
|
static struct platform_device *real6410_devices[] __initdata = {
|
||||||
|
|
|
@ -1274,8 +1274,8 @@ static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
|
||||||
int section,
|
int section,
|
||||||
struct mtd_oob_region *oobregion))
|
struct mtd_oob_region *oobregion))
|
||||||
{
|
{
|
||||||
struct mtd_oob_region oobregion = { };
|
struct mtd_oob_region oobregion;
|
||||||
int section = 0, ret;
|
int section, ret;
|
||||||
|
|
||||||
ret = mtd_ooblayout_find_region(mtd, start, §ion,
|
ret = mtd_ooblayout_find_region(mtd, start, §ion,
|
||||||
&oobregion, iter);
|
&oobregion, iter);
|
||||||
|
@ -1283,7 +1283,7 @@ static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
|
||||||
while (!ret) {
|
while (!ret) {
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
|
cnt = min_t(int, nbytes, oobregion.length);
|
||||||
memcpy(buf, oobbuf + oobregion.offset, cnt);
|
memcpy(buf, oobbuf + oobregion.offset, cnt);
|
||||||
buf += cnt;
|
buf += cnt;
|
||||||
nbytes -= cnt;
|
nbytes -= cnt;
|
||||||
|
@ -1317,8 +1317,8 @@ static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
|
||||||
int section,
|
int section,
|
||||||
struct mtd_oob_region *oobregion))
|
struct mtd_oob_region *oobregion))
|
||||||
{
|
{
|
||||||
struct mtd_oob_region oobregion = { };
|
struct mtd_oob_region oobregion;
|
||||||
int section = 0, ret;
|
int section, ret;
|
||||||
|
|
||||||
ret = mtd_ooblayout_find_region(mtd, start, §ion,
|
ret = mtd_ooblayout_find_region(mtd, start, §ion,
|
||||||
&oobregion, iter);
|
&oobregion, iter);
|
||||||
|
@ -1326,7 +1326,7 @@ static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
|
||||||
while (!ret) {
|
while (!ret) {
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
|
cnt = min_t(int, nbytes, oobregion.length);
|
||||||
memcpy(oobbuf + oobregion.offset, buf, cnt);
|
memcpy(oobbuf + oobregion.offset, buf, cnt);
|
||||||
buf += cnt;
|
buf += cnt;
|
||||||
nbytes -= cnt;
|
nbytes -= cnt;
|
||||||
|
@ -1354,7 +1354,7 @@ static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
|
||||||
int section,
|
int section,
|
||||||
struct mtd_oob_region *oobregion))
|
struct mtd_oob_region *oobregion))
|
||||||
{
|
{
|
||||||
struct mtd_oob_region oobregion = { };
|
struct mtd_oob_region oobregion;
|
||||||
int section = 0, ret, nbytes = 0;
|
int section = 0, ret, nbytes = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
|
@ -179,15 +179,6 @@ config MTD_NAND_S3C2410_DEBUG
|
||||||
help
|
help
|
||||||
Enable debugging of the S3C NAND driver
|
Enable debugging of the S3C NAND driver
|
||||||
|
|
||||||
config MTD_NAND_S3C2410_HWECC
|
|
||||||
bool "Samsung S3C NAND Hardware ECC"
|
|
||||||
depends on MTD_NAND_S3C2410
|
|
||||||
help
|
|
||||||
Enable the use of the controller's internal ECC generator when
|
|
||||||
using NAND. Early versions of the chips have had problems with
|
|
||||||
incorrect ECC generation, and if using these, the default of
|
|
||||||
software ECC is preferable.
|
|
||||||
|
|
||||||
config MTD_NAND_NDFC
|
config MTD_NAND_NDFC
|
||||||
tristate "NDFC NanD Flash Controller"
|
tristate "NDFC NanD Flash Controller"
|
||||||
depends on 4xx
|
depends on 4xx
|
||||||
|
@ -205,6 +196,13 @@ config MTD_NAND_S3C2410_CLKSTOP
|
||||||
when the is NAND chip selected or released, but will save
|
when the is NAND chip selected or released, but will save
|
||||||
approximately 5mA of power when there is nothing happening.
|
approximately 5mA of power when there is nothing happening.
|
||||||
|
|
||||||
|
config MTD_NAND_TANGO
|
||||||
|
tristate "NAND Flash support for Tango chips"
|
||||||
|
depends on ARCH_TANGO || COMPILE_TEST
|
||||||
|
depends on HAS_DMA
|
||||||
|
help
|
||||||
|
Enables the NAND Flash controller on Tango chips.
|
||||||
|
|
||||||
config MTD_NAND_DISKONCHIP
|
config MTD_NAND_DISKONCHIP
|
||||||
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
|
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
|
@ -426,6 +424,11 @@ config MTD_NAND_ORION
|
||||||
No board specific support is done by this driver, each board
|
No board specific support is done by this driver, each board
|
||||||
must advertise a platform_device for the driver to attach.
|
must advertise a platform_device for the driver to attach.
|
||||||
|
|
||||||
|
config MTD_NAND_OXNAS
|
||||||
|
tristate "NAND Flash support for Oxford Semiconductor SoC"
|
||||||
|
help
|
||||||
|
This enables the NAND flash controller on Oxford Semiconductor SoCs.
|
||||||
|
|
||||||
config MTD_NAND_FSL_ELBC
|
config MTD_NAND_FSL_ELBC
|
||||||
tristate "NAND support for Freescale eLBC controllers"
|
tristate "NAND support for Freescale eLBC controllers"
|
||||||
depends on FSL_SOC
|
depends on FSL_SOC
|
||||||
|
|
|
@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
|
||||||
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
|
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
|
||||||
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
|
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
|
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
||||||
obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
|
obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
|
||||||
|
@ -35,6 +36,7 @@ obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
|
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
|
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
|
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
|
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
|
obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
|
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
|
||||||
|
|
|
@ -234,10 +234,9 @@ static int ams_delta_init(struct platform_device *pdev)
|
||||||
goto out_gpio;
|
goto out_gpio;
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
/* Scan to find existence of the device */
|
||||||
if (nand_scan(ams_delta_mtd, 1)) {
|
err = nand_scan(ams_delta_mtd, 1);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto out_mtd;
|
goto out_mtd;
|
||||||
}
|
|
||||||
|
|
||||||
/* Register the partitions */
|
/* Register the partitions */
|
||||||
mtd_device_register(ams_delta_mtd, partition_info,
|
mtd_device_register(ams_delta_mtd, partition_info,
|
||||||
|
|
|
@ -2267,10 +2267,9 @@ static int atmel_nand_probe(struct platform_device *pdev)
|
||||||
dev_info(host->dev, "No DMA support for NAND access.\n");
|
dev_info(host->dev, "No DMA support for NAND access.\n");
|
||||||
|
|
||||||
/* first scan to find the device and get the page size */
|
/* first scan to find the device and get the page size */
|
||||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
res = nand_scan_ident(mtd, 1, NULL);
|
||||||
res = -ENXIO;
|
if (res)
|
||||||
goto err_scan_ident;
|
goto err_scan_ident;
|
||||||
}
|
|
||||||
|
|
||||||
if (host->board.on_flash_bbt || on_flash_bbt)
|
if (host->board.on_flash_bbt || on_flash_bbt)
|
||||||
nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
|
nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||||
|
@ -2304,10 +2303,9 @@ static int atmel_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* second phase scan */
|
/* second phase scan */
|
||||||
if (nand_scan_tail(mtd)) {
|
res = nand_scan_tail(mtd);
|
||||||
res = -ENXIO;
|
if (res)
|
||||||
goto err_scan_tail;
|
goto err_scan_tail;
|
||||||
}
|
|
||||||
|
|
||||||
mtd->name = "atmel_nand";
|
mtd->name = "atmel_nand";
|
||||||
res = mtd_device_register(mtd, host->board.parts,
|
res = mtd_device_register(mtd, host->board.parts,
|
||||||
|
|
|
@ -2209,8 +2209,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
|
||||||
nand_writereg(ctrl, cfg_offs,
|
nand_writereg(ctrl, cfg_offs,
|
||||||
nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
|
nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
|
||||||
|
|
||||||
if (nand_scan_ident(mtd, 1, NULL))
|
ret = nand_scan_ident(mtd, 1, NULL);
|
||||||
return -ENXIO;
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||||
/*
|
/*
|
||||||
|
@ -2234,8 +2235,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (nand_scan_tail(mtd))
|
ret = nand_scan_tail(mtd);
|
||||||
return -ENXIO;
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return mtd_device_register(mtd, NULL, 0);
|
return mtd_device_register(mtd, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -725,10 +725,9 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
||||||
usedma = 0;
|
usedma = 0;
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
/* Scan to find existence of the device */
|
||||||
if (nand_scan_ident(mtd, 2, NULL)) {
|
err = nand_scan_ident(mtd, 2, NULL);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto out_irq;
|
goto out_irq;
|
||||||
}
|
|
||||||
|
|
||||||
cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev,
|
cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev,
|
||||||
2112 + sizeof(struct nand_buffers) +
|
2112 + sizeof(struct nand_buffers) +
|
||||||
|
|
|
@ -195,9 +195,9 @@ static int __init cmx270_init(void)
|
||||||
this->write_buf = cmx270_write_buf;
|
this->write_buf = cmx270_write_buf;
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
/* Scan to find existence of the device */
|
||||||
if (nand_scan (cmx270_nand_mtd, 1)) {
|
ret = nand_scan(cmx270_nand_mtd, 1);
|
||||||
|
if (ret) {
|
||||||
pr_notice("No NAND device\n");
|
pr_notice("No NAND device\n");
|
||||||
ret = -ENXIO;
|
|
||||||
goto err_scan;
|
goto err_scan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -242,10 +242,9 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
/* Scan to find existence of the device */
|
||||||
if (nand_scan(new_mtd, 1)) {
|
err = nand_scan(new_mtd, 1);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
|
||||||
|
|
||||||
cs553x_mtd[cs] = new_mtd;
|
cs553x_mtd[cs] = new_mtd;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
@ -182,9 +181,6 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
|
|
||||||
__FILE__, __LINE__, __func__);
|
|
||||||
|
|
||||||
for (i = 0; i < denali->max_banks; i++)
|
for (i = 0; i < denali->max_banks; i++)
|
||||||
iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
|
iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
|
||||||
denali->flash_reg + INTR_STATUS(i));
|
denali->flash_reg + INTR_STATUS(i));
|
||||||
|
@ -234,9 +230,6 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali,
|
||||||
uint16_t acc_clks;
|
uint16_t acc_clks;
|
||||||
uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
|
uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
|
||||||
|
|
||||||
dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
|
|
||||||
__FILE__, __LINE__, __func__);
|
|
||||||
|
|
||||||
en_lo = CEIL_DIV(Trp[mode], CLK_X);
|
en_lo = CEIL_DIV(Trp[mode], CLK_X);
|
||||||
en_hi = CEIL_DIV(Treh[mode], CLK_X);
|
en_hi = CEIL_DIV(Treh[mode], CLK_X);
|
||||||
#if ONFI_BLOOM_TIME
|
#if ONFI_BLOOM_TIME
|
||||||
|
@ -403,7 +396,7 @@ static void get_hynix_nand_para(struct denali_nand_info *denali,
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_warn(denali->dev,
|
dev_warn(denali->dev,
|
||||||
"Spectra: Unknown Hynix NAND (Device ID: 0x%x).\n"
|
"Unknown Hynix NAND (Device ID: 0x%x).\n"
|
||||||
"Will use default parameter values instead.\n",
|
"Will use default parameter values instead.\n",
|
||||||
device_id);
|
device_id);
|
||||||
}
|
}
|
||||||
|
@ -474,33 +467,6 @@ static void detect_max_banks(struct denali_nand_info *denali)
|
||||||
denali->max_banks = 1 << (features & FEATURES__N_BANKS);
|
denali->max_banks = 1 << (features & FEATURES__N_BANKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void detect_partition_feature(struct denali_nand_info *denali)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* For MRST platform, denali->fwblks represent the
|
|
||||||
* number of blocks firmware is taken,
|
|
||||||
* FW is in protect partition and MTD driver has no
|
|
||||||
* permission to access it. So let driver know how many
|
|
||||||
* blocks it can't touch.
|
|
||||||
*/
|
|
||||||
if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) {
|
|
||||||
if ((ioread32(denali->flash_reg + PERM_SRC_ID(1)) &
|
|
||||||
PERM_SRC_ID__SRCID) == SPECTRA_PARTITION_ID) {
|
|
||||||
denali->fwblks =
|
|
||||||
((ioread32(denali->flash_reg + MIN_MAX_BANK(1)) &
|
|
||||||
MIN_MAX_BANK__MIN_VALUE) *
|
|
||||||
denali->blksperchip)
|
|
||||||
+
|
|
||||||
(ioread32(denali->flash_reg + MIN_BLK_ADDR(1)) &
|
|
||||||
MIN_BLK_ADDR__VALUE);
|
|
||||||
} else {
|
|
||||||
denali->fwblks = SPECTRA_START_BLOCK;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
denali->fwblks = SPECTRA_START_BLOCK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
||||||
{
|
{
|
||||||
uint16_t status = PASS;
|
uint16_t status = PASS;
|
||||||
|
@ -508,9 +474,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
||||||
uint8_t maf_id, device_id;
|
uint8_t maf_id, device_id;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
|
|
||||||
__FILE__, __LINE__, __func__);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use read id method to get device ID and other params.
|
* Use read id method to get device ID and other params.
|
||||||
* For some NAND chips, controller can't report the correct
|
* For some NAND chips, controller can't report the correct
|
||||||
|
@ -552,8 +515,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
||||||
|
|
||||||
find_valid_banks(denali);
|
find_valid_banks(denali);
|
||||||
|
|
||||||
detect_partition_feature(denali);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the user specified to override the default timings
|
* If the user specified to override the default timings
|
||||||
* with a specific ONFI mode, we apply those changes here.
|
* with a specific ONFI mode, we apply those changes here.
|
||||||
|
@ -567,9 +528,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
||||||
static void denali_set_intr_modes(struct denali_nand_info *denali,
|
static void denali_set_intr_modes(struct denali_nand_info *denali,
|
||||||
uint16_t INT_ENABLE)
|
uint16_t INT_ENABLE)
|
||||||
{
|
{
|
||||||
dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
|
|
||||||
__FILE__, __LINE__, __func__);
|
|
||||||
|
|
||||||
if (INT_ENABLE)
|
if (INT_ENABLE)
|
||||||
iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
|
iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
|
||||||
else
|
else
|
||||||
|
@ -605,7 +563,6 @@ static void denali_irq_init(struct denali_nand_info *denali)
|
||||||
static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
|
static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
|
||||||
{
|
{
|
||||||
denali_set_intr_modes(denali, false);
|
denali_set_intr_modes(denali, false);
|
||||||
free_irq(irqnum, denali);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void denali_irq_enable(struct denali_nand_info *denali,
|
static void denali_irq_enable(struct denali_nand_info *denali,
|
||||||
|
@ -1437,9 +1394,6 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
||||||
/* initialize driver data structures */
|
/* initialize driver data structures */
|
||||||
static void denali_drv_init(struct denali_nand_info *denali)
|
static void denali_drv_init(struct denali_nand_info *denali)
|
||||||
{
|
{
|
||||||
denali->idx = 0;
|
|
||||||
|
|
||||||
/* setup interrupt handler */
|
|
||||||
/*
|
/*
|
||||||
* the completion object will be used to notify
|
* the completion object will be used to notify
|
||||||
* the callee that the interrupt is done
|
* the callee that the interrupt is done
|
||||||
|
@ -1485,14 +1439,12 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
denali_hw_init(denali);
|
denali_hw_init(denali);
|
||||||
denali_drv_init(denali);
|
denali_drv_init(denali);
|
||||||
|
|
||||||
/*
|
/* Request IRQ after all the hardware initialization is finished */
|
||||||
* denali_isr register is done after all the hardware
|
ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
|
||||||
* initilization is finished
|
IRQF_SHARED, DENALI_NAND_NAME, denali);
|
||||||
*/
|
if (ret) {
|
||||||
if (request_irq(denali->irq, denali_isr, IRQF_SHARED,
|
dev_err(denali->dev, "Unable to request IRQ\n");
|
||||||
DENALI_NAND_NAME, denali)) {
|
return ret;
|
||||||
pr_err("Spectra: Unable to allocate IRQ\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now that our ISR is registered, we can enable interrupts */
|
/* now that our ISR is registered, we can enable interrupts */
|
||||||
|
@ -1510,10 +1462,9 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
* this is the first stage in a two step process to register
|
* this is the first stage in a two step process to register
|
||||||
* with the nand subsystem
|
* with the nand subsystem
|
||||||
*/
|
*/
|
||||||
if (nand_scan_ident(mtd, denali->max_banks, NULL)) {
|
ret = nand_scan_ident(mtd, denali->max_banks, NULL);
|
||||||
ret = -ENXIO;
|
if (ret)
|
||||||
goto failed_req_irq;
|
goto failed_req_irq;
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate the right size buffer now */
|
/* allocate the right size buffer now */
|
||||||
devm_kfree(denali->dev, denali->buf.buf);
|
devm_kfree(denali->dev, denali->buf.buf);
|
||||||
|
@ -1528,7 +1479,7 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
/* Is 32-bit DMA supported? */
|
/* Is 32-bit DMA supported? */
|
||||||
ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32));
|
ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Spectra: no usable DMA configuration\n");
|
dev_err(denali->dev, "No usable DMA configuration\n");
|
||||||
goto failed_req_irq;
|
goto failed_req_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1536,7 +1487,7 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
mtd->writesize + mtd->oobsize,
|
mtd->writesize + mtd->oobsize,
|
||||||
DMA_BIDIRECTIONAL);
|
DMA_BIDIRECTIONAL);
|
||||||
if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
|
if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
|
||||||
dev_err(denali->dev, "Spectra: failed to map DMA buffer\n");
|
dev_err(denali->dev, "Failed to map DMA buffer\n");
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto failed_req_irq;
|
goto failed_req_irq;
|
||||||
}
|
}
|
||||||
|
@ -1547,16 +1498,16 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
* the real pagesize and anything necessery
|
* the real pagesize and anything necessery
|
||||||
*/
|
*/
|
||||||
denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
|
denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
|
||||||
denali->nand.chipsize <<= (denali->devnum - 1);
|
denali->nand.chipsize <<= denali->devnum - 1;
|
||||||
denali->nand.page_shift += (denali->devnum - 1);
|
denali->nand.page_shift += denali->devnum - 1;
|
||||||
denali->nand.pagemask = (denali->nand.chipsize >>
|
denali->nand.pagemask = (denali->nand.chipsize >>
|
||||||
denali->nand.page_shift) - 1;
|
denali->nand.page_shift) - 1;
|
||||||
denali->nand.bbt_erase_shift += (denali->devnum - 1);
|
denali->nand.bbt_erase_shift += denali->devnum - 1;
|
||||||
denali->nand.phys_erase_shift = denali->nand.bbt_erase_shift;
|
denali->nand.phys_erase_shift = denali->nand.bbt_erase_shift;
|
||||||
denali->nand.chip_shift += (denali->devnum - 1);
|
denali->nand.chip_shift += denali->devnum - 1;
|
||||||
mtd->writesize <<= (denali->devnum - 1);
|
mtd->writesize <<= denali->devnum - 1;
|
||||||
mtd->oobsize <<= (denali->devnum - 1);
|
mtd->oobsize <<= denali->devnum - 1;
|
||||||
mtd->erasesize <<= (denali->devnum - 1);
|
mtd->erasesize <<= denali->devnum - 1;
|
||||||
mtd->size = denali->nand.numchips * denali->nand.chipsize;
|
mtd->size = denali->nand.numchips * denali->nand.chipsize;
|
||||||
denali->bbtskipbytes *= denali->devnum;
|
denali->bbtskipbytes *= denali->devnum;
|
||||||
|
|
||||||
|
@ -1606,14 +1557,6 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
denali->nand.ecc.bytes *= denali->devnum;
|
denali->nand.ecc.bytes *= denali->devnum;
|
||||||
denali->nand.ecc.strength *= denali->devnum;
|
denali->nand.ecc.strength *= denali->devnum;
|
||||||
|
|
||||||
/*
|
|
||||||
* Let driver know the total blocks number and how many blocks
|
|
||||||
* contained by each nand chip. blksperchip will help driver to
|
|
||||||
* know how many blocks is taken by FW.
|
|
||||||
*/
|
|
||||||
denali->totalblks = mtd->size >> denali->nand.phys_erase_shift;
|
|
||||||
denali->blksperchip = denali->totalblks / denali->nand.numchips;
|
|
||||||
|
|
||||||
/* override the default read operations */
|
/* override the default read operations */
|
||||||
denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum;
|
denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum;
|
||||||
denali->nand.ecc.read_page = denali_read_page;
|
denali->nand.ecc.read_page = denali_read_page;
|
||||||
|
@ -1624,15 +1567,13 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
denali->nand.ecc.write_oob = denali_write_oob;
|
denali->nand.ecc.write_oob = denali_write_oob;
|
||||||
denali->nand.erase = denali_erase;
|
denali->nand.erase = denali_erase;
|
||||||
|
|
||||||
if (nand_scan_tail(mtd)) {
|
ret = nand_scan_tail(mtd);
|
||||||
ret = -ENXIO;
|
if (ret)
|
||||||
goto failed_req_irq;
|
goto failed_req_irq;
|
||||||
}
|
|
||||||
|
|
||||||
ret = mtd_device_register(mtd, NULL, 0);
|
ret = mtd_device_register(mtd, NULL, 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(denali->dev, "Spectra: Failed to register MTD: %d\n",
|
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
|
||||||
ret);
|
|
||||||
goto failed_req_irq;
|
goto failed_req_irq;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -383,14 +383,6 @@
|
||||||
#define CLK_X 5
|
#define CLK_X 5
|
||||||
#define CLK_MULTI 4
|
#define CLK_MULTI 4
|
||||||
|
|
||||||
/* spectraswconfig.h */
|
|
||||||
#define CMD_DMA 0
|
|
||||||
|
|
||||||
#define SPECTRA_PARTITION_ID 0
|
|
||||||
/**** Block Table and Reserved Block Parameters *****/
|
|
||||||
#define SPECTRA_START_BLOCK 3
|
|
||||||
#define NUM_FREE_BLOCKS_GATE 30
|
|
||||||
|
|
||||||
/* KBV - Updated to LNW scratch register address */
|
/* KBV - Updated to LNW scratch register address */
|
||||||
#define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
#define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||||
#define SCRATCH_REG_SIZE 64
|
#define SCRATCH_REG_SIZE 64
|
||||||
|
@ -467,13 +459,9 @@ struct denali_nand_info {
|
||||||
spinlock_t irq_lock;
|
spinlock_t irq_lock;
|
||||||
uint32_t irq_status;
|
uint32_t irq_status;
|
||||||
int irq_debug_array[32];
|
int irq_debug_array[32];
|
||||||
int idx;
|
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
uint32_t devnum; /* represent how many nands connected */
|
uint32_t devnum; /* represent how many nands connected */
|
||||||
uint32_t fwblks; /* represent how many blocks FW used */
|
|
||||||
uint32_t totalblks;
|
|
||||||
uint32_t blksperchip;
|
|
||||||
uint32_t bbtskipbytes;
|
uint32_t bbtskipbytes;
|
||||||
uint32_t max_banks;
|
uint32_t max_banks;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
|
|
||||||
#include "denali.h"
|
#include "denali.h"
|
||||||
|
|
||||||
|
@ -110,7 +109,7 @@ static int denali_dt_remove(struct platform_device *ofdev)
|
||||||
struct denali_dt *dt = platform_get_drvdata(ofdev);
|
struct denali_dt *dt = platform_get_drvdata(ofdev);
|
||||||
|
|
||||||
denali_remove(&dt->denali);
|
denali_remove(&dt->denali);
|
||||||
clk_disable(dt->clk);
|
clk_disable_unprepare(dt->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
|
|
||||||
#include "denali.h"
|
#include "denali.h"
|
||||||
|
|
||||||
|
|
|
@ -926,8 +926,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
/*
|
/*
|
||||||
* Scan to find existence of the device
|
* Scan to find existence of the device
|
||||||
*/
|
*/
|
||||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
ret = nand_scan_ident(mtd, 1, NULL);
|
||||||
ret = -ENXIO;
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "No NAND Device found!\n");
|
dev_err(&pdev->dev, "No NAND Device found!\n");
|
||||||
goto err_scan_ident;
|
goto err_scan_ident;
|
||||||
}
|
}
|
||||||
|
@ -992,10 +992,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Second stage of scan to fill MTD data-structures */
|
/* Second stage of scan to fill MTD data-structures */
|
||||||
if (nand_scan_tail(mtd)) {
|
ret = nand_scan_tail(mtd);
|
||||||
ret = -ENXIO;
|
if (ret)
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The partition information can is accessed by (in the same precedence)
|
* The partition information can is accessed by (in the same precedence)
|
||||||
|
|
|
@ -286,10 +286,9 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
||||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
||||||
gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
|
gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
|
||||||
|
|
||||||
if (nand_scan(mtd, 1)) {
|
ret = nand_scan(mtd, 1);
|
||||||
ret = -ENXIO;
|
if (ret)
|
||||||
goto err_wp;
|
goto err_wp;
|
||||||
}
|
|
||||||
|
|
||||||
if (gpiomtd->plat.adjust_parts)
|
if (gpiomtd->plat.adjust_parts)
|
||||||
gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size);
|
gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size);
|
||||||
|
|
|
@ -774,10 +774,8 @@ static int hisi_nfc_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nand_scan_ident(mtd, max_chips, NULL);
|
ret = nand_scan_ident(mtd, max_chips, NULL);
|
||||||
if (ret) {
|
if (ret)
|
||||||
ret = -ENODEV;
|
|
||||||
goto err_res;
|
goto err_res;
|
||||||
}
|
|
||||||
|
|
||||||
host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
|
host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
|
||||||
&host->dma_buffer, GFP_KERNEL);
|
&host->dma_buffer, GFP_KERNEL);
|
||||||
|
|
|
@ -747,10 +747,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
* Scan to find existance of the device and
|
* Scan to find existance of the device and
|
||||||
* Get the type of NAND device SMALL block or LARGE block
|
* Get the type of NAND device SMALL block or LARGE block
|
||||||
*/
|
*/
|
||||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
res = nand_scan_ident(mtd, 1, NULL);
|
||||||
res = -ENXIO;
|
if (res)
|
||||||
goto err_exit3;
|
goto err_exit3;
|
||||||
}
|
|
||||||
|
|
||||||
host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL);
|
host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL);
|
||||||
if (!host->dma_buf) {
|
if (!host->dma_buf) {
|
||||||
|
@ -793,10 +792,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
* Fills out all the uninitialized function pointers with the defaults
|
* Fills out all the uninitialized function pointers with the defaults
|
||||||
* And scans for a bad block table if appropriate.
|
* And scans for a bad block table if appropriate.
|
||||||
*/
|
*/
|
||||||
if (nand_scan_tail(mtd)) {
|
res = nand_scan_tail(mtd);
|
||||||
res = -ENXIO;
|
if (res)
|
||||||
goto err_exit4;
|
goto err_exit4;
|
||||||
}
|
|
||||||
|
|
||||||
mtd->name = DRV_NAME;
|
mtd->name = DRV_NAME;
|
||||||
|
|
||||||
|
|
|
@ -894,10 +894,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find NAND device */
|
/* Find NAND device */
|
||||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
res = nand_scan_ident(mtd, 1, NULL);
|
||||||
res = -ENXIO;
|
if (res)
|
||||||
goto err_exit3;
|
goto err_exit3;
|
||||||
}
|
|
||||||
|
|
||||||
/* OOB and ECC CPU and DMA work areas */
|
/* OOB and ECC CPU and DMA work areas */
|
||||||
host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE);
|
host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE);
|
||||||
|
@ -929,10 +928,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||||
/*
|
/*
|
||||||
* Fills out all the uninitialized function pointers with the defaults
|
* Fills out all the uninitialized function pointers with the defaults
|
||||||
*/
|
*/
|
||||||
if (nand_scan_tail(mtd)) {
|
res = nand_scan_tail(mtd);
|
||||||
res = -ENXIO;
|
if (res)
|
||||||
goto err_exit3;
|
goto err_exit3;
|
||||||
}
|
|
||||||
|
|
||||||
mtd->name = "nxp_lpc3220_slc";
|
mtd->name = "nxp_lpc3220_slc";
|
||||||
res = mtd_device_register(mtd, host->ncfg->parts,
|
res = mtd_device_register(mtd, host->ncfg->parts,
|
||||||
|
|
|
@ -777,9 +777,9 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detect NAND chips */
|
/* Detect NAND chips */
|
||||||
if (nand_scan(mtd, be32_to_cpup(chips_no))) {
|
retval = nand_scan(mtd, be32_to_cpup(chips_no));
|
||||||
|
if (retval) {
|
||||||
dev_err(dev, "NAND Flash not found !\n");
|
dev_err(dev, "NAND Flash not found !\n");
|
||||||
retval = -ENXIO;
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1297,7 +1297,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
|
||||||
|
|
||||||
ret = nand_scan_ident(mtd, nsels, NULL);
|
ret = nand_scan_ident(mtd, nsels, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return -ENODEV;
|
return ret;
|
||||||
|
|
||||||
/* store bbt magic in page, cause OOB is not protected */
|
/* store bbt magic in page, cause OOB is not protected */
|
||||||
if (nand->bbt_options & NAND_BBT_USE_FLASH)
|
if (nand->bbt_options & NAND_BBT_USE_FLASH)
|
||||||
|
@ -1323,7 +1323,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
|
||||||
|
|
||||||
ret = nand_scan_tail(mtd);
|
ret = nand_scan_tail(mtd);
|
||||||
if (ret)
|
if (ret)
|
||||||
return -ENODEV;
|
return ret;
|
||||||
|
|
||||||
ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
|
ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -1747,10 +1747,9 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* first scan to find the device and get the page size */
|
/* first scan to find the device and get the page size */
|
||||||
if (nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL)) {
|
err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto escan;
|
goto escan;
|
||||||
}
|
|
||||||
|
|
||||||
switch (this->ecc.mode) {
|
switch (this->ecc.mode) {
|
||||||
case NAND_ECC_HW:
|
case NAND_ECC_HW:
|
||||||
|
@ -1808,10 +1807,9 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* second phase scan */
|
/* second phase scan */
|
||||||
if (nand_scan_tail(mtd)) {
|
err = nand_scan_tail(mtd);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto escan;
|
goto escan;
|
||||||
}
|
|
||||||
|
|
||||||
/* Register the partitions */
|
/* Register the partitions */
|
||||||
mtd_device_parse_register(mtd, part_probes,
|
mtd_device_parse_register(mtd, part_probes,
|
||||||
|
|
|
@ -709,6 +709,25 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
|
||||||
nand_wait_ready(mtd);
|
nand_wait_ready(mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nand_ccs_delay(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The controller already takes care of waiting for tCCS when the RNDIN
|
||||||
|
* or RNDOUT command is sent, return directly.
|
||||||
|
*/
|
||||||
|
if (!(chip->options & NAND_WAIT_TCCS))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait tCCS_min if it is correctly defined, otherwise wait 500ns
|
||||||
|
* (which should be safe for all NANDs).
|
||||||
|
*/
|
||||||
|
if (chip->data_interface && chip->data_interface->timings.sdr.tCCS_min)
|
||||||
|
ndelay(chip->data_interface->timings.sdr.tCCS_min / 1000);
|
||||||
|
else
|
||||||
|
ndelay(500);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_command_lp - [DEFAULT] Send command to NAND large page device
|
* nand_command_lp - [DEFAULT] Send command to NAND large page device
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
|
@ -773,10 +792,13 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
||||||
case NAND_CMD_ERASE1:
|
case NAND_CMD_ERASE1:
|
||||||
case NAND_CMD_ERASE2:
|
case NAND_CMD_ERASE2:
|
||||||
case NAND_CMD_SEQIN:
|
case NAND_CMD_SEQIN:
|
||||||
case NAND_CMD_RNDIN:
|
|
||||||
case NAND_CMD_STATUS:
|
case NAND_CMD_STATUS:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case NAND_CMD_RNDIN:
|
||||||
|
nand_ccs_delay(chip);
|
||||||
|
return;
|
||||||
|
|
||||||
case NAND_CMD_RESET:
|
case NAND_CMD_RESET:
|
||||||
if (chip->dev_ready)
|
if (chip->dev_ready)
|
||||||
break;
|
break;
|
||||||
|
@ -795,6 +817,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);
|
||||||
|
|
||||||
|
nand_ccs_delay(chip);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case NAND_CMD_READ0:
|
case NAND_CMD_READ0:
|
||||||
|
@ -1946,7 +1970,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||||
__func__, buf);
|
__func__, buf);
|
||||||
|
|
||||||
read_retry:
|
read_retry:
|
||||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
if (nand_standard_page_accessors(&chip->ecc))
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now read the page into the buffer. Absent an error,
|
* Now read the page into the buffer. Absent an error,
|
||||||
|
@ -2634,7 +2659,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
else
|
else
|
||||||
subpage = 0;
|
subpage = 0;
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
if (nand_standard_page_accessors(&chip->ecc))
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
||||||
|
|
||||||
if (unlikely(raw))
|
if (unlikely(raw))
|
||||||
status = chip->ecc.write_page_raw(mtd, chip, buf,
|
status = chip->ecc.write_page_raw(mtd, chip, buf,
|
||||||
|
@ -2657,7 +2683,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
|
||||||
if (!cached || !NAND_HAS_CACHEPROG(chip)) {
|
if (!cached || !NAND_HAS_CACHEPROG(chip)) {
|
||||||
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
if (nand_standard_page_accessors(&chip->ecc))
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||||
status = chip->waitfunc(mtd, chip);
|
status = chip->waitfunc(mtd, chip);
|
||||||
/*
|
/*
|
||||||
* See if operation failed and additional status checks are
|
* See if operation failed and additional status checks are
|
||||||
|
@ -3985,10 +4012,9 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
/*
|
/*
|
||||||
* Get the flash and manufacturer id and lookup if the type is supported.
|
* Get the flash and manufacturer id and lookup if the type is supported.
|
||||||
*/
|
*/
|
||||||
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
struct nand_chip *chip,
|
int *maf_id, int *dev_id,
|
||||||
int *maf_id, int *dev_id,
|
struct nand_flash_dev *type)
|
||||||
struct nand_flash_dev *type)
|
|
||||||
{
|
{
|
||||||
int busw;
|
int busw;
|
||||||
int i, maf_idx;
|
int i, maf_idx;
|
||||||
|
@ -4026,7 +4052,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||||
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
|
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
|
||||||
pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
|
pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
|
||||||
*maf_id, *dev_id, id_data[0], id_data[1]);
|
*maf_id, *dev_id, id_data[0], id_data[1]);
|
||||||
return ERR_PTR(-ENODEV);
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
|
@ -4053,7 +4079,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type->name)
|
if (!type->name)
|
||||||
return ERR_PTR(-ENODEV);
|
return -ENODEV;
|
||||||
|
|
||||||
if (!mtd->name)
|
if (!mtd->name)
|
||||||
mtd->name = type->name;
|
mtd->name = type->name;
|
||||||
|
@ -4098,7 +4124,7 @@ ident_done:
|
||||||
pr_warn("bus width %d instead %d bit\n",
|
pr_warn("bus width %d instead %d bit\n",
|
||||||
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
||||||
busw ? 16 : 8);
|
busw ? 16 : 8);
|
||||||
return ERR_PTR(-EINVAL);
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nand_decode_bbm_options(mtd, chip, id_data);
|
nand_decode_bbm_options(mtd, chip, id_data);
|
||||||
|
@ -4140,7 +4166,7 @@ ident_done:
|
||||||
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
|
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
|
||||||
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||||
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
|
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
|
||||||
return type;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * const nand_ecc_modes[] = {
|
static const char * const nand_ecc_modes[] = {
|
||||||
|
@ -4306,7 +4332,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||||
{
|
{
|
||||||
int i, nand_maf_id, nand_dev_id;
|
int i, nand_maf_id, nand_dev_id;
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
struct nand_flash_dev *type;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = nand_dt_init(chip);
|
ret = nand_dt_init(chip);
|
||||||
|
@ -4329,14 +4354,12 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||||
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
||||||
|
|
||||||
/* Read the flash type */
|
/* Read the flash type */
|
||||||
type = nand_get_flash_type(mtd, chip, &nand_maf_id,
|
ret = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, table);
|
||||||
&nand_dev_id, table);
|
if (ret) {
|
||||||
|
|
||||||
if (IS_ERR(type)) {
|
|
||||||
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
|
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
|
||||||
pr_warn("No NAND device found\n");
|
pr_warn("No NAND device found\n");
|
||||||
chip->select_chip(mtd, -1);
|
chip->select_chip(mtd, -1);
|
||||||
return PTR_ERR(type);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the ->data_interface field. */
|
/* Initialize the ->data_interface field. */
|
||||||
|
@ -4515,6 +4538,26 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
|
||||||
return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
|
return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool invalid_ecc_page_accessors(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||||
|
|
||||||
|
if (nand_standard_page_accessors(ecc))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NAND_ECC_CUSTOM_PAGE_ACCESS flag is set, make sure the NAND
|
||||||
|
* controller driver implements all the page accessors because
|
||||||
|
* default helpers are not suitable when the core does not
|
||||||
|
* send the READ0/PAGEPROG commands.
|
||||||
|
*/
|
||||||
|
return (!ecc->read_page || !ecc->write_page ||
|
||||||
|
!ecc->read_page_raw || !ecc->write_page_raw ||
|
||||||
|
(NAND_HAS_SUBPAGE_READ(chip) && !ecc->read_subpage) ||
|
||||||
|
(NAND_HAS_SUBPAGE_WRITE(chip) && !ecc->write_subpage &&
|
||||||
|
ecc->hwctl && ecc->calculate));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_scan_tail - [NAND Interface] Scan for the NAND device
|
* nand_scan_tail - [NAND Interface] Scan for the NAND device
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
|
@ -4535,6 +4578,11 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||||
!(chip->bbt_options & NAND_BBT_USE_FLASH)))
|
!(chip->bbt_options & NAND_BBT_USE_FLASH)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (invalid_ecc_page_accessors(chip)) {
|
||||||
|
pr_err("Invalid ECC page accessors setup\n");
|
||||||
|
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
|
||||||
+ mtd->oobsize * 3, GFP_KERNEL);
|
+ mtd->oobsize * 3, GFP_KERNEL);
|
||||||
|
|
|
@ -36,6 +36,9 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||||
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
||||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||||
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
|
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
|
||||||
|
{"TC58NVG2S0H 4G 3.3V 8-bit",
|
||||||
|
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} },
|
||||||
|
SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) },
|
||||||
{"TC58NVG3S0F 8G 3.3V 8-bit",
|
{"TC58NVG3S0F 8G 3.3V 8-bit",
|
||||||
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
||||||
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
|
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
|
||||||
|
|
|
@ -18,6 +18,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||||
{
|
{
|
||||||
.type = NAND_SDR_IFACE,
|
.type = NAND_SDR_IFACE,
|
||||||
.timings.sdr = {
|
.timings.sdr = {
|
||||||
|
.tCCS_min = 500000,
|
||||||
|
.tR_max = 200000000,
|
||||||
.tADL_min = 400000,
|
.tADL_min = 400000,
|
||||||
.tALH_min = 20000,
|
.tALH_min = 20000,
|
||||||
.tALS_min = 50000,
|
.tALS_min = 50000,
|
||||||
|
@ -58,6 +60,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||||
{
|
{
|
||||||
.type = NAND_SDR_IFACE,
|
.type = NAND_SDR_IFACE,
|
||||||
.timings.sdr = {
|
.timings.sdr = {
|
||||||
|
.tCCS_min = 500000,
|
||||||
|
.tR_max = 200000000,
|
||||||
.tADL_min = 400000,
|
.tADL_min = 400000,
|
||||||
.tALH_min = 10000,
|
.tALH_min = 10000,
|
||||||
.tALS_min = 25000,
|
.tALS_min = 25000,
|
||||||
|
@ -98,6 +102,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||||
{
|
{
|
||||||
.type = NAND_SDR_IFACE,
|
.type = NAND_SDR_IFACE,
|
||||||
.timings.sdr = {
|
.timings.sdr = {
|
||||||
|
.tCCS_min = 500000,
|
||||||
|
.tR_max = 200000000,
|
||||||
.tADL_min = 400000,
|
.tADL_min = 400000,
|
||||||
.tALH_min = 10000,
|
.tALH_min = 10000,
|
||||||
.tALS_min = 15000,
|
.tALS_min = 15000,
|
||||||
|
@ -138,6 +144,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||||
{
|
{
|
||||||
.type = NAND_SDR_IFACE,
|
.type = NAND_SDR_IFACE,
|
||||||
.timings.sdr = {
|
.timings.sdr = {
|
||||||
|
.tCCS_min = 500000,
|
||||||
|
.tR_max = 200000000,
|
||||||
.tADL_min = 400000,
|
.tADL_min = 400000,
|
||||||
.tALH_min = 5000,
|
.tALH_min = 5000,
|
||||||
.tALS_min = 10000,
|
.tALS_min = 10000,
|
||||||
|
@ -178,6 +186,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||||
{
|
{
|
||||||
.type = NAND_SDR_IFACE,
|
.type = NAND_SDR_IFACE,
|
||||||
.timings.sdr = {
|
.timings.sdr = {
|
||||||
|
.tCCS_min = 500000,
|
||||||
|
.tR_max = 200000000,
|
||||||
.tADL_min = 400000,
|
.tADL_min = 400000,
|
||||||
.tALH_min = 5000,
|
.tALH_min = 5000,
|
||||||
.tALS_min = 10000,
|
.tALS_min = 10000,
|
||||||
|
@ -218,6 +228,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||||
{
|
{
|
||||||
.type = NAND_SDR_IFACE,
|
.type = NAND_SDR_IFACE,
|
||||||
.timings.sdr = {
|
.timings.sdr = {
|
||||||
|
.tCCS_min = 500000,
|
||||||
|
.tR_max = 200000000,
|
||||||
.tADL_min = 400000,
|
.tADL_min = 400000,
|
||||||
.tALH_min = 5000,
|
.tALH_min = 5000,
|
||||||
.tALS_min = 10000,
|
.tALS_min = 10000,
|
||||||
|
@ -290,10 +302,22 @@ int onfi_init_data_interface(struct nand_chip *chip,
|
||||||
*iface = onfi_sdr_timings[timing_mode];
|
*iface = onfi_sdr_timings[timing_mode];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: initialize timings that cannot be deduced from timing mode:
|
* Initialize timings that cannot be deduced from timing mode:
|
||||||
* tR, tPROG, tCCS, ...
|
* tR, tPROG, tCCS, ...
|
||||||
* These information are part of the ONFI parameter page.
|
* These information are part of the ONFI parameter page.
|
||||||
*/
|
*/
|
||||||
|
if (chip->onfi_version) {
|
||||||
|
struct nand_onfi_params *params = &chip->onfi_params;
|
||||||
|
struct nand_sdr_timings *timings = &iface->timings.sdr;
|
||||||
|
|
||||||
|
/* microseconds -> picoseconds */
|
||||||
|
timings->tPROG_max = 1000000UL * le16_to_cpu(params->t_prog);
|
||||||
|
timings->tBERS_max = 1000000UL * le16_to_cpu(params->t_bers);
|
||||||
|
timings->tR_max = 1000000UL * le16_to_cpu(params->t_r);
|
||||||
|
|
||||||
|
/* nanoseconds -> picoseconds */
|
||||||
|
timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -525,24 +525,20 @@ static int nandsim_debugfs_create(struct nandsim *dev)
|
||||||
{
|
{
|
||||||
struct nandsim_debug_info *dbg = &dev->dbg;
|
struct nandsim_debug_info *dbg = &dev->dbg;
|
||||||
struct dentry *dent;
|
struct dentry *dent;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!IS_ENABLED(CONFIG_DEBUG_FS))
|
if (!IS_ENABLED(CONFIG_DEBUG_FS))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
dent = debugfs_create_dir("nandsim", NULL);
|
dent = debugfs_create_dir("nandsim", NULL);
|
||||||
if (IS_ERR_OR_NULL(dent)) {
|
if (!dent) {
|
||||||
int err = dent ? -ENODEV : PTR_ERR(dent);
|
NS_ERR("cannot create \"nandsim\" debugfs directory\n");
|
||||||
|
return -ENODEV;
|
||||||
NS_ERR("cannot create \"nandsim\" debugfs directory, err %d\n",
|
|
||||||
err);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
dbg->dfs_root = dent;
|
dbg->dfs_root = dent;
|
||||||
|
|
||||||
dent = debugfs_create_file("wear_report", S_IRUSR,
|
dent = debugfs_create_file("wear_report", S_IRUSR,
|
||||||
dbg->dfs_root, dev, &dfs_fops);
|
dbg->dfs_root, dev, &dfs_fops);
|
||||||
if (IS_ERR_OR_NULL(dent))
|
if (!dent)
|
||||||
goto out_remove;
|
goto out_remove;
|
||||||
dbg->dfs_wear_report = dent;
|
dbg->dfs_wear_report = dent;
|
||||||
|
|
||||||
|
@ -550,8 +546,7 @@ static int nandsim_debugfs_create(struct nandsim *dev)
|
||||||
|
|
||||||
out_remove:
|
out_remove:
|
||||||
debugfs_remove_recursive(dbg->dfs_root);
|
debugfs_remove_recursive(dbg->dfs_root);
|
||||||
err = dent ? PTR_ERR(dent) : -ENODEV;
|
return -ENODEV;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2313,8 +2308,6 @@ static int __init ns_init_module(void)
|
||||||
retval = nand_scan_ident(nsmtd, 1, NULL);
|
retval = nand_scan_ident(nsmtd, 1, NULL);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
NS_ERR("cannot scan NAND Simulator device\n");
|
NS_ERR("cannot scan NAND Simulator device\n");
|
||||||
if (retval > 0)
|
|
||||||
retval = -ENXIO;
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2350,8 +2343,6 @@ static int __init ns_init_module(void)
|
||||||
retval = nand_scan_tail(nsmtd);
|
retval = nand_scan_tail(nsmtd);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
NS_ERR("can't register NAND Simulator\n");
|
NS_ERR("can't register NAND Simulator\n");
|
||||||
if (retval > 0)
|
|
||||||
retval = -ENXIO;
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1895,10 +1895,10 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* scan NAND device connected to chip controller */
|
/* scan NAND device connected to chip controller */
|
||||||
nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
|
nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
|
||||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
err = nand_scan_ident(mtd, 1, NULL);
|
||||||
|
if (err) {
|
||||||
dev_err(&info->pdev->dev,
|
dev_err(&info->pdev->dev,
|
||||||
"scan failed, may be bus-width mismatch\n");
|
"scan failed, may be bus-width mismatch\n");
|
||||||
err = -ENXIO;
|
|
||||||
goto return_error;
|
goto return_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2154,10 +2154,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
scan_tail:
|
scan_tail:
|
||||||
/* second phase scan */
|
/* second phase scan */
|
||||||
if (nand_scan_tail(mtd)) {
|
err = nand_scan_tail(mtd);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto return_error;
|
goto return_error;
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->of_node)
|
if (dev->of_node)
|
||||||
mtd_device_register(mtd, NULL, 0);
|
mtd_device_register(mtd, NULL, 0);
|
||||||
|
@ -2197,6 +2196,7 @@ static const struct of_device_id omap_nand_ids[] = {
|
||||||
{ .compatible = "ti,omap2-nand", },
|
{ .compatible = "ti,omap2-nand", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, omap_nand_ids);
|
||||||
|
|
||||||
static struct platform_driver omap_nand_driver = {
|
static struct platform_driver omap_nand_driver = {
|
||||||
.probe = omap_nand_probe,
|
.probe = omap_nand_probe,
|
||||||
|
|
|
@ -155,10 +155,9 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nand_scan(mtd, 1)) {
|
ret = nand_scan(mtd, 1);
|
||||||
ret = -ENXIO;
|
if (ret)
|
||||||
goto no_dev;
|
goto no_dev;
|
||||||
}
|
|
||||||
|
|
||||||
mtd->name = "orion_nand";
|
mtd->name = "orion_nand";
|
||||||
ret = mtd_device_register(mtd, board->parts, board->nr_parts);
|
ret = mtd_device_register(mtd, board->parts, board->nr_parts);
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
* Oxford Semiconductor OXNAS NAND driver
|
||||||
|
|
||||||
|
* Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
|
||||||
|
* Heavily based on plat_nand.c :
|
||||||
|
* Author: Vitaly Wool <vitalywool@gmail.com>
|
||||||
|
* Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
|
||||||
|
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
/* Nand commands */
|
||||||
|
#define OXNAS_NAND_CMD_ALE BIT(18)
|
||||||
|
#define OXNAS_NAND_CMD_CLE BIT(19)
|
||||||
|
|
||||||
|
#define OXNAS_NAND_MAX_CHIPS 1
|
||||||
|
|
||||||
|
struct oxnas_nand_ctrl {
|
||||||
|
struct nand_hw_control base;
|
||||||
|
void __iomem *io_base;
|
||||||
|
struct clk *clk;
|
||||||
|
struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS];
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t oxnas_nand_read_byte(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
||||||
|
|
||||||
|
return readb(oxnas->io_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void oxnas_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
||||||
|
|
||||||
|
ioread8_rep(oxnas->io_base, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void oxnas_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
||||||
|
|
||||||
|
iowrite8_rep(oxnas->io_base, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single CS command control */
|
||||||
|
static void oxnas_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||||
|
unsigned int ctrl)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
||||||
|
|
||||||
|
if (ctrl & NAND_CLE)
|
||||||
|
writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE);
|
||||||
|
else if (ctrl & NAND_ALE)
|
||||||
|
writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Probe for the NAND device.
|
||||||
|
*/
|
||||||
|
static int oxnas_nand_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct device_node *nand_np;
|
||||||
|
struct oxnas_nand_ctrl *oxnas;
|
||||||
|
struct nand_chip *chip;
|
||||||
|
struct mtd_info *mtd;
|
||||||
|
struct resource *res;
|
||||||
|
int nchips = 0;
|
||||||
|
int count = 0;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* Allocate memory for the device structure (and zero it) */
|
||||||
|
oxnas = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!oxnas)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
nand_hw_control_init(&oxnas->base);
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
oxnas->io_base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(oxnas->io_base))
|
||||||
|
return PTR_ERR(oxnas->io_base);
|
||||||
|
|
||||||
|
oxnas->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(oxnas->clk))
|
||||||
|
oxnas->clk = NULL;
|
||||||
|
|
||||||
|
/* Only a single chip node is supported */
|
||||||
|
count = of_get_child_count(np);
|
||||||
|
if (count > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
clk_prepare_enable(oxnas->clk);
|
||||||
|
device_reset_optional(&pdev->dev);
|
||||||
|
|
||||||
|
for_each_child_of_node(np, nand_np) {
|
||||||
|
chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!chip)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
chip->controller = &oxnas->base;
|
||||||
|
|
||||||
|
nand_set_flash_node(chip, nand_np);
|
||||||
|
nand_set_controller_data(chip, oxnas);
|
||||||
|
|
||||||
|
mtd = nand_to_mtd(chip);
|
||||||
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
mtd->priv = chip;
|
||||||
|
|
||||||
|
chip->cmd_ctrl = oxnas_nand_cmd_ctrl;
|
||||||
|
chip->read_buf = oxnas_nand_read_buf;
|
||||||
|
chip->read_byte = oxnas_nand_read_byte;
|
||||||
|
chip->write_buf = oxnas_nand_write_buf;
|
||||||
|
chip->chip_delay = 30;
|
||||||
|
|
||||||
|
/* Scan to find existence of the device */
|
||||||
|
err = nand_scan(mtd, 1);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = mtd_device_register(mtd, NULL, 0);
|
||||||
|
if (err) {
|
||||||
|
nand_release(mtd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
oxnas->chips[nchips] = chip;
|
||||||
|
++nchips;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit if no chips found */
|
||||||
|
if (!nchips)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, oxnas);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oxnas_nand_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (oxnas->chips[0])
|
||||||
|
nand_release(nand_to_mtd(oxnas->chips[0]));
|
||||||
|
|
||||||
|
clk_disable_unprepare(oxnas->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id oxnas_nand_match[] = {
|
||||||
|
{ .compatible = "oxsemi,ox820-nand" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, oxnas_nand_match);
|
||||||
|
|
||||||
|
static struct platform_driver oxnas_nand_driver = {
|
||||||
|
.probe = oxnas_nand_probe,
|
||||||
|
.remove = oxnas_nand_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "oxnas_nand",
|
||||||
|
.of_match_table = oxnas_nand_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(oxnas_nand_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||||
|
MODULE_DESCRIPTION("Oxnas NAND driver");
|
||||||
|
MODULE_ALIAS("platform:oxnas_nand");
|
|
@ -156,10 +156,9 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
|
||||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
/* Scan to find existence of the device */
|
||||||
if (nand_scan(pasemi_nand_mtd, 1)) {
|
err = nand_scan(pasemi_nand_mtd, 1);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto out_lpc;
|
goto out_lpc;
|
||||||
}
|
|
||||||
|
|
||||||
if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) {
|
if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) {
|
||||||
dev_err(dev, "Unable to register MTD device\n");
|
dev_err(dev, "Unable to register MTD device\n");
|
||||||
|
|
|
@ -86,10 +86,9 @@ static int plat_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
/* Scan to find existence of the device */
|
||||||
if (nand_scan(mtd, pdata->chip.nr_chips)) {
|
err = nand_scan(mtd, pdata->chip.nr_chips);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
part_types = pdata->chip.part_probe_types;
|
part_types = pdata->chip.part_probe_types;
|
||||||
|
|
||||||
|
|
|
@ -1680,8 +1680,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
||||||
chip->ecc.strength = pdata->ecc_strength;
|
chip->ecc.strength = pdata->ecc_strength;
|
||||||
chip->ecc.size = pdata->ecc_step_size;
|
chip->ecc.size = pdata->ecc_step_size;
|
||||||
|
|
||||||
if (nand_scan_ident(mtd, 1, NULL))
|
ret = nand_scan_ident(mtd, 1, NULL);
|
||||||
return -ENODEV;
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (!pdata->keep_config) {
|
if (!pdata->keep_config) {
|
||||||
ret = pxa3xx_nand_init(host);
|
ret = pxa3xx_nand_init(host);
|
||||||
|
@ -1774,8 +1775,11 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
int ret, irq, cs;
|
int ret, irq, cs;
|
||||||
|
|
||||||
pdata = dev_get_platdata(&pdev->dev);
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
if (pdata->num_cs <= 0)
|
if (pdata->num_cs <= 0) {
|
||||||
|
dev_err(&pdev->dev, "invalid number of chip selects\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
info = devm_kzalloc(&pdev->dev,
|
info = devm_kzalloc(&pdev->dev,
|
||||||
sizeof(*info) + sizeof(*host) * pdata->num_cs,
|
sizeof(*info) + sizeof(*host) * pdata->num_cs,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -1813,8 +1817,9 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
nand_hw_control_init(chip->controller);
|
nand_hw_control_init(chip->controller);
|
||||||
info->clk = devm_clk_get(&pdev->dev, NULL);
|
info->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(info->clk)) {
|
if (IS_ERR(info->clk)) {
|
||||||
dev_err(&pdev->dev, "failed to get nand clock\n");
|
ret = PTR_ERR(info->clk);
|
||||||
return PTR_ERR(info->clk);
|
dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
ret = clk_prepare_enable(info->clk);
|
ret = clk_prepare_enable(info->clk);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -1842,6 +1847,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
info->mmio_base = devm_ioremap_resource(&pdev->dev, r);
|
info->mmio_base = devm_ioremap_resource(&pdev->dev, r);
|
||||||
if (IS_ERR(info->mmio_base)) {
|
if (IS_ERR(info->mmio_base)) {
|
||||||
ret = PTR_ERR(info->mmio_base);
|
ret = PTR_ERR(info->mmio_base);
|
||||||
|
dev_err(&pdev->dev, "failed to map register space: %d\n", ret);
|
||||||
goto fail_disable_clk;
|
goto fail_disable_clk;
|
||||||
}
|
}
|
||||||
info->mmio_phys = r->start;
|
info->mmio_phys = r->start;
|
||||||
|
@ -1861,7 +1867,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
||||||
pxa3xx_nand_irq_thread, IRQF_ONESHOT,
|
pxa3xx_nand_irq_thread, IRQF_ONESHOT,
|
||||||
pdev->name, info);
|
pdev->name, info);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret);
|
||||||
goto fail_free_buf;
|
goto fail_free_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1960,10 +1966,8 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = alloc_nand_resource(pdev);
|
ret = alloc_nand_resource(pdev);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "alloc nand resource failed\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
info = platform_get_drvdata(pdev);
|
info = platform_get_drvdata(pdev);
|
||||||
probe_success = 0;
|
probe_success = 0;
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
|
@ -185,6 +187,22 @@ struct s3c2410_nand_info {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct s3c24XX_nand_devtype_data {
|
||||||
|
enum s3c_cpu_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = {
|
||||||
|
.type = TYPE_S3C2410,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = {
|
||||||
|
.type = TYPE_S3C2412,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = {
|
||||||
|
.type = TYPE_S3C2440,
|
||||||
|
};
|
||||||
|
|
||||||
/* conversion functions */
|
/* conversion functions */
|
||||||
|
|
||||||
static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
|
static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
|
||||||
|
@ -497,7 +515,6 @@ static int s3c2412_nand_devready(struct mtd_info *mtd)
|
||||||
|
|
||||||
/* ECC handling functions */
|
/* ECC handling functions */
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
|
|
||||||
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||||
u_char *read_ecc, u_char *calc_ecc)
|
u_char *read_ecc, u_char *calc_ecc)
|
||||||
{
|
{
|
||||||
|
@ -649,7 +666,6 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* over-ride the standard functions for a little more speed. We can
|
/* over-ride the standard functions for a little more speed. We can
|
||||||
* use read/write block to move the data buffers to/from the controller
|
* use read/write block to move the data buffers to/from the controller
|
||||||
|
@ -796,6 +812,30 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd,
|
||||||
|
const struct nand_data_interface *conf,
|
||||||
|
bool check_only)
|
||||||
|
{
|
||||||
|
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||||
|
struct s3c2410_platform_nand *pdata = info->platform;
|
||||||
|
const struct nand_sdr_timings *timings;
|
||||||
|
int tacls;
|
||||||
|
|
||||||
|
timings = nand_get_sdr_timings(conf);
|
||||||
|
if (IS_ERR(timings))
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
tacls = timings->tCLS_min - timings->tWP_min;
|
||||||
|
if (tacls < 0)
|
||||||
|
tacls = 0;
|
||||||
|
|
||||||
|
pdata->tacls = DIV_ROUND_UP(tacls, 1000);
|
||||||
|
pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000);
|
||||||
|
pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000);
|
||||||
|
|
||||||
|
return s3c2410_nand_setrate(info);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* s3c2410_nand_init_chip - initialise a single instance of an chip
|
* s3c2410_nand_init_chip - initialise a single instance of an chip
|
||||||
* @info: The base NAND controller the chip is on.
|
* @info: The base NAND controller the chip is on.
|
||||||
|
@ -810,9 +850,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||||
struct s3c2410_nand_mtd *nmtd,
|
struct s3c2410_nand_mtd *nmtd,
|
||||||
struct s3c2410_nand_set *set)
|
struct s3c2410_nand_set *set)
|
||||||
{
|
{
|
||||||
|
struct device_node *np = info->device->of_node;
|
||||||
struct nand_chip *chip = &nmtd->chip;
|
struct nand_chip *chip = &nmtd->chip;
|
||||||
void __iomem *regs = info->regs;
|
void __iomem *regs = info->regs;
|
||||||
|
|
||||||
|
nand_set_flash_node(chip, set->of_node);
|
||||||
|
|
||||||
chip->write_buf = s3c2410_nand_write_buf;
|
chip->write_buf = s3c2410_nand_write_buf;
|
||||||
chip->read_buf = s3c2410_nand_read_buf;
|
chip->read_buf = s3c2410_nand_read_buf;
|
||||||
chip->select_chip = s3c2410_nand_select_chip;
|
chip->select_chip = s3c2410_nand_select_chip;
|
||||||
|
@ -821,6 +864,13 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||||
chip->options = set->options;
|
chip->options = set->options;
|
||||||
chip->controller = &info->controller;
|
chip->controller = &info->controller;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* let's keep behavior unchanged for legacy boards booting via pdata and
|
||||||
|
* auto-detect timings only when booting with a device tree.
|
||||||
|
*/
|
||||||
|
if (np)
|
||||||
|
chip->setup_data_interface = s3c2410_nand_setup_data_interface;
|
||||||
|
|
||||||
switch (info->cpu_type) {
|
switch (info->cpu_type) {
|
||||||
case TYPE_S3C2410:
|
case TYPE_S3C2410:
|
||||||
chip->IO_ADDR_W = regs + S3C2410_NFDATA;
|
chip->IO_ADDR_W = regs + S3C2410_NFDATA;
|
||||||
|
@ -858,58 +908,14 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||||
nmtd->info = info;
|
nmtd->info = info;
|
||||||
nmtd->set = set;
|
nmtd->set = set;
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
|
chip->ecc.mode = info->platform->ecc_mode;
|
||||||
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
|
|
||||||
chip->ecc.correct = s3c2410_nand_correct_data;
|
|
||||||
chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
chip->ecc.strength = 1;
|
|
||||||
|
|
||||||
switch (info->cpu_type) {
|
/*
|
||||||
case TYPE_S3C2410:
|
* If you use u-boot BBT creation code, specifying this flag will
|
||||||
chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
|
* let the kernel fish out the BBT from the NAND.
|
||||||
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
|
*/
|
||||||
break;
|
if (set->flash_bbt)
|
||||||
|
|
||||||
case TYPE_S3C2412:
|
|
||||||
chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
|
|
||||||
chip->ecc.calculate = s3c2412_nand_calculate_ecc;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_S3C2440:
|
|
||||||
chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
|
|
||||||
chip->ecc.calculate = s3c2440_nand_calculate_ecc;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
chip->ecc.mode = NAND_ECC_SOFT;
|
|
||||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (set->disable_ecc)
|
|
||||||
chip->ecc.mode = NAND_ECC_NONE;
|
|
||||||
|
|
||||||
switch (chip->ecc.mode) {
|
|
||||||
case NAND_ECC_NONE:
|
|
||||||
dev_info(info->device, "NAND ECC disabled\n");
|
|
||||||
break;
|
|
||||||
case NAND_ECC_SOFT:
|
|
||||||
dev_info(info->device, "NAND soft ECC\n");
|
|
||||||
break;
|
|
||||||
case NAND_ECC_HW:
|
|
||||||
dev_info(info->device, "NAND hardware ECC\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dev_info(info->device, "NAND ECC UNKNOWN\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If you use u-boot BBT creation code, specifying this flag will
|
|
||||||
* let the kernel fish out the BBT from the NAND, and also skip the
|
|
||||||
* full NAND scan that can take 1/2s or so. Little things... */
|
|
||||||
if (set->flash_bbt) {
|
|
||||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||||
chip->options |= NAND_SKIP_BBTSCAN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -923,28 +929,146 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||||
*
|
*
|
||||||
* The internal state is currently limited to the ECC state information.
|
* The internal state is currently limited to the ECC state information.
|
||||||
*/
|
*/
|
||||||
static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
||||||
struct s3c2410_nand_mtd *nmtd)
|
struct s3c2410_nand_mtd *nmtd)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = &nmtd->chip;
|
struct nand_chip *chip = &nmtd->chip;
|
||||||
|
|
||||||
dev_dbg(info->device, "chip %p => page shift %d\n",
|
switch (chip->ecc.mode) {
|
||||||
chip, chip->page_shift);
|
|
||||||
|
|
||||||
if (chip->ecc.mode != NAND_ECC_HW)
|
case NAND_ECC_NONE:
|
||||||
return;
|
dev_info(info->device, "ECC disabled\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_ECC_SOFT:
|
||||||
|
/*
|
||||||
|
* This driver expects Hamming based ECC when ecc_mode is set
|
||||||
|
* to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
|
||||||
|
* avoid adding an extra ecc_algo field to
|
||||||
|
* s3c2410_platform_nand.
|
||||||
|
*/
|
||||||
|
chip->ecc.algo = NAND_ECC_HAMMING;
|
||||||
|
dev_info(info->device, "soft ECC\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_ECC_HW:
|
||||||
|
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
|
||||||
|
chip->ecc.correct = s3c2410_nand_correct_data;
|
||||||
|
chip->ecc.strength = 1;
|
||||||
|
|
||||||
|
switch (info->cpu_type) {
|
||||||
|
case TYPE_S3C2410:
|
||||||
|
chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
|
||||||
|
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_S3C2412:
|
||||||
|
chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
|
||||||
|
chip->ecc.calculate = s3c2412_nand_calculate_ecc;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_S3C2440:
|
||||||
|
chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
|
||||||
|
chip->ecc.calculate = s3c2440_nand_calculate_ecc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(info->device, "chip %p => page shift %d\n",
|
||||||
|
chip, chip->page_shift);
|
||||||
|
|
||||||
/* change the behaviour depending on whether we are using
|
/* change the behaviour depending on whether we are using
|
||||||
* the large or small page nand device */
|
* the large or small page nand device */
|
||||||
|
if (chip->page_shift > 10) {
|
||||||
|
chip->ecc.size = 256;
|
||||||
|
chip->ecc.bytes = 3;
|
||||||
|
} else {
|
||||||
|
chip->ecc.size = 512;
|
||||||
|
chip->ecc.bytes = 3;
|
||||||
|
mtd_set_ooblayout(nand_to_mtd(chip),
|
||||||
|
&s3c2410_ooblayout_ops);
|
||||||
|
}
|
||||||
|
|
||||||
if (chip->page_shift > 10) {
|
dev_info(info->device, "hardware ECC\n");
|
||||||
chip->ecc.size = 256;
|
break;
|
||||||
chip->ecc.bytes = 3;
|
|
||||||
} else {
|
default:
|
||||||
chip->ecc.size = 512;
|
dev_err(info->device, "invalid ECC mode!\n");
|
||||||
chip->ecc.bytes = 3;
|
return -EINVAL;
|
||||||
mtd_set_ooblayout(nand_to_mtd(chip), &s3c2410_ooblayout_ops);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||||
|
chip->options |= NAND_SKIP_BBTSCAN;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id s3c24xx_nand_dt_ids[] = {
|
||||||
|
{
|
||||||
|
.compatible = "samsung,s3c2410-nand",
|
||||||
|
.data = &s3c2410_nand_devtype_data,
|
||||||
|
}, {
|
||||||
|
/* also compatible with s3c6400 */
|
||||||
|
.compatible = "samsung,s3c2412-nand",
|
||||||
|
.data = &s3c2412_nand_devtype_data,
|
||||||
|
}, {
|
||||||
|
.compatible = "samsung,s3c2440-nand",
|
||||||
|
.data = &s3c2440_nand_devtype_data,
|
||||||
|
},
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids);
|
||||||
|
|
||||||
|
static int s3c24xx_nand_probe_dt(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
const struct s3c24XX_nand_devtype_data *devtype_data;
|
||||||
|
struct s3c2410_platform_nand *pdata;
|
||||||
|
struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
|
||||||
|
struct device_node *np = pdev->dev.of_node, *child;
|
||||||
|
struct s3c2410_nand_set *sets;
|
||||||
|
|
||||||
|
devtype_data = of_device_get_match_data(&pdev->dev);
|
||||||
|
if (!devtype_data)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
info->cpu_type = devtype_data->type;
|
||||||
|
|
||||||
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
|
if (!pdata)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pdev->dev.platform_data = pdata;
|
||||||
|
|
||||||
|
pdata->nr_sets = of_get_child_count(np);
|
||||||
|
if (!pdata->nr_sets)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!sets)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pdata->sets = sets;
|
||||||
|
|
||||||
|
for_each_available_child_of_node(np, child) {
|
||||||
|
sets->name = (char *)child->name;
|
||||||
|
sets->of_node = child;
|
||||||
|
sets->nr_chips = 1;
|
||||||
|
|
||||||
|
of_node_get(child);
|
||||||
|
|
||||||
|
sets++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s3c24xx_nand_probe_pdata(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
info->cpu_type = platform_get_device_id(pdev)->driver_data;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_nand_probe
|
/* s3c24xx_nand_probe
|
||||||
|
@ -956,8 +1080,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
||||||
*/
|
*/
|
||||||
static int s3c24xx_nand_probe(struct platform_device *pdev)
|
static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
|
struct s3c2410_platform_nand *plat;
|
||||||
enum s3c_cpu_type cpu_type;
|
|
||||||
struct s3c2410_nand_info *info;
|
struct s3c2410_nand_info *info;
|
||||||
struct s3c2410_nand_mtd *nmtd;
|
struct s3c2410_nand_mtd *nmtd;
|
||||||
struct s3c2410_nand_set *sets;
|
struct s3c2410_nand_set *sets;
|
||||||
|
@ -967,8 +1090,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
int nr_sets;
|
int nr_sets;
|
||||||
int setno;
|
int setno;
|
||||||
|
|
||||||
cpu_type = platform_get_device_id(pdev)->driver_data;
|
|
||||||
|
|
||||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||||
if (info == NULL) {
|
if (info == NULL) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -990,6 +1111,16 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
|
s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
|
||||||
|
|
||||||
|
if (pdev->dev.of_node)
|
||||||
|
err = s3c24xx_nand_probe_dt(pdev);
|
||||||
|
else
|
||||||
|
err = s3c24xx_nand_probe_pdata(pdev);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto exit_error;
|
||||||
|
|
||||||
|
plat = to_nand_plat(pdev);
|
||||||
|
|
||||||
/* allocate and map the resource */
|
/* allocate and map the resource */
|
||||||
|
|
||||||
/* currently we assume we have the one resource */
|
/* currently we assume we have the one resource */
|
||||||
|
@ -998,7 +1129,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
info->device = &pdev->dev;
|
info->device = &pdev->dev;
|
||||||
info->platform = plat;
|
info->platform = plat;
|
||||||
info->cpu_type = cpu_type;
|
|
||||||
|
|
||||||
info->regs = devm_ioremap_resource(&pdev->dev, res);
|
info->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(info->regs)) {
|
if (IS_ERR(info->regs)) {
|
||||||
|
@ -1008,12 +1138,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
|
dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
|
||||||
|
|
||||||
/* initialise the hardware */
|
|
||||||
|
|
||||||
err = s3c2410_nand_inithw(info);
|
|
||||||
if (err != 0)
|
|
||||||
goto exit_error;
|
|
||||||
|
|
||||||
sets = (plat != NULL) ? plat->sets : NULL;
|
sets = (plat != NULL) ? plat->sets : NULL;
|
||||||
nr_sets = (plat != NULL) ? plat->nr_sets : 1;
|
nr_sets = (plat != NULL) ? plat->nr_sets : 1;
|
||||||
|
|
||||||
|
@ -1046,7 +1170,9 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (nmtd->scan_res == 0) {
|
if (nmtd->scan_res == 0) {
|
||||||
s3c2410_nand_update_chip(info, nmtd);
|
err = s3c2410_nand_update_chip(info, nmtd);
|
||||||
|
if (err < 0)
|
||||||
|
goto exit_error;
|
||||||
nand_scan_tail(mtd);
|
nand_scan_tail(mtd);
|
||||||
s3c2410_nand_add_partition(info, nmtd, sets);
|
s3c2410_nand_add_partition(info, nmtd, sets);
|
||||||
}
|
}
|
||||||
|
@ -1055,6 +1181,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||||
sets++;
|
sets++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* initialise the hardware */
|
||||||
|
err = s3c2410_nand_inithw(info);
|
||||||
|
if (err != 0)
|
||||||
|
goto exit_error;
|
||||||
|
|
||||||
err = s3c2410_nand_cpufreq_register(info);
|
err = s3c2410_nand_cpufreq_register(info);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&pdev->dev, "failed to init cpufreq support\n");
|
dev_err(&pdev->dev, "failed to init cpufreq support\n");
|
||||||
|
@ -1155,6 +1286,7 @@ static struct platform_driver s3c24xx_nand_driver = {
|
||||||
.id_table = s3c24xx_driver_ids,
|
.id_table = s3c24xx_driver_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "s3c24xx-nand",
|
.name = "s3c24xx-nand",
|
||||||
|
.of_match_table = s3c24xx_nand_dt_ids,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -187,17 +187,9 @@ static int socrates_nand_probe(struct platform_device *ofdev)
|
||||||
|
|
||||||
dev_set_drvdata(&ofdev->dev, host);
|
dev_set_drvdata(&ofdev->dev, host);
|
||||||
|
|
||||||
/* first scan to find the device and get the page size */
|
res = nand_scan(mtd, 1);
|
||||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
if (res)
|
||||||
res = -ENXIO;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
/* second phase scan */
|
|
||||||
if (nand_scan_tail(mtd)) {
|
|
||||||
res = -ENXIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = mtd_device_register(mtd, NULL, 0);
|
res = mtd_device_register(mtd, NULL, 0);
|
||||||
if (!res)
|
if (!res)
|
||||||
|
|
|
@ -145,6 +145,7 @@
|
||||||
#define NFC_ECC_PIPELINE BIT(3)
|
#define NFC_ECC_PIPELINE BIT(3)
|
||||||
#define NFC_ECC_EXCEPTION BIT(4)
|
#define NFC_ECC_EXCEPTION BIT(4)
|
||||||
#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
|
#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
|
||||||
|
#define NFC_ECC_BLOCK_512 BIT(5)
|
||||||
#define NFC_RANDOM_EN BIT(9)
|
#define NFC_RANDOM_EN BIT(9)
|
||||||
#define NFC_RANDOM_DIRECTION BIT(10)
|
#define NFC_RANDOM_DIRECTION BIT(10)
|
||||||
#define NFC_ECC_MODE_MSK GENMASK(15, 12)
|
#define NFC_ECC_MODE_MSK GENMASK(15, 12)
|
||||||
|
@ -817,6 +818,9 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
|
||||||
ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION |
|
ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION |
|
||||||
NFC_ECC_PIPELINE;
|
NFC_ECC_PIPELINE;
|
||||||
|
|
||||||
|
if (nand->ecc.size == 512)
|
||||||
|
ecc_ctl |= NFC_ECC_BLOCK_512;
|
||||||
|
|
||||||
writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
|
writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,668 @@
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
/* Offsets relative to chip->base */
|
||||||
|
#define PBUS_CMD 0
|
||||||
|
#define PBUS_ADDR 4
|
||||||
|
#define PBUS_DATA 8
|
||||||
|
|
||||||
|
/* Offsets relative to reg_base */
|
||||||
|
#define NFC_STATUS 0x00
|
||||||
|
#define NFC_FLASH_CMD 0x04
|
||||||
|
#define NFC_DEVICE_CFG 0x08
|
||||||
|
#define NFC_TIMING1 0x0c
|
||||||
|
#define NFC_TIMING2 0x10
|
||||||
|
#define NFC_XFER_CFG 0x14
|
||||||
|
#define NFC_PKT_0_CFG 0x18
|
||||||
|
#define NFC_PKT_N_CFG 0x1c
|
||||||
|
#define NFC_BB_CFG 0x20
|
||||||
|
#define NFC_ADDR_PAGE 0x24
|
||||||
|
#define NFC_ADDR_OFFSET 0x28
|
||||||
|
#define NFC_XFER_STATUS 0x2c
|
||||||
|
|
||||||
|
/* NFC_STATUS values */
|
||||||
|
#define CMD_READY BIT(31)
|
||||||
|
|
||||||
|
/* NFC_FLASH_CMD values */
|
||||||
|
#define NFC_READ 1
|
||||||
|
#define NFC_WRITE 2
|
||||||
|
|
||||||
|
/* NFC_XFER_STATUS values */
|
||||||
|
#define PAGE_IS_EMPTY BIT(16)
|
||||||
|
|
||||||
|
/* Offsets relative to mem_base */
|
||||||
|
#define METADATA 0x000
|
||||||
|
#define ERROR_REPORT 0x1c0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Error reports are split in two bytes:
|
||||||
|
* byte 0 for the first packet in the page (PKT_0)
|
||||||
|
* byte 1 for other packets in the page (PKT_N, for N > 0)
|
||||||
|
* ERR_COUNT_PKT_N is the max error count over all but the first packet.
|
||||||
|
*/
|
||||||
|
#define DECODE_OK_PKT_0(v) ((v) & BIT(7))
|
||||||
|
#define DECODE_OK_PKT_N(v) ((v) & BIT(15))
|
||||||
|
#define ERR_COUNT_PKT_0(v) (((v) >> 0) & 0x3f)
|
||||||
|
#define ERR_COUNT_PKT_N(v) (((v) >> 8) & 0x3f)
|
||||||
|
|
||||||
|
/* Offsets relative to pbus_base */
|
||||||
|
#define PBUS_CS_CTRL 0x83c
|
||||||
|
#define PBUS_PAD_MODE 0x8f0
|
||||||
|
|
||||||
|
/* PBUS_CS_CTRL values */
|
||||||
|
#define PBUS_IORDY BIT(31)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PBUS_PAD_MODE values
|
||||||
|
* In raw mode, the driver communicates directly with the NAND chips.
|
||||||
|
* In NFC mode, the NAND Flash controller manages the communication.
|
||||||
|
* We use NFC mode for read and write; raw mode for everything else.
|
||||||
|
*/
|
||||||
|
#define MODE_RAW 0
|
||||||
|
#define MODE_NFC BIT(31)
|
||||||
|
|
||||||
|
#define METADATA_SIZE 4
|
||||||
|
#define BBM_SIZE 6
|
||||||
|
#define FIELD_ORDER 15
|
||||||
|
|
||||||
|
#define MAX_CS 4
|
||||||
|
|
||||||
|
struct tango_nfc {
|
||||||
|
struct nand_hw_control hw;
|
||||||
|
void __iomem *reg_base;
|
||||||
|
void __iomem *mem_base;
|
||||||
|
void __iomem *pbus_base;
|
||||||
|
struct tango_chip *chips[MAX_CS];
|
||||||
|
struct dma_chan *chan;
|
||||||
|
int freq_kHz;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw)
|
||||||
|
|
||||||
|
struct tango_chip {
|
||||||
|
struct nand_chip nand_chip;
|
||||||
|
void __iomem *base;
|
||||||
|
u32 timing1;
|
||||||
|
u32 timing2;
|
||||||
|
u32 xfer_cfg;
|
||||||
|
u32 pkt_0_cfg;
|
||||||
|
u32 pkt_n_cfg;
|
||||||
|
u32 bb_cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip)
|
||||||
|
|
||||||
|
#define XFER_CFG(cs, page_count, steps, metadata_size) \
|
||||||
|
((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size))
|
||||||
|
|
||||||
|
#define PKT_CFG(size, strength) ((size) << 16 | (strength))
|
||||||
|
|
||||||
|
#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size))
|
||||||
|
|
||||||
|
#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3))
|
||||||
|
|
||||||
|
static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
|
||||||
|
{
|
||||||
|
struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
|
||||||
|
|
||||||
|
if (ctrl & NAND_CLE)
|
||||||
|
writeb_relaxed(dat, tchip->base + PBUS_CMD);
|
||||||
|
|
||||||
|
if (ctrl & NAND_ALE)
|
||||||
|
writeb_relaxed(dat, tchip->base + PBUS_ADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_dev_ready(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||||
|
|
||||||
|
return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 tango_read_byte(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
|
||||||
|
|
||||||
|
return readb_relaxed(tchip->base + PBUS_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tango_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||||
|
{
|
||||||
|
struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
|
||||||
|
|
||||||
|
ioread8_rep(tchip->base + PBUS_DATA, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tango_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
|
||||||
|
{
|
||||||
|
struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
|
||||||
|
|
||||||
|
iowrite8_rep(tchip->base + PBUS_DATA, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tango_select_chip(struct mtd_info *mtd, int idx)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||||
|
struct tango_chip *tchip = to_tango_chip(chip);
|
||||||
|
|
||||||
|
if (idx < 0)
|
||||||
|
return; /* No "chip unselect" function */
|
||||||
|
|
||||||
|
writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1);
|
||||||
|
writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2);
|
||||||
|
writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG);
|
||||||
|
writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG);
|
||||||
|
writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG);
|
||||||
|
writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The controller does not check for bitflips in erased pages,
|
||||||
|
* therefore software must check instead.
|
||||||
|
*/
|
||||||
|
static int check_erased_page(struct nand_chip *chip, u8 *buf)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
u8 *meta = chip->oob_poi + BBM_SIZE;
|
||||||
|
u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE;
|
||||||
|
const int ecc_size = chip->ecc.bytes;
|
||||||
|
const int pkt_size = chip->ecc.size;
|
||||||
|
int i, res, meta_len, bitflips = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < chip->ecc.steps; ++i) {
|
||||||
|
meta_len = i ? 0 : METADATA_SIZE;
|
||||||
|
res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
|
||||||
|
meta, meta_len,
|
||||||
|
chip->ecc.strength);
|
||||||
|
if (res < 0)
|
||||||
|
mtd->ecc_stats.failed++;
|
||||||
|
|
||||||
|
bitflips = max(res, bitflips);
|
||||||
|
buf += pkt_size;
|
||||||
|
ecc += ecc_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitflips;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int decode_error_report(struct tango_nfc *nfc)
|
||||||
|
{
|
||||||
|
u32 status, res;
|
||||||
|
|
||||||
|
status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS);
|
||||||
|
if (status & PAGE_IS_EMPTY)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
res = readl_relaxed(nfc->mem_base + ERROR_REPORT);
|
||||||
|
|
||||||
|
if (DECODE_OK_PKT_0(res) && DECODE_OK_PKT_N(res))
|
||||||
|
return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res));
|
||||||
|
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tango_dma_callback(void *arg)
|
||||||
|
{
|
||||||
|
complete(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_dma(struct tango_nfc *nfc, int dir, int cmd, const void *buf,
|
||||||
|
int len, int page)
|
||||||
|
{
|
||||||
|
void __iomem *addr = nfc->reg_base + NFC_STATUS;
|
||||||
|
struct dma_chan *chan = nfc->chan;
|
||||||
|
struct dma_async_tx_descriptor *desc;
|
||||||
|
struct scatterlist sg;
|
||||||
|
struct completion tx_done;
|
||||||
|
int err = -EIO;
|
||||||
|
u32 res, val;
|
||||||
|
|
||||||
|
sg_init_one(&sg, buf, len);
|
||||||
|
if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir, DMA_PREP_INTERRUPT);
|
||||||
|
if (!desc)
|
||||||
|
goto dma_unmap;
|
||||||
|
|
||||||
|
desc->callback = tango_dma_callback;
|
||||||
|
desc->callback_param = &tx_done;
|
||||||
|
init_completion(&tx_done);
|
||||||
|
|
||||||
|
writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE);
|
||||||
|
|
||||||
|
writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE);
|
||||||
|
writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET);
|
||||||
|
writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD);
|
||||||
|
|
||||||
|
dmaengine_submit(desc);
|
||||||
|
dma_async_issue_pending(chan);
|
||||||
|
|
||||||
|
res = wait_for_completion_timeout(&tx_done, HZ);
|
||||||
|
if (res > 0)
|
||||||
|
err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000);
|
||||||
|
|
||||||
|
writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE);
|
||||||
|
|
||||||
|
dma_unmap:
|
||||||
|
dma_unmap_sg(chan->device->dev, &sg, 1, dir);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
u8 *buf, int oob_required, int page)
|
||||||
|
{
|
||||||
|
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||||
|
int err, res, len = mtd->writesize;
|
||||||
|
|
||||||
|
if (oob_required)
|
||||||
|
chip->ecc.read_oob(mtd, chip, page);
|
||||||
|
|
||||||
|
err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
res = decode_error_report(nfc);
|
||||||
|
if (res < 0) {
|
||||||
|
chip->ecc.read_oob_raw(mtd, chip, page);
|
||||||
|
res = check_erased_page(chip, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
const u8 *buf, int oob_required, int page)
|
||||||
|
{
|
||||||
|
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||||
|
int err, len = mtd->writesize;
|
||||||
|
|
||||||
|
/* Calling tango_write_oob() would send PAGEPROG twice */
|
||||||
|
if (oob_required)
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
writel_relaxed(0xffffffff, nfc->mem_base + METADATA);
|
||||||
|
err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
|
||||||
|
*pos += len;
|
||||||
|
|
||||||
|
if (!*buf) {
|
||||||
|
/* skip over "len" bytes */
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, *pos, -1);
|
||||||
|
} else {
|
||||||
|
tango_read_buf(mtd, *buf, len);
|
||||||
|
*buf += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
|
||||||
|
*pos += len;
|
||||||
|
|
||||||
|
if (!*buf) {
|
||||||
|
/* skip over "len" bytes */
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, *pos, -1);
|
||||||
|
} else {
|
||||||
|
tango_write_buf(mtd, *buf, len);
|
||||||
|
*buf += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Physical page layout (not drawn to scale)
|
||||||
|
*
|
||||||
|
* NB: Bad Block Marker area splits PKT_N in two (N1, N2).
|
||||||
|
*
|
||||||
|
* +---+-----------------+-------+-----+-----------+-----+----+-------+
|
||||||
|
* | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N |
|
||||||
|
* +---+-----------------+-------+-----+-----------+-----+----+-------+
|
||||||
|
*
|
||||||
|
* Logical page layout:
|
||||||
|
*
|
||||||
|
* +-----+---+-------+-----+-------+
|
||||||
|
* oob = | BBM | M | ECC_0 | ... | ECC_N |
|
||||||
|
* +-----+---+-------+-----+-------+
|
||||||
|
*
|
||||||
|
* +-----------------+-----+-----------------+
|
||||||
|
* buf = | PKT_0 | ... | PKT_N |
|
||||||
|
* +-----------------+-----+-----------------+
|
||||||
|
*/
|
||||||
|
static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
u8 *oob_orig = oob;
|
||||||
|
const int page_size = mtd->writesize;
|
||||||
|
const int ecc_size = chip->ecc.bytes;
|
||||||
|
const int pkt_size = chip->ecc.size;
|
||||||
|
int pos = 0; /* position within physical page */
|
||||||
|
int rem = page_size; /* bytes remaining until BBM area */
|
||||||
|
|
||||||
|
if (oob)
|
||||||
|
oob += BBM_SIZE;
|
||||||
|
|
||||||
|
aux_read(chip, &oob, METADATA_SIZE, &pos);
|
||||||
|
|
||||||
|
while (rem > pkt_size) {
|
||||||
|
aux_read(chip, &buf, pkt_size, &pos);
|
||||||
|
aux_read(chip, &oob, ecc_size, &pos);
|
||||||
|
rem = page_size - pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
aux_read(chip, &buf, rem, &pos);
|
||||||
|
aux_read(chip, &oob_orig, BBM_SIZE, &pos);
|
||||||
|
aux_read(chip, &buf, pkt_size - rem, &pos);
|
||||||
|
aux_read(chip, &oob, ecc_size, &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
const u8 *oob_orig = oob;
|
||||||
|
const int page_size = mtd->writesize;
|
||||||
|
const int ecc_size = chip->ecc.bytes;
|
||||||
|
const int pkt_size = chip->ecc.size;
|
||||||
|
int pos = 0; /* position within physical page */
|
||||||
|
int rem = page_size; /* bytes remaining until BBM area */
|
||||||
|
|
||||||
|
if (oob)
|
||||||
|
oob += BBM_SIZE;
|
||||||
|
|
||||||
|
aux_write(chip, &oob, METADATA_SIZE, &pos);
|
||||||
|
|
||||||
|
while (rem > pkt_size) {
|
||||||
|
aux_write(chip, &buf, pkt_size, &pos);
|
||||||
|
aux_write(chip, &oob, ecc_size, &pos);
|
||||||
|
rem = page_size - pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
aux_write(chip, &buf, rem, &pos);
|
||||||
|
aux_write(chip, &oob_orig, BBM_SIZE, &pos);
|
||||||
|
aux_write(chip, &buf, pkt_size - rem, &pos);
|
||||||
|
aux_write(chip, &oob, ecc_size, &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
u8 *buf, int oob_required, int page)
|
||||||
|
{
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
||||||
|
raw_read(chip, buf, chip->oob_poi);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
const u8 *buf, int oob_required, int page)
|
||||||
|
{
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
|
||||||
|
raw_write(chip, buf, chip->oob_poi);
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
int page)
|
||||||
|
{
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
||||||
|
raw_read(chip, NULL, chip->oob_poi);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
int page)
|
||||||
|
{
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
|
||||||
|
raw_write(chip, NULL, chip->oob_poi);
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||||
|
chip->waitfunc(mtd, chip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||||
|
|
||||||
|
if (idx >= ecc->steps)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx;
|
||||||
|
res->length = ecc->bytes;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
|
||||||
|
{
|
||||||
|
return -ERANGE; /* no free space in spare area */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = {
|
||||||
|
.ecc = oob_ecc,
|
||||||
|
.free = oob_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32 to_ticks(int kHz, int ps)
|
||||||
|
{
|
||||||
|
return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_set_timings(struct mtd_info *mtd,
|
||||||
|
const struct nand_data_interface *conf,
|
||||||
|
bool check_only)
|
||||||
|
{
|
||||||
|
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||||
|
struct tango_chip *tchip = to_tango_chip(chip);
|
||||||
|
u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr;
|
||||||
|
int kHz = nfc->freq_kHz;
|
||||||
|
|
||||||
|
if (IS_ERR(sdr))
|
||||||
|
return PTR_ERR(sdr);
|
||||||
|
|
||||||
|
if (check_only)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max);
|
||||||
|
Textw = to_ticks(kHz, sdr->tWB_max);
|
||||||
|
Twc = to_ticks(kHz, sdr->tWC_min);
|
||||||
|
Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min);
|
||||||
|
|
||||||
|
Tacc = to_ticks(kHz, sdr->tREA_max);
|
||||||
|
Thold = to_ticks(kHz, sdr->tREH_min);
|
||||||
|
Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min);
|
||||||
|
Textr = to_ticks(kHz, sdr->tRHZ_max);
|
||||||
|
|
||||||
|
tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw);
|
||||||
|
tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chip_init(struct device *dev, struct device_node *np)
|
||||||
|
{
|
||||||
|
u32 cs;
|
||||||
|
int err, res;
|
||||||
|
struct mtd_info *mtd;
|
||||||
|
struct nand_chip *chip;
|
||||||
|
struct tango_chip *tchip;
|
||||||
|
struct nand_ecc_ctrl *ecc;
|
||||||
|
struct tango_nfc *nfc = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL);
|
||||||
|
if (!tchip)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = of_property_count_u32_elems(np, "reg");
|
||||||
|
if (res < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (res != 1)
|
||||||
|
return -ENOTSUPP; /* Multi-CS chips are not supported */
|
||||||
|
|
||||||
|
err = of_property_read_u32_index(np, "reg", 0, &cs);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (cs >= MAX_CS)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
chip = &tchip->nand_chip;
|
||||||
|
ecc = &chip->ecc;
|
||||||
|
mtd = nand_to_mtd(chip);
|
||||||
|
|
||||||
|
chip->read_byte = tango_read_byte;
|
||||||
|
chip->write_buf = tango_write_buf;
|
||||||
|
chip->read_buf = tango_read_buf;
|
||||||
|
chip->select_chip = tango_select_chip;
|
||||||
|
chip->cmd_ctrl = tango_cmd_ctrl;
|
||||||
|
chip->dev_ready = tango_dev_ready;
|
||||||
|
chip->setup_data_interface = tango_set_timings;
|
||||||
|
chip->options = NAND_USE_BOUNCE_BUFFER |
|
||||||
|
NAND_NO_SUBPAGE_WRITE |
|
||||||
|
NAND_WAIT_TCCS;
|
||||||
|
chip->controller = &nfc->hw;
|
||||||
|
tchip->base = nfc->pbus_base + (cs * 256);
|
||||||
|
|
||||||
|
nand_set_flash_node(chip, np);
|
||||||
|
mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops);
|
||||||
|
mtd->dev.parent = dev;
|
||||||
|
|
||||||
|
err = nand_scan_ident(mtd, 1, NULL);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ecc->mode = NAND_ECC_HW;
|
||||||
|
ecc->algo = NAND_ECC_BCH;
|
||||||
|
ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE);
|
||||||
|
|
||||||
|
ecc->read_page_raw = tango_read_page_raw;
|
||||||
|
ecc->write_page_raw = tango_write_page_raw;
|
||||||
|
ecc->read_page = tango_read_page;
|
||||||
|
ecc->write_page = tango_write_page;
|
||||||
|
ecc->read_oob = tango_read_oob;
|
||||||
|
ecc->write_oob = tango_write_oob;
|
||||||
|
ecc->options = NAND_ECC_CUSTOM_PAGE_ACCESS;
|
||||||
|
|
||||||
|
err = nand_scan_tail(mtd);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE);
|
||||||
|
tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength);
|
||||||
|
tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength);
|
||||||
|
tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE);
|
||||||
|
|
||||||
|
err = mtd_device_register(mtd, NULL, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
nfc->chips[cs] = tchip;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_nand_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int cs;
|
||||||
|
struct tango_nfc *nfc = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
dma_release_channel(nfc->chan);
|
||||||
|
|
||||||
|
for (cs = 0; cs < MAX_CS; ++cs) {
|
||||||
|
if (nfc->chips[cs])
|
||||||
|
nand_release(nand_to_mtd(&nfc->chips[cs]->nand_chip));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tango_nand_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct clk *clk;
|
||||||
|
struct resource *res;
|
||||||
|
struct tango_nfc *nfc;
|
||||||
|
struct device_node *np;
|
||||||
|
|
||||||
|
nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
|
||||||
|
if (!nfc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
nfc->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(nfc->reg_base))
|
||||||
|
return PTR_ERR(nfc->reg_base);
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
nfc->mem_base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(nfc->mem_base))
|
||||||
|
return PTR_ERR(nfc->mem_base);
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||||
|
nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(nfc->pbus_base))
|
||||||
|
return PTR_ERR(nfc->pbus_base);
|
||||||
|
|
||||||
|
clk = clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(clk))
|
||||||
|
return PTR_ERR(clk);
|
||||||
|
|
||||||
|
nfc->chan = dma_request_chan(&pdev->dev, "nfc_sbox");
|
||||||
|
if (IS_ERR(nfc->chan))
|
||||||
|
return PTR_ERR(nfc->chan);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, nfc);
|
||||||
|
nand_hw_control_init(&nfc->hw);
|
||||||
|
nfc->freq_kHz = clk_get_rate(clk) / 1000;
|
||||||
|
|
||||||
|
for_each_child_of_node(pdev->dev.of_node, np) {
|
||||||
|
err = chip_init(&pdev->dev, np);
|
||||||
|
if (err) {
|
||||||
|
tango_nand_remove(pdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id tango_nand_ids[] = {
|
||||||
|
{ .compatible = "sigma,smp8758-nand" },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver tango_nand_driver = {
|
||||||
|
.probe = tango_nand_probe,
|
||||||
|
.remove = tango_nand_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "tango-nand",
|
||||||
|
.of_match_table = tango_nand_ids,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(tango_nand_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Sigma Designs");
|
||||||
|
MODULE_DESCRIPTION("Tango4 NAND Flash controller driver");
|
|
@ -435,10 +435,10 @@ static int tmio_probe(struct platform_device *dev)
|
||||||
nand_chip->waitfunc = tmio_nand_wait;
|
nand_chip->waitfunc = tmio_nand_wait;
|
||||||
|
|
||||||
/* Scan to find existence of the device */
|
/* Scan to find existence of the device */
|
||||||
if (nand_scan(mtd, 1)) {
|
retval = nand_scan(mtd, 1);
|
||||||
retval = -ENODEV;
|
if (retval)
|
||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
|
||||||
/* Register the partitions */
|
/* Register the partitions */
|
||||||
retval = mtd_device_parse_register(mtd, NULL, NULL,
|
retval = mtd_device_parse_register(mtd, NULL, NULL,
|
||||||
data ? data->partition : NULL,
|
data ? data->partition : NULL,
|
||||||
|
|
|
@ -717,10 +717,9 @@ static int vf610_nfc_probe(struct platform_device *pdev)
|
||||||
vf610_nfc_preinit_controller(nfc);
|
vf610_nfc_preinit_controller(nfc);
|
||||||
|
|
||||||
/* first scan to find the device and get the page size */
|
/* first scan to find the device and get the page size */
|
||||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
err = nand_scan_ident(mtd, 1, NULL);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
|
|
||||||
vf610_nfc_init_controller(nfc);
|
vf610_nfc_init_controller(nfc);
|
||||||
|
|
||||||
|
@ -775,10 +774,9 @@ static int vf610_nfc_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* second phase scan */
|
/* second phase scan */
|
||||||
if (nand_scan_tail(mtd)) {
|
err = nand_scan_tail(mtd);
|
||||||
err = -ENXIO;
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, mtd);
|
platform_set_drvdata(pdev, mtd);
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,12 @@ enum nand_ecc_algo {
|
||||||
*/
|
*/
|
||||||
#define NAND_ECC_GENERIC_ERASED_CHECK BIT(0)
|
#define NAND_ECC_GENERIC_ERASED_CHECK BIT(0)
|
||||||
#define NAND_ECC_MAXIMIZE BIT(1)
|
#define NAND_ECC_MAXIMIZE BIT(1)
|
||||||
|
/*
|
||||||
|
* If your controller already sends the required NAND commands when
|
||||||
|
* reading or writing a page, then the framework is not supposed to
|
||||||
|
* send READ0 and SEQIN/PAGEPROG respectively.
|
||||||
|
*/
|
||||||
|
#define NAND_ECC_CUSTOM_PAGE_ACCESS BIT(2)
|
||||||
|
|
||||||
/* Bit mask for flags passed to do_nand_read_ecc */
|
/* Bit mask for flags passed to do_nand_read_ecc */
|
||||||
#define NAND_GET_DEVICE 0x80
|
#define NAND_GET_DEVICE 0x80
|
||||||
|
@ -186,6 +192,7 @@ enum nand_ecc_algo {
|
||||||
/* Macros to identify the above */
|
/* Macros to identify the above */
|
||||||
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
|
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
|
||||||
#define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ))
|
#define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ))
|
||||||
|
#define NAND_HAS_SUBPAGE_WRITE(chip) !((chip)->options & NAND_NO_SUBPAGE_WRITE)
|
||||||
|
|
||||||
/* Non chip related options */
|
/* Non chip related options */
|
||||||
/* This option skips the bbt scan during initialization. */
|
/* This option skips the bbt scan during initialization. */
|
||||||
|
@ -210,6 +217,16 @@ enum nand_ecc_algo {
|
||||||
*/
|
*/
|
||||||
#define NAND_USE_BOUNCE_BUFFER 0x00100000
|
#define NAND_USE_BOUNCE_BUFFER 0x00100000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In case your controller is implementing ->cmd_ctrl() and is relying on the
|
||||||
|
* default ->cmdfunc() implementation, you may want to let the core handle the
|
||||||
|
* tCCS delay which is required when a column change (RNDIN or RNDOUT) is
|
||||||
|
* requested.
|
||||||
|
* If your controller already takes care of this delay, you don't need to set
|
||||||
|
* this flag.
|
||||||
|
*/
|
||||||
|
#define NAND_WAIT_TCCS 0x00200000
|
||||||
|
|
||||||
/* Options set by nand scan */
|
/* Options set by nand scan */
|
||||||
/* Nand scan has allocated controller struct */
|
/* Nand scan has allocated controller struct */
|
||||||
#define NAND_CONTROLLER_ALLOC 0x80000000
|
#define NAND_CONTROLLER_ALLOC 0x80000000
|
||||||
|
@ -558,6 +575,11 @@ struct nand_ecc_ctrl {
|
||||||
int page);
|
int page);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline int nand_standard_page_accessors(struct nand_ecc_ctrl *ecc)
|
||||||
|
{
|
||||||
|
return !(ecc->options & NAND_ECC_CUSTOM_PAGE_ACCESS);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nand_buffers - buffer structure for read/write
|
* struct nand_buffers - buffer structure for read/write
|
||||||
* @ecccalc: buffer pointer for calculated ECC, size is oobsize.
|
* @ecccalc: buffer pointer for calculated ECC, size is oobsize.
|
||||||
|
@ -584,6 +606,10 @@ struct nand_buffers {
|
||||||
*
|
*
|
||||||
* All these timings are expressed in picoseconds.
|
* All these timings are expressed in picoseconds.
|
||||||
*
|
*
|
||||||
|
* @tBERS_max: Block erase time
|
||||||
|
* @tCCS_min: Change column setup time
|
||||||
|
* @tPROG_max: Page program time
|
||||||
|
* @tR_max: Page read time
|
||||||
* @tALH_min: ALE hold time
|
* @tALH_min: ALE hold time
|
||||||
* @tADL_min: ALE to data loading time
|
* @tADL_min: ALE to data loading time
|
||||||
* @tALS_min: ALE setup time
|
* @tALS_min: ALE setup time
|
||||||
|
@ -621,6 +647,10 @@ struct nand_buffers {
|
||||||
* @tWW_min: WP# transition to WE# low
|
* @tWW_min: WP# transition to WE# low
|
||||||
*/
|
*/
|
||||||
struct nand_sdr_timings {
|
struct nand_sdr_timings {
|
||||||
|
u32 tBERS_max;
|
||||||
|
u32 tCCS_min;
|
||||||
|
u32 tPROG_max;
|
||||||
|
u32 tR_max;
|
||||||
u32 tALH_min;
|
u32 tALH_min;
|
||||||
u32 tADL_min;
|
u32 tADL_min;
|
||||||
u32 tALS_min;
|
u32 tALS_min;
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
#ifndef __MTD_NAND_S3C2410_H
|
#ifndef __MTD_NAND_S3C2410_H
|
||||||
#define __MTD_NAND_S3C2410_H
|
#define __MTD_NAND_S3C2410_H
|
||||||
|
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct s3c2410_nand_set - define a set of one or more nand chips
|
* struct s3c2410_nand_set - define a set of one or more nand chips
|
||||||
* @disable_ecc: Entirely disable ECC - Dangerous
|
|
||||||
* @flash_bbt: Openmoko u-boot can create a Bad Block Table
|
* @flash_bbt: Openmoko u-boot can create a Bad Block Table
|
||||||
* Setting this flag will allow the kernel to
|
* Setting this flag will allow the kernel to
|
||||||
* look for it at boot time and also skip the NAND
|
* look for it at boot time and also skip the NAND
|
||||||
|
@ -31,7 +32,6 @@
|
||||||
* a warning at boot time.
|
* a warning at boot time.
|
||||||
*/
|
*/
|
||||||
struct s3c2410_nand_set {
|
struct s3c2410_nand_set {
|
||||||
unsigned int disable_ecc:1;
|
|
||||||
unsigned int flash_bbt:1;
|
unsigned int flash_bbt:1;
|
||||||
|
|
||||||
unsigned int options;
|
unsigned int options;
|
||||||
|
@ -40,6 +40,7 @@ struct s3c2410_nand_set {
|
||||||
char *name;
|
char *name;
|
||||||
int *nr_map;
|
int *nr_map;
|
||||||
struct mtd_partition *partitions;
|
struct mtd_partition *partitions;
|
||||||
|
struct device_node *of_node;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s3c2410_platform_nand {
|
struct s3c2410_platform_nand {
|
||||||
|
@ -51,6 +52,8 @@ struct s3c2410_platform_nand {
|
||||||
|
|
||||||
unsigned int ignore_unset_ecc:1;
|
unsigned int ignore_unset_ecc:1;
|
||||||
|
|
||||||
|
nand_ecc_modes_t ecc_mode;
|
||||||
|
|
||||||
int nr_sets;
|
int nr_sets;
|
||||||
struct s3c2410_nand_set *sets;
|
struct s3c2410_nand_set *sets;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue