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:
Brian Norris 2016-11-29 18:28:30 -08:00
commit 0989b0909c
57 changed files with 1477 additions and 319 deletions

View File

@ -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>;
};
};
};

View File

@ -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>;
};
};
};
};

View File

@ -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>;
};
};

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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,
}; };

View File

@ -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)

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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[] = {

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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, &section, ret = mtd_ooblayout_find_region(mtd, start, &section,
&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, &section, ret = mtd_ooblayout_find_region(mtd, start, &section,
&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) {

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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);
} }

View File

@ -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) +

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;
}; };

View File

@ -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;
} }

View File

@ -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"

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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,

View File

@ -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);

View File

@ -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) },

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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,

View File

@ -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);

View File

@ -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");

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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,
}, },
}; };

View File

@ -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)

View File

@ -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);
} }

View File

@ -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");

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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;