Ben Gardner <bgardner@wabtec.com>
Ben M Cahill <ben.m.cahill@intel.com>
Björn Steinbrink <B.Steinbrink@gmx.de>
-Boris Brezillon <boris.brezillon@free-electrons.com>
-Boris Brezillon <boris.brezillon@free-electrons.com> <b.brezillon.dev@gmail.com>
-Boris Brezillon <boris.brezillon@free-electrons.com> <b.brezillon@overkiz.com>
+Boris Brezillon <boris.brezillon@bootlin.com> <boris.brezillon@free-electrons.com>
+Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon.dev@gmail.com>
+Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon@overkiz.com>
Brian Avery <b.avery@hp.com>
Brian King <brking@us.ibm.com>
Christoph Hellwig <hch@lst.de>
Michael Buesch <m@bues.ch>
Michel Dänzer <michel@tungstengraphics.com>
Miodrag Dinic <miodrag.dinic@mips.com> <miodrag.dinic@imgtec.com>
+Miquel Raynal <miquel.raynal@bootlin.com> <miquel.raynal@free-electrons.com>
Mitesh shah <mshah@teja.com>
Mohit Kumar <mohit.kumar@st.com> <mohit.kumar.dhaka@gmail.com>
Morten Welinder <terra@gnome.org>
----
The NAND hardware is similar to the S3C2440, and is supported by the
- s3c2410 driver in the drivers/mtd/nand directory.
+ s3c2410 driver in the drivers/mtd/nand/raw directory.
USB Host
- #address-cells: shall be set to 1. Encode the NAND CS.
- #size-cells: shall be set to 0.
- interrupts: shall define the NAND controller interrupt.
-- clocks: shall reference the NAND controller clock.
+- clocks: shall reference the NAND controller clocks, the second one is
+ is only needed for the Armada 7K/8K SoCs
+- clock-names: mandatory if there is a second clock, in this case there
+ should be one clock named "core" and another one named "reg"
- marvell,system-controller: Set to retrieve the syscon node that handles
NAND controller related registers (only required with the
"marvell,armada-8k-nand[-controller]" compatibles).
+++ /dev/null
-PXA3xx NAND DT bindings
-
-Required properties:
-
- - compatible: Should be set to one of the following:
- marvell,pxa3xx-nand
- marvell,armada370-nand
- marvell,armada-8k-nand
- - reg: The register base for the controller
- - interrupts: The interrupt to map
- - #address-cells: Set to <1> if the node includes partitions
- - marvell,system-controller: Set to retrieve the syscon node that handles
- NAND controller related registers (only required
- with marvell,armada-8k-nand compatible).
-
-Optional properties:
-
- - dmas: dma data channel, see dma.txt binding doc
- - marvell,nand-enable-arbiter: Set to enable the bus arbiter
- - marvell,nand-keep-config: Set to keep the NAND controller config as set
- by the bootloader
- - num-cs: Number of chipselect lines to use
- - nand-on-flash-bbt: boolean to enable on flash bbt option if
- not present false
- - nand-ecc-strength: number of bits to correct per ECC step
- - nand-ecc-step-size: number of data bytes covered by a single ECC step
-
-The following ECC strength and step size are currently supported:
-
- - nand-ecc-strength = <1>, nand-ecc-step-size = <512>
- - nand-ecc-strength = <4>, nand-ecc-step-size = <512>
- - nand-ecc-strength = <8>, nand-ecc-step-size = <512>
-
-Example:
-
- nand0: nand@43100000 {
- compatible = "marvell,pxa3xx-nand";
- reg = <0x43100000 90>;
- interrupts = <45>;
- dmas = <&pdma 97 0>;
- dma-names = "data";
- #address-cells = <1>;
-
- marvell,nand-enable-arbiter;
- marvell,nand-keep-config;
- num-cs = <1>;
-
- /* partitions (optional) */
- };
-
- allwinner,rb : shall contain the native Ready/Busy ids.
or
- rb-gpios : shall contain the gpios used as R/B pins.
-- nand-ecc-mode : one of the supported ECC modes ("hw", "hw_syndrome", "soft",
- "soft_bch" or "none")
+- nand-ecc-mode : one of the supported ECC modes ("hw", "soft", "soft_bch" or
+ "none")
see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
which is marked with an [XXX] identifier. See the chapter "Documentation
hints" for an explanation.
-.. kernel-doc:: drivers/mtd/nand/nand_base.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c
:export:
-.. kernel-doc:: drivers/mtd/nand/nand_ecc.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_ecc.c
:export:
Internal Functions Provided
for an explanation. The functions marked with [DEFAULT] might be
relevant for a board driver developer.
-.. kernel-doc:: drivers/mtd/nand/nand_base.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c
:internal:
-.. kernel-doc:: drivers/mtd/nand/nand_bbt.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_bbt.c
:internal:
Credits
it from 1-to-0-to-1. If that hardware does not receive its "ping"
periodically, it will reset the system.
-- gpio-nand: drivers/mtd/nand/gpio.c is used to connect a NAND flash chip to
- a set of simple GPIO lines: RDY, NCE, ALE, CLE, NWP. It interacts with the
+- gpio-nand: drivers/mtd/nand/raw/gpio.c is used to connect a NAND flash chip
+ to a set of simple GPIO lines: RDY, NCE, ALE, CLE, NWP. It interacts with the
NAND flash MTD subsystem and provides chip access and partition parsing like
any other NAND driving hardware.
F: drivers/*/*aspeed*
ARM/ATMEL AT91 Clock Support
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
S: Maintained
F: drivers/clk/at91
F: drivers/input/touchscreen/w90p910_ts.c
F: drivers/watchdog/nuc900_wdt.c
F: drivers/net/ethernet/nuvoton/w90p910_ether.c
-F: drivers/mtd/nand/nuc900_nand.c
+F: drivers/mtd/nand/raw/nuc900_nand.c
F: drivers/rtc/rtc-nuc900.c
F: drivers/spi/spi-nuc900.c
F: drivers/usb/host/ehci-w90x900.c
L: linux-mtd@lists.infradead.org
L: bcm-kernel-feedback-list@broadcom.com
S: Maintained
-F: drivers/mtd/nand/brcmnand/
+F: drivers/mtd/nand/raw/brcmnand/
BROADCOM STB DPFE DRIVER
M: Markus Mayer <mmayer@broadcom.com>
M: Masahiro Yamada <yamada.masahiro@socionext.com>
L: linux-mtd@lists.infradead.org
S: Supported
-F: drivers/mtd/nand/denali*
+F: drivers/mtd/nand/raw/denali*
DESIGNWARE USB2 DRD IP DRIVER
M: John Youn <johnyoun@synopsys.com>
T: git git://anongit.freedesktop.org/drm/drm-misc
DRM DRIVERS FOR ATMEL HLCDC
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
L: dri-devel@lists.freedesktop.org
S: Supported
F: drivers/gpu/drm/atmel-hlcdc/
M: Han Xu <han.xu@nxp.com>
L: linux-mtd@lists.infradead.org
S: Maintained
-F: drivers/mtd/nand/gpmi-nand/*
+F: drivers/mtd/nand/raw/gpmi-nand/*
FREESCALE I2C CPM DRIVER
M: Jochen Friedrich <jochen@scram.de>
M: Harvey Hunt <harveyhuntnexus@gmail.com>
L: linux-mtd@lists.infradead.org
S: Maintained
-F: drivers/mtd/nand/jz4780_*
+F: drivers/mtd/nand/raw/jz4780_*
INOTIFY
M: Jan Kara <jack@suse.cz>
F: Documentation/devicetree/bindings/display/armada/
MARVELL CRYPTO DRIVER
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
M: Arnaud Ebalard <arno@natisbad.org>
F: drivers/crypto/marvell/
S: Maintained
F: drivers/net/wireless/marvell/mwl8k.c
MARVELL NAND CONTROLLER DRIVER
-M: Miquel Raynal <miquel.raynal@free-electrons.com>
+M: Miquel Raynal <miquel.raynal@bootlin.com>
L: linux-mtd@lists.infradead.org
S: Maintained
-F: drivers/mtd/nand/marvell_nand.c
+F: drivers/mtd/nand/raw/marvell_nand.c
F: Documentation/devicetree/bindings/mtd/marvell-nand.txt
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
MEMORY TECHNOLOGY DEVICES (MTD)
M: David Woodhouse <dwmw2@infradead.org>
M: Brian Norris <computersforpeace@gmail.com>
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
M: Marek Vasut <marek.vasut@gmail.com>
M: Richard Weinberger <richard@nod.at>
L: linux-mtd@lists.infradead.org
M: Josh Wu <rainyfeeling@outlook.com>
L: linux-mtd@lists.infradead.org
S: Supported
-F: drivers/mtd/nand/atmel/*
+F: drivers/mtd/nand/raw/atmel/*
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
F: drivers/net/ethernet/myricom/myri10ge/
NAND FLASH SUBSYSTEM
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
R: Richard Weinberger <richard@nod.at>
L: linux-mtd@lists.infradead.org
W: http://www.linux-mtd.infradead.org/
M: Kyungmin Park <kyungmin.park@samsung.com>
L: linux-mtd@lists.infradead.org
S: Maintained
-F: drivers/mtd/onenand/
+F: drivers/mtd/nand/onenand/
F: include/linux/mtd/onenand*.h
ONSTREAM SCSI TAPE DRIVER
F: sound/arm/pxa*
F: sound/soc/pxa/
-PXA3xx NAND FLASH DRIVER
-M: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
-L: linux-mtd@lists.infradead.org
-S: Maintained
-F: drivers/mtd/nand/pxa3xx_nand.c
-
QAT DRIVER
M: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
L: qat-linux@intel.com
RICOH SMARTMEDIA/XD DRIVER
M: Maxim Levitsky <maximlevitsky@gmail.com>
S: Maintained
-F: drivers/mtd/nand/r852.c
-F: drivers/mtd/nand/r852.h
+F: drivers/mtd/nand/raw/r852.c
+F: drivers/mtd/nand/raw/r852.h
RISC-V ARCHITECTURE
M: Palmer Dabbelt <palmer@sifive.com>
M: Stefan Agner <stefan@agner.ch>
L: linux-mtd@lists.infradead.org
S: Supported
-F: drivers/mtd/nand/vf610_nfc.c
+F: drivers/mtd/nand/raw/vf610_nfc.c
VFAT/FAT/MSDOS FILESYSTEM
M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
status = "disabled";
};
- nand0: nand@43100000 {
- compatible = "marvell,pxa3xx-nand";
+ nand_controller: nand-controller@43100000 {
+ compatible = "marvell,pxa3xx-nand-controller";
reg = <0x43100000 90>;
interrupts = <45>;
clocks = <&clks CLK_NAND>;
dmas = <&pdma 97 3>;
dma-names = "data";
#address-cells = <1>;
- #size-cells = <1>;
+ #size-cells = <0>;
status = "disabled";
};
CONFIG_MTD=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_PXA3xx=y
+CONFIG_MTD_NAND_MARVELL=y
CONFIG_MTD_UBI=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_MTD=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_PXA3xx=y
-CONFIG_MTD_NAND_PXA3xx_BUILTIN=y
+CONFIG_MTD_NAND_MARVELL=y
CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y
CONFIG_MTD_ONENAND_GENERIC=y
CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH=y
CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE=y
CONFIG_MTD_NAND_SHARPSL=m
-CONFIG_MTD_NAND_PXA3xx=m
+CONFIG_MTD_NAND_MARVELL=m
CONFIG_MTD_NAND_CM_X270=m
CONFIG_MTD_NAND_TMIO=m
CONFIG_MTD_NAND_BRCMNAND=m
CONFIG_NFTL_RW=y
CONFIG_MTD_BLOCK2MTD=y
CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_PXA3xx=y
+CONFIG_MTD_NAND_MARVELL=y
CONFIG_MTD_UBI=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_ISL29003=y
};
static struct pxa3xx_nand_platform_data aspenite_nand_info = {
- .enable_arbiter = 1,
- .num_cs = 1,
- .parts[0] = aspenite_nand_partitions,
- .nr_parts[0] = ARRAY_SIZE(aspenite_nand_partitions),
+ .parts = aspenite_nand_partitions,
+ .nr_parts = ARRAY_SIZE(aspenite_nand_partitions),
};
static struct i2c_board_info aspenite_i2c_info[] __initdata = {
#endif
#endif
-#if IS_ENABLED(CONFIG_MTD_NAND_PXA3xx)
-static struct pxa3xx_nand_platform_data dkb_nand_info = {
- .enable_arbiter = 1,
- .num_cs = 1,
-};
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
+static struct pxa3xx_nand_platform_data dkb_nand_info = {};
#endif
#if IS_ENABLED(CONFIG_MMP_DISP)
/* on-chip devices */
pxa910_add_uart(1);
-#if IS_ENABLED(CONFIG_MTD_NAND_PXA3xx)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
pxa910_add_nand(&dkb_nand_info);
#endif
static inline void cm_x300_init_ac97(void) {}
#endif
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
static struct mtd_partition cm_x300_nand_partitions[] = {
[0] = {
.name = "OBM",
};
static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
- .enable_arbiter = 1,
.keep_config = 1,
- .num_cs = 1,
- .parts[0] = cm_x300_nand_partitions,
- .nr_parts[0] = ARRAY_SIZE(cm_x300_nand_partitions),
+ .parts = cm_x300_nand_partitions,
+ .nr_parts = ARRAY_SIZE(cm_x300_nand_partitions),
};
static void __init cm_x300_init_nand(void)
}
#endif
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
static struct mtd_partition colibri_nand_partitions[] = {
{
.name = "bootloader",
};
static struct pxa3xx_nand_platform_data colibri_nand_info = {
- .enable_arbiter = 1,
.keep_config = 1,
- .num_cs = 1,
- .parts[0] = colibri_nand_partitions,
- .nr_parts[0] = ARRAY_SIZE(colibri_nand_partitions),
+ .parts = colibri_nand_partitions,
+ .nr_parts = ARRAY_SIZE(colibri_nand_partitions),
};
void __init colibri_pxa3xx_init_nand(void)
extern void colibri_pxa3xx_init_eth(struct ax_plat_data *plat_data);
#endif
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
extern void colibri_pxa3xx_init_nand(void);
#else
static inline void colibri_pxa3xx_init_nand(void) {}
static inline void littleton_init_mmc(void) {}
#endif
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
static struct mtd_partition littleton_nand_partitions[] = {
[0] = {
.name = "Bootloader",
};
static struct pxa3xx_nand_platform_data littleton_nand_info = {
- .enable_arbiter = 1,
- .num_cs = 1,
- .parts[0] = littleton_nand_partitions,
- .nr_parts[0] = ARRAY_SIZE(littleton_nand_partitions),
+ .parts = littleton_nand_partitions,
+ .nr_parts = ARRAY_SIZE(littleton_nand_partitions),
};
static void __init littleton_init_nand(void)
}
#else
static inline void littleton_init_nand(void) {}
-#endif /* CONFIG_MTD_NAND_PXA3xx || CONFIG_MTD_NAND_PXA3xx_MODULE */
+#endif /* IS_ENABLED(CONFIG_MTD_NAND_MARVELL) */
#if defined(CONFIG_I2C_PXA) || defined(CONFIG_I2C_PXA_MODULE)
static struct led_info littleton_da9034_leds[] = {
}
/* NAND flash Support */
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
#define NAND_BLOCK_SIZE SZ_128K
#define NB(x) (NAND_BLOCK_SIZE * (x))
static struct mtd_partition mxm_8x10_nand_partitions[] = {
};
static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
- .enable_arbiter = 1,
.keep_config = 1,
- .num_cs = 1,
- .parts[0] = mxm_8x10_nand_partitions,
- .nr_parts[0] = ARRAY_SIZE(mxm_8x10_nand_partitions)
+ .parts = mxm_8x10_nand_partitions,
+ .nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
};
static void __init mxm_8x10_nand_init(void)
}
#else
static inline void mxm_8x10_nand_init(void) {}
-#endif /* CONFIG_MTD_NAND_PXA3xx || CONFIG_MTD_NAND_PXA3xx_MODULE */
+#endif /* IS_ENABLED(CONFIG_MTD_NAND_MARVELL) */
/* Ethernet support: Davicom DM9000 */
static struct resource dm9k_resources[] = {
};
static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
- .enable_arbiter = 1,
.keep_config = 1,
- .num_cs = 1,
- .parts[0] = raumfeld_nand_partitions,
- .nr_parts[0] = ARRAY_SIZE(raumfeld_nand_partitions),
+ .parts = raumfeld_nand_partitions,
+ .nr_parts = ARRAY_SIZE(raumfeld_nand_partitions),
};
/**
static inline void zylonite_init_keypad(void) {}
#endif
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
static struct mtd_partition zylonite_nand_partitions[] = {
[0] = {
.name = "Bootloader",
};
static struct pxa3xx_nand_platform_data zylonite_nand_info = {
- .enable_arbiter = 1,
- .num_cs = 1,
- .parts[0] = zylonite_nand_partitions,
- .nr_parts[0] = ARRAY_SIZE(zylonite_nand_partitions),
+ .parts = zylonite_nand_partitions,
+ .nr_parts = ARRAY_SIZE(zylonite_nand_partitions),
};
static void __init zylonite_init_nand(void)
}
#else
static inline void zylonite_init_nand(void) {}
-#endif /* CONFIG_MTD_NAND_PXA3xx || CONFIG_MTD_NAND_PXA3xx_MODULE */
+#endif /* IS_ENABLED(CONFIG_MTD_NAND_MARVELL) */
#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
static struct pxaohci_platform_data zylonite_ohci_info = {
*
* Copyright (c) 2007
*
- * Derived from drivers/mtd/nand/spia.c
+ * Derived from drivers/mtd/nand/spia.c (removed in v3.8)
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* This program is free software; you can redistribute it and/or modify
*
* Copyright (c) 2004
*
- * Derived from drivers/mtd/nand/spia.c
+ * Derived from drivers/mtd/nand/spia.c (removed in v3.8)
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* This program is free software; you can redistribute it and/or modify
source "drivers/mtd/nand/Kconfig"
-source "drivers/mtd/onenand/Kconfig"
-
source "drivers/mtd/lpddr/Kconfig"
source "drivers/mtd/spi-nor/Kconfig"
nftl-objs := nftlcore.o nftlmount.o
inftl-objs := inftlcore.o inftlmount.o
-obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
+obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/
-config MTD_NAND_ECC
+config MTD_NAND_CORE
tristate
-config MTD_NAND_ECC_SMC
- bool "NAND ECC Smart Media byte order"
- depends on MTD_NAND_ECC
- default n
- help
- Software ECC according to the Smart Media Specification.
- The original Linux implementation had byte 0 and 1 swapped.
+source "drivers/mtd/nand/onenand/Kconfig"
-
-menuconfig MTD_NAND
- tristate "NAND Device Support"
- depends on MTD
- select MTD_NAND_ECC
- help
- This enables support for accessing all type of NAND flash
- devices. For further information see
- <http://www.linux-mtd.infradead.org/doc/nand.html>.
-
-if MTD_NAND
-
-config MTD_NAND_BCH
- tristate
- select BCH
- depends on MTD_NAND_ECC_BCH
- default MTD_NAND
-
-config MTD_NAND_ECC_BCH
- bool "Support software BCH ECC"
- default n
- help
- This enables support for software BCH error correction. Binary BCH
- codes are more powerful and cpu intensive than traditional Hamming
- ECC codes. They are used with NAND devices requiring more than 1 bit
- of error correction.
-
-config MTD_SM_COMMON
- tristate
- default n
-
-config MTD_NAND_DENALI
- tristate
-
-config MTD_NAND_DENALI_PCI
- tristate "Support Denali NAND controller on Intel Moorestown"
- select MTD_NAND_DENALI
- depends on HAS_DMA && PCI
- help
- Enable the driver for NAND flash on Intel Moorestown, using the
- Denali NAND controller core.
-
-config MTD_NAND_DENALI_DT
- tristate "Support Denali NAND controller as a DT device"
- select MTD_NAND_DENALI
- depends on HAS_DMA && HAVE_CLK && OF
- help
- Enable the driver for NAND flash on platforms using a Denali NAND
- controller as a DT device.
-
-config MTD_NAND_GPIO
- tristate "GPIO assisted NAND Flash driver"
- depends on GPIOLIB || COMPILE_TEST
- depends on HAS_IOMEM
- help
- This enables a NAND flash driver where control signals are
- connected to GPIO pins, and commands and data are communicated
- via a memory mapped interface.
-
-config MTD_NAND_AMS_DELTA
- tristate "NAND Flash device on Amstrad E3"
- depends on MACH_AMS_DELTA
- default y
- help
- Support for NAND flash on Amstrad E3 (Delta).
-
-config MTD_NAND_OMAP2
- tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
- depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
- help
- Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
- and Keystone platforms.
-
-config MTD_NAND_OMAP_BCH
- depends on MTD_NAND_OMAP2
- bool "Support hardware based BCH error correction"
- default n
- select BCH
- help
- This config enables the ELM hardware engine, which can be used to
- locate and correct errors when using BCH ECC scheme. This offloads
- the cpu from doing ECC error searching and correction. However some
- legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
- so this is optional for them.
-
-config MTD_NAND_OMAP_BCH_BUILD
- def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
-
-config MTD_NAND_RICOH
- tristate "Ricoh xD card reader"
- default n
- depends on PCI
- select MTD_SM_COMMON
- help
- Enable support for Ricoh R5C852 xD card reader
- You also need to enable ether
- NAND SSFDC (SmartMedia) read only translation layer' or new
- expermental, readwrite
- 'SmartMedia/xD new translation layer'
-
-config MTD_NAND_AU1550
- tristate "Au1550/1200 NAND support"
- depends on MIPS_ALCHEMY
- help
- This enables the driver for the NAND flash controller on the
- AMD/Alchemy 1550 SOC.
-
-config MTD_NAND_BF5XX
- tristate "Blackfin on-chip NAND Flash Controller driver"
- depends on BF54x || BF52x
- help
- This enables the Blackfin on-chip NAND flash controller
-
- No board specific support is done by this driver, each board
- must advertise a platform_device for the driver to attach.
-
- This driver can also be built as a module. If so, the module
- will be called bf5xx-nand.
-
-config MTD_NAND_BF5XX_HWECC
- bool "BF5XX NAND Hardware ECC"
- default y
- depends on MTD_NAND_BF5XX
- help
- Enable the use of the BF5XX's internal ECC generator when
- using NAND.
-
-config MTD_NAND_BF5XX_BOOTROM_ECC
- bool "Use Blackfin BootROM ECC Layout"
- default n
- depends on MTD_NAND_BF5XX_HWECC
- help
- If you wish to modify NAND pages and allow the Blackfin on-chip
- BootROM to boot from them, say Y here. This is only necessary
- if you are booting U-Boot out of NAND and you wish to update
- U-Boot from Linux' userspace. Otherwise, you should say N here.
-
- If unsure, say N.
-
-config MTD_NAND_S3C2410
- tristate "NAND Flash support for Samsung S3C SoCs"
- depends on ARCH_S3C24XX || ARCH_S3C64XX
- help
- This enables the NAND flash controller on the S3C24xx and S3C64xx
- SoCs
-
- No board specific support is done by this driver, each board
- must advertise a platform_device for the driver to attach.
-
-config MTD_NAND_S3C2410_DEBUG
- bool "Samsung S3C NAND driver debug"
- depends on MTD_NAND_S3C2410
- help
- Enable debugging of the S3C NAND driver
-
-config MTD_NAND_NDFC
- tristate "NDFC NanD Flash Controller"
- depends on 4xx
- select MTD_NAND_ECC_SMC
- help
- NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
-
-config MTD_NAND_S3C2410_CLKSTOP
- bool "Samsung S3C NAND IDLE clock stop"
- depends on MTD_NAND_S3C2410
- default n
- help
- Stop the clock to the NAND controller when there is no chip
- selected to save power. This will mean there is a small delay
- when the is NAND chip selected or released, but will save
- 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
- tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
- depends on HAS_IOMEM
- select REED_SOLOMON
- select REED_SOLOMON_DEC16
- help
- This is a reimplementation of M-Systems DiskOnChip 2000,
- Millennium and Millennium Plus as a standard NAND device driver,
- as opposed to the earlier self-contained MTD device drivers.
- This should enable, among other things, proper JFFS2 operation on
- these devices.
-
-config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
- bool "Advanced detection options for DiskOnChip"
- depends on MTD_NAND_DISKONCHIP
- help
- This option allows you to specify nonstandard address at which to
- probe for a DiskOnChip, or to change the detection options. You
- are unlikely to need any of this unless you are using LinuxBIOS.
- Say 'N'.
-
-config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
- hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
- depends on MTD_NAND_DISKONCHIP
- default "0"
- ---help---
- By default, the probe for DiskOnChip devices will look for a
- DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
- This option allows you to specify a single address at which to probe
- for the device, which is useful if you have other devices in that
- range which get upset when they are probed.
-
- (Note that on PowerPC, the normal probe will only check at
- 0xE4000000.)
-
- Normally, you should leave this set to zero, to allow the probe at
- the normal addresses.
-
-config MTD_NAND_DISKONCHIP_PROBE_HIGH
- bool "Probe high addresses"
- depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
- help
- By default, the probe for DiskOnChip devices will look for a
- DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
- This option changes to make it probe between 0xFFFC8000 and
- 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
- useful to you. Say 'N'.
-
-config MTD_NAND_DISKONCHIP_BBTWRITE
- bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
- depends on MTD_NAND_DISKONCHIP
- help
- On DiskOnChip devices shipped with the INFTL filesystem (Millennium
- and 2000 TSOP/Alon), Linux reserves some space at the end of the
- device for the Bad Block Table (BBT). If you have existing INFTL
- data on your device (created by non-Linux tools such as M-Systems'
- DOS drivers), your data might overlap the area Linux wants to use for
- the BBT. If this is a concern for you, leave this option disabled and
- Linux will not write BBT data into this area.
- The downside of leaving this option disabled is that if bad blocks
- are detected by Linux, they will not be recorded in the BBT, which
- could cause future problems.
- Once you enable this option, new filesystems (INFTL or others, created
- in Linux or other operating systems) will not use the reserved area.
- The only reason not to enable this option is to prevent damage to
- preexisting filesystems.
- Even if you leave this disabled, you can enable BBT writes at module
- load time (assuming you build diskonchip as a module) with the module
- parameter "inftl_bbt_write=1".
-
-config MTD_NAND_DOCG4
- tristate "Support for DiskOnChip G4"
- depends on HAS_IOMEM
- select BCH
- select BITREVERSE
- help
- Support for diskonchip G4 nand flash, found in various smartphones and
- PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
- Portege G900, Asus P526, and O2 XDA Zinc.
-
- With this driver you will be able to use UBI and create a ubifs on the
- device, so you may wish to consider enabling UBI and UBIFS as well.
-
- These devices ship with the Mys/Sandisk SAFTL formatting, for which
- there is currently no mtd parser, so you may want to use command line
- partitioning to segregate write-protected blocks. On the Treo680, the
- first five erase blocks (256KiB each) are write-protected, followed
- by the block containing the saftl partition table. This is probably
- typical.
-
-config MTD_NAND_SHARPSL
- tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
- depends on ARCH_PXA
-
-config MTD_NAND_CAFE
- tristate "NAND support for OLPC CAFÉ chip"
- depends on PCI
- select REED_SOLOMON
- select REED_SOLOMON_DEC16
- help
- Use NAND flash attached to the CAFÉ chip designed for the OLPC
- laptop.
-
-config MTD_NAND_CS553X
- tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
- depends on X86_32
- depends on !UML && HAS_IOMEM
- help
- The CS553x companion chips for the AMD Geode processor
- include NAND flash controllers with built-in hardware ECC
- capabilities; enabling this option will allow you to use
- these. The driver will check the MSRs to verify that the
- controller is enabled for NAND, and currently requires that
- the controller be in MMIO mode.
-
- If you say "m", the module will be called cs553x_nand.
-
-config MTD_NAND_ATMEL
- tristate "Support for NAND Flash / SmartMedia on AT91"
- depends on ARCH_AT91
- select MFD_ATMEL_SMC
- help
- Enables support for NAND Flash / Smart Media Card interface
- on Atmel AT91 processors.
-
-config MTD_NAND_PXA3xx
- tristate "NAND support on PXA3xx and Armada 370/XP"
- depends on !MTD_NAND_MARVELL
- depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU
- help
-
- This enables the driver for the NAND flash device found on
- PXA3xx processors (NFCv1) and also on 32-bit Armada
- platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada
- platforms (7K, 8K) (NFCv2).
-
-config MTD_NAND_MARVELL
- tristate "NAND controller support on Marvell boards"
- depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
- COMPILE_TEST
- depends on HAS_IOMEM
- help
- This enables the NAND flash controller driver for Marvell boards,
- including:
- - PXA3xx processors (NFCv1)
- - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
- - 64-bit Aramda platforms (7k, 8k) (NFCv2)
-
-config MTD_NAND_SLC_LPC32XX
- tristate "NXP LPC32xx SLC Controller"
- depends on ARCH_LPC32XX
- help
- Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
- chips) NAND controller. This is the default for the PHYTEC 3250
- reference board which contains a NAND256R3A2CZA6 chip.
-
- Please check the actual NAND chip connected and its support
- by the SLC NAND controller.
-
-config MTD_NAND_MLC_LPC32XX
- tristate "NXP LPC32xx MLC Controller"
- depends on ARCH_LPC32XX
- help
- Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
- controller. This is the default for the WORK92105 controller
- board.
-
- Please check the actual NAND chip connected and its support
- by the MLC NAND controller.
-
-config MTD_NAND_CM_X270
- tristate "Support for NAND Flash on CM-X270 modules"
- depends on MACH_ARMCORE
-
-config MTD_NAND_PASEMI
- tristate "NAND support for PA Semi PWRficient"
- depends on PPC_PASEMI
- help
- Enables support for NAND Flash interface on PA Semi PWRficient
- based boards
-
-config MTD_NAND_TMIO
- tristate "NAND Flash device on Toshiba Mobile IO Controller"
- depends on MFD_TMIO
- help
- Support for NAND flash connected to a Toshiba Mobile IO
- Controller in some PDAs, including the Sharp SL6000x.
-
-config MTD_NAND_NANDSIM
- tristate "Support for NAND Flash Simulator"
- help
- The simulator may simulate various NAND flash chips for the
- MTD nand layer.
-
-config MTD_NAND_GPMI_NAND
- tristate "GPMI NAND Flash Controller driver"
- depends on MTD_NAND && MXS_DMA
- help
- Enables NAND Flash support for IMX23, IMX28 or IMX6.
- The GPMI controller is very powerful, with the help of BCH
- module, it can do the hardware ECC. The GPMI supports several
- NAND flashs at the same time.
-
-config MTD_NAND_BRCMNAND
- tristate "Broadcom STB NAND controller"
- depends on ARM || ARM64 || MIPS
- help
- Enables the Broadcom NAND controller driver. The controller was
- originally designed for Set-Top Box but is used on various BCM7xxx,
- BCM3xxx, BCM63xxx, iProc/Cygnus and more.
-
-config MTD_NAND_BCM47XXNFLASH
- tristate "Support for NAND flash on BCM4706 BCMA bus"
- depends on BCMA_NFLASH
- help
- BCMA bus can have various flash memories attached, they are
- registered by bcma as platform devices. This enables driver for
- NAND flash memories. For now only BCM4706 is supported.
-
-config MTD_NAND_PLATFORM
- tristate "Support for generic platform NAND driver"
- depends on HAS_IOMEM
- help
- This implements a generic NAND driver for on-SOC platform
- devices. You will need to provide platform-specific functions
- via platform_data.
-
-config MTD_NAND_ORION
- tristate "NAND Flash support for Marvell Orion SoC"
- depends on PLAT_ORION
- help
- This enables the NAND flash controller on Orion machines.
-
- No board specific support is done by this driver, each board
- must advertise a platform_device for the driver to attach.
-
-config MTD_NAND_OXNAS
- tristate "NAND Flash support for Oxford Semiconductor SoC"
- depends on ARCH_OXNAS || COMPILE_TEST
- depends on HAS_IOMEM
- help
- This enables the NAND flash controller on Oxford Semiconductor SoCs.
-
-config MTD_NAND_FSL_ELBC
- tristate "NAND support for Freescale eLBC controllers"
- depends on FSL_SOC
- select FSL_LBC
- help
- Various Freescale chips, including the 8313, include a NAND Flash
- Controller Module with built-in hardware ECC capabilities.
- Enabling this option will enable you to use this to control
- external NAND devices.
-
-config MTD_NAND_FSL_IFC
- tristate "NAND support for Freescale IFC controller"
- depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
- select FSL_IFC
- select MEMORY
- help
- Various Freescale chips e.g P1010, include a NAND Flash machine
- with built-in hardware ECC capabilities.
- Enabling this option will enable you to use this to control
- external NAND devices.
-
-config MTD_NAND_FSL_UPM
- tristate "Support for NAND on Freescale UPM"
- depends on PPC_83xx || PPC_85xx
- select FSL_LBC
- help
- Enables support for NAND Flash chips wired onto Freescale PowerPC
- processor localbus with User-Programmable Machine support.
-
-config MTD_NAND_MPC5121_NFC
- tristate "MPC5121 built-in NAND Flash Controller support"
- depends on PPC_MPC512x
- help
- This enables the driver for the NAND flash controller on the
- MPC5121 SoC.
-
-config MTD_NAND_VF610_NFC
- tristate "Support for Freescale NFC for VF610/MPC5125"
- depends on (SOC_VF610 || COMPILE_TEST)
- depends on HAS_IOMEM
- help
- Enables support for NAND Flash Controller on some Freescale
- processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
- The driver supports a maximum 2k page size. With 2k pages and
- 64 bytes or more of OOB, hardware ECC with up to 32-bit error
- correction is supported. Hardware ECC is only enabled through
- device tree.
-
-config MTD_NAND_MXC
- tristate "MXC NAND support"
- depends on ARCH_MXC
- help
- This enables the driver for the NAND flash controller on the
- MXC processors.
-
-config MTD_NAND_SH_FLCTL
- tristate "Support for NAND on Renesas SuperH FLCTL"
- depends on SUPERH || COMPILE_TEST
- depends on HAS_IOMEM
- depends on HAS_DMA
- help
- Several Renesas SuperH CPU has FLCTL. This option enables support
- for NAND Flash using FLCTL.
-
-config MTD_NAND_DAVINCI
- tristate "Support NAND on DaVinci/Keystone SoC"
- depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF)
- help
- Enable the driver for NAND flash chips on Texas Instruments
- DaVinci/Keystone processors.
-
-config MTD_NAND_TXX9NDFMC
- tristate "NAND Flash support for TXx9 SoC"
- depends on SOC_TX4938 || SOC_TX4939
- help
- This enables the NAND flash controller on the TXx9 SoCs.
-
-config MTD_NAND_SOCRATES
- tristate "Support for NAND on Socrates board"
- depends on SOCRATES
- help
- Enables support for NAND Flash chips wired onto Socrates board.
-
-config MTD_NAND_NUC900
- tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
- depends on ARCH_W90X900
- help
- This enables the driver for the NAND Flash on evaluation board based
- on w90p910 / NUC9xx.
-
-config MTD_NAND_JZ4740
- tristate "Support for JZ4740 SoC NAND controller"
- depends on MACH_JZ4740
- help
- Enables support for NAND Flash on JZ4740 SoC based boards.
-
-config MTD_NAND_JZ4780
- tristate "Support for NAND on JZ4780 SoC"
- depends on MACH_JZ4780 && JZ4780_NEMC
- help
- Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
- based boards, using the BCH controller for hardware error correction.
-
-config MTD_NAND_FSMC
- tristate "Support for NAND on ST Micros FSMC"
- depends on OF
- depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
- help
- Enables support for NAND Flash chips on the ST Microelectronics
- Flexible Static Memory Controller (FSMC)
-
-config MTD_NAND_XWAY
- bool "Support for NAND on Lantiq XWAY SoC"
- depends on LANTIQ && SOC_TYPE_XWAY
- help
- Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
- to the External Bus Unit (EBU).
-
-config MTD_NAND_SUNXI
- tristate "Support for NAND on Allwinner SoCs"
- depends on ARCH_SUNXI
- help
- Enables support for NAND Flash chips on Allwinner SoCs.
-
-config MTD_NAND_HISI504
- tristate "Support for NAND controller on Hisilicon SoC Hip04"
- depends on ARCH_HISI || COMPILE_TEST
- depends on HAS_DMA
- help
- Enables support for NAND controller on Hisilicon SoC Hip04.
-
-config MTD_NAND_QCOM
- tristate "Support for NAND on QCOM SoCs"
- depends on ARCH_QCOM
- help
- Enables support for NAND flash chips on SoCs containing the EBI2 NAND
- controller. This controller is found on IPQ806x SoC.
-
-config MTD_NAND_MTK
- tristate "Support for NAND controller on MTK SoCs"
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_DMA
- help
- Enables support for NAND controller on MTK SoCs.
- This controller is found on mt27xx, mt81xx, mt65xx SoCs.
-
-endif # MTD_NAND
+source "drivers/mtd/nand/raw/Kconfig"
# SPDX-License-Identifier: GPL-2.0
-#
-# linux/drivers/nand/Makefile
-#
-obj-$(CONFIG_MTD_NAND) += nand.o
-obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
-obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
-obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
+nandcore-objs := core.o bbt.o
+obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
-obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
-obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
-obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
-obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
-obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
-obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
-obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
-obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
-obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
-obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
-obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
-obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
-obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
-obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
-obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
-obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
-obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
-obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/
-obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
-omap2_nand-objs := omap2.o
-obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
-obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
-obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
-obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
-obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o
-obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
-obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
-obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_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_IFC) += fsl_ifc_nand.o
-obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
-obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o
-obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
-obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
-obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
-obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
-obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
-obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
-obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
-obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
-obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
-obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
-obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
-obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
-obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
-obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
-obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
-obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
-obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
-obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
-obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
-
-nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
-nand-objs += nand_amd.o
-nand-objs += nand_hynix.o
-nand-objs += nand_macronix.o
-nand-objs += nand_micron.o
-nand-objs += nand_samsung.o
-nand-objs += nand_toshiba.o
+obj-y += onenand/
+obj-y += raw/
+++ /dev/null
-/*
- * drivers/mtd/nand/ams-delta.c
- *
- * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
- *
- * Derived from drivers/mtd/toto.c
- * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- * Partially stolen from drivers/mtd/nand/plat_nand.c
- *
- * 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.
- *
- * Overview:
- * This is a device driver for the NAND flash device found on the
- * Amstrad E3 (Delta).
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/gpio.h>
-#include <linux/platform_data/gpio-omap.h>
-
-#include <asm/io.h>
-#include <asm/sizes.h>
-
-#include <mach/board-ams-delta.h>
-
-#include <mach/hardware.h>
-
-/*
- * MTD structure for E3 (Delta)
- */
-static struct mtd_info *ams_delta_mtd = NULL;
-
-/*
- * Define partitions for flash devices
- */
-
-static const struct mtd_partition partition_info[] = {
- { .name = "Kernel",
- .offset = 0,
- .size = 3 * SZ_1M + SZ_512K },
- { .name = "u-boot",
- .offset = 3 * SZ_1M + SZ_512K,
- .size = SZ_256K },
- { .name = "u-boot params",
- .offset = 3 * SZ_1M + SZ_512K + SZ_256K,
- .size = SZ_256K },
- { .name = "Amstrad LDR",
- .offset = 4 * SZ_1M,
- .size = SZ_256K },
- { .name = "File system",
- .offset = 4 * SZ_1M + 1 * SZ_256K,
- .size = 27 * SZ_1M },
- { .name = "PBL reserved",
- .offset = 32 * SZ_1M - 3 * SZ_256K,
- .size = 3 * SZ_256K },
-};
-
-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
-
- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
- writew(byte, this->IO_ADDR_W);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0);
- ndelay(40);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1);
-}
-
-static u_char ams_delta_read_byte(struct mtd_info *mtd)
-{
- u_char res;
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
-
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0);
- ndelay(40);
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
- res = readw(this->IO_ADDR_R);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1);
-
- return res;
-}
-
-static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
- int len)
-{
- int i;
-
- for (i=0; i<len; i++)
- ams_delta_write_byte(mtd, buf[i]);
-}
-
-static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- int i;
-
- for (i=0; i<len; i++)
- buf[i] = ams_delta_read_byte(mtd);
-}
-
-/*
- * Command control function
- *
- * ctrl:
- * NAND_NCE: bit 0 -> bit 2
- * NAND_CLE: bit 1 -> bit 7
- * NAND_ALE: bit 2 -> bit 6
- */
-static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
-
- if (ctrl & NAND_CTRL_CHANGE) {
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE,
- (ctrl & NAND_NCE) == 0);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE,
- (ctrl & NAND_CLE) != 0);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE,
- (ctrl & NAND_ALE) != 0);
- }
-
- if (cmd != NAND_CMD_NONE)
- ams_delta_write_byte(mtd, cmd);
-}
-
-static int ams_delta_nand_ready(struct mtd_info *mtd)
-{
- return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB);
-}
-
-static const struct gpio _mandatory_gpio[] = {
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nce",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nre",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nwp",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nwe",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE,
- .flags = GPIOF_OUT_INIT_LOW,
- .label = "nand_ale",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE,
- .flags = GPIOF_OUT_INIT_LOW,
- .label = "nand_cle",
- },
-};
-
-/*
- * Main initialization routine
- */
-static int ams_delta_init(struct platform_device *pdev)
-{
- struct nand_chip *this;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- void __iomem *io_base;
- int err = 0;
-
- if (!res)
- return -ENXIO;
-
- /* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
- printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
- }
-
- ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->owner = THIS_MODULE;
-
- /*
- * Don't try to request the memory region from here,
- * it should have been already requested from the
- * gpio-omap driver and requesting it again would fail.
- */
-
- io_base = ioremap(res->start, resource_size(res));
- if (io_base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- err = -EIO;
- goto out_free;
- }
-
- nand_set_controller_data(this, (void *)io_base);
-
- /* Set address of NAND IO lines */
- this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
- this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
- this->read_byte = ams_delta_read_byte;
- this->write_buf = ams_delta_write_buf;
- this->read_buf = ams_delta_read_buf;
- this->cmd_ctrl = ams_delta_hwcontrol;
- if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) {
- this->dev_ready = ams_delta_nand_ready;
- } else {
- this->dev_ready = NULL;
- printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n");
- }
- /* 25 us command delay time */
- this->chip_delay = 30;
- this->ecc.mode = NAND_ECC_SOFT;
- this->ecc.algo = NAND_ECC_HAMMING;
-
- platform_set_drvdata(pdev, io_base);
-
- /* Set chip enabled, but */
- err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
- if (err)
- goto out_gpio;
-
- /* Scan to find existence of the device */
- err = nand_scan(ams_delta_mtd, 1);
- if (err)
- goto out_mtd;
-
- /* Register the partitions */
- mtd_device_register(ams_delta_mtd, partition_info,
- ARRAY_SIZE(partition_info));
-
- goto out;
-
- out_mtd:
- gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
-out_gpio:
- gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
- iounmap(io_base);
-out_free:
- kfree(this);
- out:
- return err;
-}
-
-/*
- * Clean up routine
- */
-static int ams_delta_cleanup(struct platform_device *pdev)
-{
- void __iomem *io_base = platform_get_drvdata(pdev);
-
- /* Release resources, unregister device */
- nand_release(ams_delta_mtd);
-
- gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
- gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
- iounmap(io_base);
-
- /* Free the MTD device structure */
- kfree(mtd_to_nand(ams_delta_mtd));
-
- return 0;
-}
-
-static struct platform_driver ams_delta_nand_driver = {
- .probe = ams_delta_init,
- .remove = ams_delta_cleanup,
- .driver = {
- .name = "ams-delta-nand",
- },
-};
-
-module_platform_driver(ams_delta_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
-MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
+++ /dev/null
-obj-$(CONFIG_MTD_NAND_ATMEL) += atmel-nand-controller.o atmel-pmecc.o
-
-atmel-nand-controller-objs := nand-controller.o
-atmel-pmecc-objs := pmecc.o
+++ /dev/null
-/*
- * Copyright 2017 ATMEL
- * Copyright 2017 Free Electrons
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
- *
- * Derived from the atmel_nand.c driver which contained the following
- * copyrights:
- *
- * Copyright 2003 Rick Bronson
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * Derived from drivers/mtd/spia.c
- * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
- *
- *
- * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
- *
- * Derived from Das U-Boot source code
- * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- * Add Programmable Multibit ECC support for various AT91 SoC
- * Copyright 2012 ATMEL, Hong Xu
- *
- * Add Nand Flash Controller support for SAMA5 SoC
- * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
- *
- * 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.
- *
- * A few words about the naming convention in this file. This convention
- * applies to structure and function names.
- *
- * Prefixes:
- *
- * - atmel_nand_: all generic structures/functions
- * - atmel_smc_nand_: all structures/functions specific to the SMC interface
- * (at91sam9 and avr32 SoCs)
- * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface
- * (sama5 SoCs and later)
- * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block
- * that is available in the HSMC block
- * - <soc>_nand_: all SoC specific structures/functions
- */
-
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/genalloc.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/atmel-matrix.h>
-#include <linux/mfd/syscon/atmel-smc.h>
-#include <linux/module.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/iopoll.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-
-#include "pmecc.h"
-
-#define ATMEL_HSMC_NFC_CFG 0x0
-#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24)
-#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24)
-#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20))
-#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16)
-#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13)
-#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12)
-#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9)
-#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8)
-#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0)
-#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1)
-
-#define ATMEL_HSMC_NFC_CTRL 0x4
-#define ATMEL_HSMC_NFC_CTRL_EN BIT(0)
-#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1)
-
-#define ATMEL_HSMC_NFC_SR 0x8
-#define ATMEL_HSMC_NFC_IER 0xc
-#define ATMEL_HSMC_NFC_IDR 0x10
-#define ATMEL_HSMC_NFC_IMR 0x14
-#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1)
-#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4)
-#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5)
-#define ATMEL_HSMC_NFC_SR_BUSY BIT(8)
-#define ATMEL_HSMC_NFC_SR_WR BIT(11)
-#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12)
-#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16)
-#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17)
-#define ATMEL_HSMC_NFC_SR_DTOE BIT(20)
-#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21)
-#define ATMEL_HSMC_NFC_SR_AWB BIT(22)
-#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23)
-#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \
- ATMEL_HSMC_NFC_SR_UNDEF | \
- ATMEL_HSMC_NFC_SR_AWB | \
- ATMEL_HSMC_NFC_SR_NFCASE)
-#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24)
-
-#define ATMEL_HSMC_NFC_ADDR 0x18
-#define ATMEL_HSMC_NFC_BANK 0x1c
-
-#define ATMEL_NFC_MAX_RB_ID 7
-
-#define ATMEL_NFC_SRAM_SIZE 0x2400
-
-#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2))
-#define ATMEL_NFC_VCMD2 BIT(18)
-#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19)
-#define ATMEL_NFC_CSID(cs) ((cs) << 22)
-#define ATMEL_NFC_DATAEN BIT(25)
-#define ATMEL_NFC_NFCWR BIT(26)
-
-#define ATMEL_NFC_MAX_ADDR_CYCLES 5
-
-#define ATMEL_NAND_ALE_OFFSET BIT(21)
-#define ATMEL_NAND_CLE_OFFSET BIT(22)
-
-#define DEFAULT_TIMEOUT_MS 1000
-#define MIN_DMA_LEN 128
-
-enum atmel_nand_rb_type {
- ATMEL_NAND_NO_RB,
- ATMEL_NAND_NATIVE_RB,
- ATMEL_NAND_GPIO_RB,
-};
-
-struct atmel_nand_rb {
- enum atmel_nand_rb_type type;
- union {
- struct gpio_desc *gpio;
- int id;
- };
-};
-
-struct atmel_nand_cs {
- int id;
- struct atmel_nand_rb rb;
- struct gpio_desc *csgpio;
- struct {
- void __iomem *virt;
- dma_addr_t dma;
- } io;
-
- struct atmel_smc_cs_conf smcconf;
-};
-
-struct atmel_nand {
- struct list_head node;
- struct device *dev;
- struct nand_chip base;
- struct atmel_nand_cs *activecs;
- struct atmel_pmecc_user *pmecc;
- struct gpio_desc *cdgpio;
- int numcs;
- struct atmel_nand_cs cs[];
-};
-
-static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip)
-{
- return container_of(chip, struct atmel_nand, base);
-}
-
-enum atmel_nfc_data_xfer {
- ATMEL_NFC_NO_DATA,
- ATMEL_NFC_READ_DATA,
- ATMEL_NFC_WRITE_DATA,
-};
-
-struct atmel_nfc_op {
- u8 cs;
- u8 ncmds;
- u8 cmds[2];
- u8 naddrs;
- u8 addrs[5];
- enum atmel_nfc_data_xfer data;
- u32 wait;
- u32 errors;
-};
-
-struct atmel_nand_controller;
-struct atmel_nand_controller_caps;
-
-struct atmel_nand_controller_ops {
- int (*probe)(struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps);
- int (*remove)(struct atmel_nand_controller *nc);
- void (*nand_init)(struct atmel_nand_controller *nc,
- struct atmel_nand *nand);
- int (*ecc_init)(struct atmel_nand *nand);
- int (*setup_data_interface)(struct atmel_nand *nand, int csline,
- const struct nand_data_interface *conf);
-};
-
-struct atmel_nand_controller_caps {
- bool has_dma;
- bool legacy_of_bindings;
- u32 ale_offs;
- u32 cle_offs;
- const struct atmel_nand_controller_ops *ops;
-};
-
-struct atmel_nand_controller {
- struct nand_hw_control base;
- const struct atmel_nand_controller_caps *caps;
- struct device *dev;
- struct regmap *smc;
- struct dma_chan *dmac;
- struct atmel_pmecc *pmecc;
- struct list_head chips;
- struct clk *mck;
-};
-
-static inline struct atmel_nand_controller *
-to_nand_controller(struct nand_hw_control *ctl)
-{
- return container_of(ctl, struct atmel_nand_controller, base);
-}
-
-struct atmel_smc_nand_controller {
- struct atmel_nand_controller base;
- struct regmap *matrix;
- unsigned int ebi_csa_offs;
-};
-
-static inline struct atmel_smc_nand_controller *
-to_smc_nand_controller(struct nand_hw_control *ctl)
-{
- return container_of(to_nand_controller(ctl),
- struct atmel_smc_nand_controller, base);
-}
-
-struct atmel_hsmc_nand_controller {
- struct atmel_nand_controller base;
- struct {
- struct gen_pool *pool;
- void __iomem *virt;
- dma_addr_t dma;
- } sram;
- const struct atmel_hsmc_reg_layout *hsmc_layout;
- struct regmap *io;
- struct atmel_nfc_op op;
- struct completion complete;
- int irq;
-
- /* Only used when instantiating from legacy DT bindings. */
- struct clk *clk;
-};
-
-static inline struct atmel_hsmc_nand_controller *
-to_hsmc_nand_controller(struct nand_hw_control *ctl)
-{
- return container_of(to_nand_controller(ctl),
- struct atmel_hsmc_nand_controller, base);
-}
-
-static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status)
-{
- op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS;
- op->wait ^= status & op->wait;
-
- return !op->wait || op->errors;
-}
-
-static irqreturn_t atmel_nfc_interrupt(int irq, void *data)
-{
- struct atmel_hsmc_nand_controller *nc = data;
- u32 sr, rcvd;
- bool done;
-
- regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr);
-
- rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
- done = atmel_nfc_op_done(&nc->op, sr);
-
- if (rcvd)
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd);
-
- if (done)
- complete(&nc->complete);
-
- return rcvd ? IRQ_HANDLED : IRQ_NONE;
-}
-
-static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll,
- unsigned int timeout_ms)
-{
- int ret;
-
- if (!timeout_ms)
- timeout_ms = DEFAULT_TIMEOUT_MS;
-
- if (poll) {
- u32 status;
-
- ret = regmap_read_poll_timeout(nc->base.smc,
- ATMEL_HSMC_NFC_SR, status,
- atmel_nfc_op_done(&nc->op,
- status),
- 0, timeout_ms * 1000);
- } else {
- init_completion(&nc->complete);
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER,
- nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
- ret = wait_for_completion_timeout(&nc->complete,
- msecs_to_jiffies(timeout_ms));
- if (!ret)
- ret = -ETIMEDOUT;
- else
- ret = 0;
-
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) {
- dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n");
- ret = -ETIMEDOUT;
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) {
- dev_err(nc->base.dev, "Access to an undefined area\n");
- ret = -EIO;
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) {
- dev_err(nc->base.dev, "Access while busy\n");
- ret = -EIO;
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) {
- dev_err(nc->base.dev, "Wrong access size\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static void atmel_nand_dma_transfer_finished(void *data)
-{
- struct completion *finished = data;
-
- complete(finished);
-}
-
-static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
- void *buf, dma_addr_t dev_dma, size_t len,
- enum dma_data_direction dir)
-{
- DECLARE_COMPLETION_ONSTACK(finished);
- dma_addr_t src_dma, dst_dma, buf_dma;
- struct dma_async_tx_descriptor *tx;
- dma_cookie_t cookie;
-
- buf_dma = dma_map_single(nc->dev, buf, len, dir);
- if (dma_mapping_error(nc->dev, dev_dma)) {
- dev_err(nc->dev,
- "Failed to prepare a buffer for DMA access\n");
- goto err;
- }
-
- if (dir == DMA_FROM_DEVICE) {
- src_dma = dev_dma;
- dst_dma = buf_dma;
- } else {
- src_dma = buf_dma;
- dst_dma = dev_dma;
- }
-
- tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
- if (!tx) {
- dev_err(nc->dev, "Failed to prepare DMA memcpy\n");
- goto err_unmap;
- }
-
- tx->callback = atmel_nand_dma_transfer_finished;
- tx->callback_param = &finished;
-
- cookie = dmaengine_submit(tx);
- if (dma_submit_error(cookie)) {
- dev_err(nc->dev, "Failed to do DMA tx_submit\n");
- goto err_unmap;
- }
-
- dma_async_issue_pending(nc->dmac);
- wait_for_completion(&finished);
-
- return 0;
-
-err_unmap:
- dma_unmap_single(nc->dev, buf_dma, len, dir);
-
-err:
- dev_dbg(nc->dev, "Fall back to CPU I/O\n");
-
- return -EIO;
-}
-
-static u8 atmel_nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- return ioread8(nand->activecs->io.virt);
-}
-
-static u16 atmel_nand_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- return ioread16(nand->activecs->io.virt);
-}
-
-static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- if (chip->options & NAND_BUSWIDTH_16)
- iowrite16(byte | (byte << 8), nand->activecs->io.virt);
- else
- iowrite8(byte, nand->activecs->io.virt);
-}
-
-static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(chip->controller);
-
- /*
- * If the controller supports DMA, the buffer address is DMA-able and
- * len is long enough to make DMA transfers profitable, let's trigger
- * a DMA transfer. If it fails, fallback to PIO mode.
- */
- if (nc->dmac && virt_addr_valid(buf) &&
- len >= MIN_DMA_LEN &&
- !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len,
- DMA_FROM_DEVICE))
- return;
-
- if (chip->options & NAND_BUSWIDTH_16)
- ioread16_rep(nand->activecs->io.virt, buf, len / 2);
- else
- ioread8_rep(nand->activecs->io.virt, buf, len);
-}
-
-static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(chip->controller);
-
- /*
- * If the controller supports DMA, the buffer address is DMA-able and
- * len is long enough to make DMA transfers profitable, let's trigger
- * a DMA transfer. If it fails, fallback to PIO mode.
- */
- if (nc->dmac && virt_addr_valid(buf) &&
- len >= MIN_DMA_LEN &&
- !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma,
- len, DMA_TO_DEVICE))
- return;
-
- if (chip->options & NAND_BUSWIDTH_16)
- iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
- else
- iowrite8_rep(nand->activecs->io.virt, buf, len);
-}
-
-static int atmel_nand_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- return gpiod_get_value(nand->activecs->rb.gpio);
-}
-
-static void atmel_nand_select_chip(struct mtd_info *mtd, int cs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- if (cs < 0 || cs >= nand->numcs) {
- nand->activecs = NULL;
- chip->dev_ready = NULL;
- return;
- }
-
- nand->activecs = &nand->cs[cs];
-
- if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB)
- chip->dev_ready = atmel_nand_dev_ready;
-}
-
-static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
- u32 status;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status);
-
- return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
-}
-
-static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- atmel_nand_select_chip(mtd, cs);
-
- if (!nand->activecs) {
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
- ATMEL_HSMC_NFC_CTRL_DIS);
- return;
- }
-
- if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB)
- chip->dev_ready = atmel_hsmc_nand_dev_ready;
-
- regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
- ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
- ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
- ATMEL_HSMC_NFC_CFG_RSPARE |
- ATMEL_HSMC_NFC_CFG_WSPARE,
- ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
- ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
- ATMEL_HSMC_NFC_CFG_RSPARE);
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
- ATMEL_HSMC_NFC_CTRL_EN);
-}
-
-static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll)
-{
- u8 *addrs = nc->op.addrs;
- unsigned int op = 0;
- u32 addr, val;
- int i, ret;
-
- nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE;
-
- for (i = 0; i < nc->op.ncmds; i++)
- op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]);
-
- if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++);
-
- op |= ATMEL_NFC_CSID(nc->op.cs) |
- ATMEL_NFC_ACYCLE(nc->op.naddrs);
-
- if (nc->op.ncmds > 1)
- op |= ATMEL_NFC_VCMD2;
-
- addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) |
- (addrs[3] << 24);
-
- if (nc->op.data != ATMEL_NFC_NO_DATA) {
- op |= ATMEL_NFC_DATAEN;
- nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE;
-
- if (nc->op.data == ATMEL_NFC_WRITE_DATA)
- op |= ATMEL_NFC_NFCWR;
- }
-
- /* Clear all flags. */
- regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val);
-
- /* Send the command. */
- regmap_write(nc->io, op, addr);
-
- ret = atmel_nfc_wait(nc, poll, 0);
- if (ret)
- dev_err(nc->base.dev,
- "Failed to send NAND command (err = %d)!",
- ret);
-
- /* Reset the op state. */
- memset(&nc->op, 0, sizeof(nc->op));
-
- return ret;
-}
-
-static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat,
- unsigned int ctrl)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (ctrl & NAND_ALE) {
- if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
- return;
-
- nc->op.addrs[nc->op.naddrs++] = dat;
- } else if (ctrl & NAND_CLE) {
- if (nc->op.ncmds > 1)
- return;
-
- nc->op.cmds[nc->op.ncmds++] = dat;
- }
-
- if (dat == NAND_CMD_NONE) {
- nc->op.cs = nand->activecs->id;
- atmel_nfc_exec_op(nc, true);
- }
-}
-
-static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(chip->controller);
-
- if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) {
- if (ctrl & NAND_NCE)
- gpiod_set_value(nand->activecs->csgpio, 0);
- else
- gpiod_set_value(nand->activecs->csgpio, 1);
- }
-
- if (ctrl & NAND_ALE)
- writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs);
- else if (ctrl & NAND_CLE)
- writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
-}
-
-static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
- bool oob_required)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret = -EIO;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (nc->base.dmac)
- ret = atmel_nand_dma_transfer(&nc->base, (void *)buf,
- nc->sram.dma, mtd->writesize,
- DMA_TO_DEVICE);
-
- /* Falling back to CPU copy. */
- if (ret)
- memcpy_toio(nc->sram.virt, buf, mtd->writesize);
-
- if (oob_required)
- memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi,
- mtd->oobsize);
-}
-
-static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf,
- bool oob_required)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret = -EIO;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (nc->base.dmac)
- ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma,
- mtd->writesize, DMA_FROM_DEVICE);
-
- /* Falling back to CPU copy. */
- if (ret)
- memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
-
- if (oob_required)
- memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,
- mtd->oobsize);
-}
-
-static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_hsmc_nand_controller *nc;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (column >= 0) {
- nc->op.addrs[nc->op.naddrs++] = column;
-
- /*
- * 2 address cycles for the column offset on large page NANDs.
- */
- if (mtd->writesize > 512)
- nc->op.addrs[nc->op.naddrs++] = column >> 8;
- }
-
- if (page >= 0) {
- nc->op.addrs[nc->op.naddrs++] = page;
- nc->op.addrs[nc->op.naddrs++] = page >> 8;
-
- if (chip->options & NAND_ROW_ADDR_3)
- nc->op.addrs[nc->op.naddrs++] = page >> 16;
- }
-}
-
-static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- if (raw)
- return 0;
-
- ret = atmel_pmecc_enable(nand->pmecc, op);
- if (ret)
- dev_err(nc->dev,
- "Failed to enable ECC engine (err = %d)\n", ret);
-
- return ret;
-}
-
-static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- if (!raw)
- atmel_pmecc_disable(nand->pmecc);
-}
-
-static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- struct mtd_oob_region oobregion;
- void *eccbuf;
- int ret, i;
-
- nc = to_nand_controller(chip->controller);
-
- if (raw)
- return 0;
-
- ret = atmel_pmecc_wait_rdy(nand->pmecc);
- if (ret) {
- dev_err(nc->dev,
- "Failed to transfer NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- mtd_ooblayout_ecc(mtd, 0, &oobregion);
- eccbuf = chip->oob_poi + oobregion.offset;
-
- for (i = 0; i < chip->ecc.steps; i++) {
- atmel_pmecc_get_generated_eccbytes(nand->pmecc, i,
- eccbuf);
- eccbuf += chip->ecc.bytes;
- }
-
- return 0;
-}
-
-static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf,
- bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- struct mtd_oob_region oobregion;
- int ret, i, max_bitflips = 0;
- void *databuf, *eccbuf;
-
- nc = to_nand_controller(chip->controller);
-
- if (raw)
- return 0;
-
- ret = atmel_pmecc_wait_rdy(nand->pmecc);
- if (ret) {
- dev_err(nc->dev,
- "Failed to read NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- mtd_ooblayout_ecc(mtd, 0, &oobregion);
- eccbuf = chip->oob_poi + oobregion.offset;
- databuf = buf;
-
- for (i = 0; i < chip->ecc.steps; i++) {
- ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf,
- eccbuf);
- if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc))
- ret = nand_check_erased_ecc_chunk(databuf,
- chip->ecc.size,
- eccbuf,
- chip->ecc.bytes,
- NULL, 0,
- chip->ecc.strength);
-
- if (ret >= 0)
- max_bitflips = max(ret, max_bitflips);
- else
- mtd->ecc_stats.failed++;
-
- databuf += chip->ecc.size;
- eccbuf += chip->ecc.bytes;
- }
-
- return max_bitflips;
-}
-
-static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,
- bool oob_required, int page, bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- int ret;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
- if (ret)
- return ret;
-
- atmel_nand_write_buf(mtd, buf, mtd->writesize);
-
- ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
- if (ret) {
- atmel_pmecc_disable(nand->pmecc);
- return ret;
- }
-
- atmel_nand_pmecc_disable(chip, raw);
-
- atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip, const u8 *buf,
- int oob_required, int page)
-{
- return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false);
-}
-
-static int atmel_nand_pmecc_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf, int oob_required,
- int page)
-{
- return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true);
-}
-
-static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
- bool oob_required, int page, bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
- if (ret)
- return ret;
-
- atmel_nand_read_buf(mtd, buf, mtd->writesize);
- atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
-
- atmel_nand_pmecc_disable(chip, raw);
-
- return ret;
-}
-
-static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false);
-}
-
-static int atmel_nand_pmecc_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true);
-}
-
-static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
- const u8 *buf, bool oob_required,
- int page, bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret, status;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- atmel_nfc_copy_to_sram(chip, buf, false);
-
- nc->op.cmds[0] = NAND_CMD_SEQIN;
- nc->op.ncmds = 1;
- atmel_nfc_set_op_addr(chip, page, 0x0);
- nc->op.cs = nand->activecs->id;
- nc->op.data = ATMEL_NFC_WRITE_DATA;
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
- if (ret)
- return ret;
-
- ret = atmel_nfc_exec_op(nc, false);
- if (ret) {
- atmel_nand_pmecc_disable(chip, raw);
- dev_err(nc->base.dev,
- "Failed to transfer NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
-
- atmel_nand_pmecc_disable(chip, raw);
-
- if (ret)
- return ret;
-
- atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- nc->op.cmds[0] = NAND_CMD_PAGEPROG;
- nc->op.ncmds = 1;
- nc->op.cs = nand->activecs->id;
- ret = atmel_nfc_exec_op(nc, false);
- if (ret)
- dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
- ret);
-
- status = chip->waitfunc(mtd, chip);
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return ret;
-}
-
-static int atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf, int oob_required,
- int page)
-{
- return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
- false);
-}
-
-static int atmel_hsmc_nand_pmecc_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf,
- int oob_required, int page)
-{
- return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
- true);
-}
-
-static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
- bool oob_required, int page,
- bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- /*
- * Optimized read page accessors only work when the NAND R/B pin is
- * connected to a native SoC R/B pin. If that's not the case, fallback
- * to the non-optimized one.
- */
- if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) {
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page,
- raw);
- }
-
- nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0;
-
- if (mtd->writesize > 512)
- nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART;
-
- atmel_nfc_set_op_addr(chip, page, 0x0);
- nc->op.cs = nand->activecs->id;
- nc->op.data = ATMEL_NFC_READ_DATA;
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
- if (ret)
- return ret;
-
- ret = atmel_nfc_exec_op(nc, false);
- if (ret) {
- atmel_nand_pmecc_disable(chip, raw);
- dev_err(nc->base.dev,
- "Failed to load NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- atmel_nfc_copy_from_sram(chip, buf, true);
-
- ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
-
- atmel_nand_pmecc_disable(chip, raw);
-
- return ret;
-}
-
-static int atmel_hsmc_nand_pmecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
- false);
-}
-
-static int atmel_hsmc_nand_pmecc_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- u8 *buf, int oob_required,
- int page)
-{
- return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
- true);
-}
-
-static int atmel_nand_pmecc_init(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
- struct atmel_pmecc_user_req req;
-
- nc = to_nand_controller(chip->controller);
-
- if (!nc->pmecc) {
- dev_err(nc->dev, "HW ECC not supported\n");
- return -ENOTSUPP;
- }
-
- if (nc->caps->legacy_of_bindings) {
- u32 val;
-
- if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap",
- &val))
- chip->ecc.strength = val;
-
- if (!of_property_read_u32(nc->dev->of_node,
- "atmel,pmecc-sector-size",
- &val))
- chip->ecc.size = val;
- }
-
- if (chip->ecc.options & NAND_ECC_MAXIMIZE)
- req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
- else if (chip->ecc.strength)
- req.ecc.strength = chip->ecc.strength;
- else if (chip->ecc_strength_ds)
- req.ecc.strength = chip->ecc_strength_ds;
- else
- req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
-
- if (chip->ecc.size)
- req.ecc.sectorsize = chip->ecc.size;
- else if (chip->ecc_step_ds)
- req.ecc.sectorsize = chip->ecc_step_ds;
- else
- req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO;
-
- req.pagesize = mtd->writesize;
- req.oobsize = mtd->oobsize;
-
- if (mtd->writesize <= 512) {
- req.ecc.bytes = 4;
- req.ecc.ooboffset = 0;
- } else {
- req.ecc.bytes = mtd->oobsize - 2;
- req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO;
- }
-
- nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req);
- if (IS_ERR(nand->pmecc))
- return PTR_ERR(nand->pmecc);
-
- chip->ecc.algo = NAND_ECC_BCH;
- chip->ecc.size = req.ecc.sectorsize;
- chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors;
- chip->ecc.strength = req.ecc.strength;
-
- chip->options |= NAND_NO_SUBPAGE_WRITE;
-
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-
- return 0;
-}
-
-static int atmel_nand_ecc_init(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- switch (chip->ecc.mode) {
- case NAND_ECC_NONE:
- case NAND_ECC_SOFT:
- /*
- * Nothing to do, the core will initialize everything for us.
- */
- break;
-
- case NAND_ECC_HW:
- ret = atmel_nand_pmecc_init(chip);
- if (ret)
- return ret;
-
- chip->ecc.read_page = atmel_nand_pmecc_read_page;
- chip->ecc.write_page = atmel_nand_pmecc_write_page;
- chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw;
- chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw;
- break;
-
- default:
- /* Other modes are not supported. */
- dev_err(nc->dev, "Unsupported ECC mode: %d\n",
- chip->ecc.mode);
- return -ENOTSUPP;
- }
-
- return 0;
-}
-
-static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- int ret;
-
- ret = atmel_nand_ecc_init(nand);
- if (ret)
- return ret;
-
- if (chip->ecc.mode != NAND_ECC_HW)
- return 0;
-
- /* Adjust the ECC operations for the HSMC IP. */
- chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page;
- chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page;
- chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw;
- chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw;
-
- return 0;
-}
-
-static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
- const struct nand_data_interface *conf,
- struct atmel_smc_cs_conf *smcconf)
-{
- u32 ncycles, totalcycles, timeps, mckperiodps;
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(nand->base.controller);
-
- /* DDR interface not supported. */
- if (conf->type != NAND_SDR_IFACE)
- return -ENOTSUPP;
-
- /*
- * tRC < 30ns implies EDO mode. This controller does not support this
- * mode.
- */
- if (conf->timings.sdr.tRC_min < 30000)
- return -ENOTSUPP;
-
- atmel_smc_cs_conf_init(smcconf);
-
- mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck);
- mckperiodps *= 1000;
-
- /*
- * Set write pulse timing. This one is easy to extract:
- *
- * NWE_PULSE = tWP
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps);
- totalcycles = ncycles;
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * The write setup timing depends on the operation done on the NAND.
- * All operations goes through the same data bus, but the operation
- * type depends on the address we are writing to (ALE/CLE address
- * lines).
- * Since we have no way to differentiate the different operations at
- * the SMC level, we must consider the worst case (the biggest setup
- * time among all operation types):
- *
- * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE
- */
- timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min,
- conf->timings.sdr.tALS_min);
- timeps = max(timeps, conf->timings.sdr.tDS_min);
- ncycles = DIV_ROUND_UP(timeps, mckperiodps);
- ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0;
- totalcycles += ncycles;
- ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * As for the write setup timing, the write hold timing depends on the
- * operation done on the NAND:
- *
- * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH)
- */
- timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min,
- conf->timings.sdr.tALH_min);
- timeps = max3(timeps, conf->timings.sdr.tDH_min,
- conf->timings.sdr.tWH_min);
- ncycles = DIV_ROUND_UP(timeps, mckperiodps);
- totalcycles += ncycles;
-
- /*
- * The write cycle timing is directly matching tWC, but is also
- * dependent on the other timings on the setup and hold timings we
- * calculated earlier, which gives:
- *
- * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD)
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps);
- ncycles = max(totalcycles, ncycles);
- ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * We don't want the CS line to be toggled between each byte/word
- * transfer to the NAND. The only way to guarantee that is to have the
- * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
- *
- * NCS_WR_PULSE = NWE_CYCLE
- */
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * As for the write setup timing, the read hold timing depends on the
- * operation done on the NAND:
- *
- * NRD_HOLD = max(tREH, tRHOH)
- */
- timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min);
- ncycles = DIV_ROUND_UP(timeps, mckperiodps);
- totalcycles = ncycles;
-
- /*
- * TDF = tRHZ - NRD_HOLD
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps);
- ncycles -= totalcycles;
-
- /*
- * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and
- * we might end up with a config that does not fit in the TDF field.
- * Just take the max value in this case and hope that the NAND is more
- * tolerant than advertised.
- */
- if (ncycles > ATMEL_SMC_MODE_TDF_MAX)
- ncycles = ATMEL_SMC_MODE_TDF_MAX;
- else if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
- ncycles = ATMEL_SMC_MODE_TDF_MIN;
-
- smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) |
- ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
-
- /*
- * Read pulse timing directly matches tRP:
- *
- * NRD_PULSE = tRP
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
- totalcycles += ncycles;
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * The write cycle timing is directly matching tWC, but is also
- * dependent on the setup and hold timings we calculated earlier,
- * which gives:
- *
- * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD)
- *
- * NRD_SETUP is always 0.
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps);
- ncycles = max(totalcycles, ncycles);
- ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * We don't want the CS line to be toggled between each byte/word
- * transfer from the NAND. The only way to guarantee that is to have
- * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
- *
- * NCS_RD_PULSE = NRD_CYCLE
- */
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /* Txxx timings are directly matching tXXX ones. */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TCLR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TADL_SHIFT,
- ncycles);
- /*
- * Version 4 of the ONFI spec mandates that tADL be at least 400
- * nanoseconds, but, depending on the master clock rate, 400 ns may not
- * fit in the tADL field of the SMC reg. We need to relax the check and
- * accept the -ERANGE return code.
- *
- * Note that previous versions of the ONFI spec had a lower tADL_min
- * (100 or 200 ns). It's not clear why this timing constraint got
- * increased but it seems most NANDs are fine with values lower than
- * 400ns, so we should be safe.
- */
- if (ret && ret != -ERANGE)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TAR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TRR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TWB_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /* Attach the CS line to the NFC logic. */
- smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL;
-
- /* Set the appropriate data bus width. */
- if (nand->base.options & NAND_BUSWIDTH_16)
- smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
-
- /* Operate in NRD/NWE READ/WRITEMODE. */
- smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD |
- ATMEL_SMC_MODE_WRITEMODE_NWE;
-
- return 0;
-}
-
-static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
- int csline,
- const struct nand_data_interface *conf)
-{
- struct atmel_nand_controller *nc;
- struct atmel_smc_cs_conf smcconf;
- struct atmel_nand_cs *cs;
- int ret;
-
- nc = to_nand_controller(nand->base.controller);
-
- ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
- if (ret)
- return ret;
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- cs = &nand->cs[csline];
- cs->smcconf = smcconf;
- atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
-
- return 0;
-}
-
-static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
- int csline,
- const struct nand_data_interface *conf)
-{
- struct atmel_hsmc_nand_controller *nc;
- struct atmel_smc_cs_conf smcconf;
- struct atmel_nand_cs *cs;
- int ret;
-
- nc = to_hsmc_nand_controller(nand->base.controller);
-
- ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
- if (ret)
- return ret;
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- cs = &nand->cs[csline];
- cs->smcconf = smcconf;
-
- if (cs->rb.type == ATMEL_NAND_NATIVE_RB)
- cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id);
-
- atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id,
- &cs->smcconf);
-
- return 0;
-}
-
-static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(nand->base.controller);
-
- if (csline >= nand->numcs ||
- (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
- return -EINVAL;
-
- return nc->caps->ops->setup_data_interface(nand, csline, conf);
-}
-
-static void atmel_nand_init(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- mtd->dev.parent = nc->dev;
- nand->base.controller = &nc->base;
-
- chip->cmd_ctrl = atmel_nand_cmd_ctrl;
- chip->read_byte = atmel_nand_read_byte;
- chip->read_word = atmel_nand_read_word;
- chip->write_byte = atmel_nand_write_byte;
- chip->read_buf = atmel_nand_read_buf;
- chip->write_buf = atmel_nand_write_buf;
- chip->select_chip = atmel_nand_select_chip;
-
- if (nc->mck && nc->caps->ops->setup_data_interface)
- chip->setup_data_interface = atmel_nand_setup_data_interface;
-
- /* Some NANDs require a longer delay than the default one (20us). */
- chip->chip_delay = 40;
-
- /*
- * Use a bounce buffer when the buffer passed by the MTD user is not
- * suitable for DMA.
- */
- if (nc->dmac)
- chip->options |= NAND_USE_BOUNCE_BUFFER;
-
- /* Default to HW ECC if pmecc is available. */
- if (nc->pmecc)
- chip->ecc.mode = NAND_ECC_HW;
-}
-
-static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct atmel_smc_nand_controller *smc_nc;
- int i;
-
- atmel_nand_init(nc, nand);
-
- smc_nc = to_smc_nand_controller(chip->controller);
- if (!smc_nc->matrix)
- return;
-
- /* Attach the CS to the NAND Flash logic. */
- for (i = 0; i < nand->numcs; i++)
- regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs,
- BIT(nand->cs[i].id), BIT(nand->cs[i].id));
-}
-
-static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
-
- atmel_nand_init(nc, nand);
-
- /* Overload some methods for the HSMC controller. */
- chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl;
- chip->select_chip = atmel_hsmc_nand_select_chip;
-}
-
-static int atmel_nand_detect(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- ret = nand_scan_ident(mtd, nand->numcs, NULL);
- if (ret)
- dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret);
-
- return ret;
-}
-
-static int atmel_nand_unregister(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- ret = mtd_device_unregister(mtd);
- if (ret)
- return ret;
-
- nand_cleanup(chip);
- list_del(&nand->node);
-
- return 0;
-}
-
-static int atmel_nand_register(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- if (nc->caps->legacy_of_bindings || !nc->dev->of_node) {
- /*
- * We keep the MTD name unchanged to avoid breaking platforms
- * where the MTD cmdline parser is used and the bootloader
- * has not been updated to use the new naming scheme.
- */
- mtd->name = "atmel_nand";
- } else if (!mtd->name) {
- /*
- * If the new bindings are used and the bootloader has not been
- * updated to pass a new mtdparts parameter on the cmdline, you
- * should define the following property in your nand node:
- *
- * label = "atmel_nand";
- *
- * This way, mtd->name will be set by the core when
- * nand_set_flash_node() is called.
- */
- mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL,
- "%s:nand.%d", dev_name(nc->dev),
- nand->cs[0].id);
- if (!mtd->name) {
- dev_err(nc->dev, "Failed to allocate mtd->name\n");
- return -ENOMEM;
- }
- }
-
- ret = nand_scan_tail(mtd);
- if (ret) {
- dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret);
- return ret;
- }
-
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret) {
- dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
- nand_cleanup(chip);
- return ret;
- }
-
- list_add_tail(&nand->node, &nc->chips);
-
- return 0;
-}
-
-static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
- struct device_node *np,
- int reg_cells)
-{
- struct atmel_nand *nand;
- struct gpio_desc *gpio;
- int numcs, ret, i;
-
- numcs = of_property_count_elems_of_size(np, "reg",
- reg_cells * sizeof(u32));
- if (numcs < 1) {
- dev_err(nc->dev, "Missing or invalid reg property\n");
- return ERR_PTR(-EINVAL);
- }
-
- nand = devm_kzalloc(nc->dev,
- sizeof(*nand) + (numcs * sizeof(*nand->cs)),
- GFP_KERNEL);
- if (!nand) {
- dev_err(nc->dev, "Failed to allocate NAND object\n");
- return ERR_PTR(-ENOMEM);
- }
-
- nand->numcs = numcs;
-
- gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "det", 0,
- &np->fwnode, GPIOD_IN,
- "nand-det");
- if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
- dev_err(nc->dev,
- "Failed to get detect gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return ERR_CAST(gpio);
- }
-
- if (!IS_ERR(gpio))
- nand->cdgpio = gpio;
-
- for (i = 0; i < numcs; i++) {
- struct resource res;
- u32 val;
-
- ret = of_address_to_resource(np, 0, &res);
- if (ret) {
- dev_err(nc->dev, "Invalid reg property (err = %d)\n",
- ret);
- return ERR_PTR(ret);
- }
-
- ret = of_property_read_u32_index(np, "reg", i * reg_cells,
- &val);
- if (ret) {
- dev_err(nc->dev, "Invalid reg property (err = %d)\n",
- ret);
- return ERR_PTR(ret);
- }
-
- nand->cs[i].id = val;
-
- nand->cs[i].io.dma = res.start;
- nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res);
- if (IS_ERR(nand->cs[i].io.virt))
- return ERR_CAST(nand->cs[i].io.virt);
-
- if (!of_property_read_u32(np, "atmel,rb", &val)) {
- if (val > ATMEL_NFC_MAX_RB_ID)
- return ERR_PTR(-EINVAL);
-
- nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB;
- nand->cs[i].rb.id = val;
- } else {
- gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev,
- "rb", i, &np->fwnode,
- GPIOD_IN, "nand-rb");
- if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
- dev_err(nc->dev,
- "Failed to get R/B gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return ERR_CAST(gpio);
- }
-
- if (!IS_ERR(gpio)) {
- nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
- nand->cs[i].rb.gpio = gpio;
- }
- }
-
- gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "cs",
- i, &np->fwnode,
- GPIOD_OUT_HIGH,
- "nand-cs");
- if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
- dev_err(nc->dev,
- "Failed to get CS gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return ERR_CAST(gpio);
- }
-
- if (!IS_ERR(gpio))
- nand->cs[i].csgpio = gpio;
- }
-
- nand_set_flash_node(&nand->base, np);
-
- return nand;
-}
-
-static int
-atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- int ret;
-
- /* No card inserted, skip this NAND. */
- if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) {
- dev_info(nc->dev, "No SmartMedia card inserted.\n");
- return 0;
- }
-
- nc->caps->ops->nand_init(nc, nand);
-
- ret = atmel_nand_detect(nand);
- if (ret)
- return ret;
-
- ret = nc->caps->ops->ecc_init(nand);
- if (ret)
- return ret;
-
- return atmel_nand_register(nand);
-}
-
-static int
-atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
-{
- struct atmel_nand *nand, *tmp;
- int ret;
-
- list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
- ret = atmel_nand_unregister(nand);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int
-atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
-{
- struct device *dev = nc->dev;
- struct platform_device *pdev = to_platform_device(dev);
- struct atmel_nand *nand;
- struct gpio_desc *gpio;
- struct resource *res;
-
- /*
- * Legacy bindings only allow connecting a single NAND with a unique CS
- * line to the controller.
- */
- nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs),
- GFP_KERNEL);
- if (!nand)
- return -ENOMEM;
-
- nand->numcs = 1;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nand->cs[0].io.virt = devm_ioremap_resource(dev, res);
- if (IS_ERR(nand->cs[0].io.virt))
- return PTR_ERR(nand->cs[0].io.virt);
-
- nand->cs[0].io.dma = res->start;
-
- /*
- * The old driver was hardcoding the CS id to 3 for all sama5
- * controllers. Since this id is only meaningful for the sama5
- * controller we can safely assign this id to 3 no matter the
- * controller.
- * If one wants to connect a NAND to a different CS line, he will
- * have to use the new bindings.
- */
- nand->cs[0].id = 3;
-
- /* R/B GPIO. */
- gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev, "Failed to get R/B gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return PTR_ERR(gpio);
- }
-
- if (gpio) {
- nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB;
- nand->cs[0].rb.gpio = gpio;
- }
-
- /* CS GPIO. */
- gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH);
- if (IS_ERR(gpio)) {
- dev_err(dev, "Failed to get CS gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return PTR_ERR(gpio);
- }
-
- nand->cs[0].csgpio = gpio;
-
- /* Card detect GPIO. */
- gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev,
- "Failed to get detect gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return PTR_ERR(gpio);
- }
-
- nand->cdgpio = gpio;
-
- nand_set_flash_node(&nand->base, nc->dev->of_node);
-
- return atmel_nand_controller_add_nand(nc, nand);
-}
-
-static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
-{
- struct device_node *np, *nand_np;
- struct device *dev = nc->dev;
- int ret, reg_cells;
- u32 val;
-
- /* We do not retrieve the SMC syscon when parsing old DTs. */
- if (nc->caps->legacy_of_bindings)
- return atmel_nand_controller_legacy_add_nands(nc);
-
- np = dev->of_node;
-
- ret = of_property_read_u32(np, "#address-cells", &val);
- if (ret) {
- dev_err(dev, "missing #address-cells property\n");
- return ret;
- }
-
- reg_cells = val;
-
- ret = of_property_read_u32(np, "#size-cells", &val);
- if (ret) {
- dev_err(dev, "missing #address-cells property\n");
- return ret;
- }
-
- reg_cells += val;
-
- for_each_child_of_node(np, nand_np) {
- struct atmel_nand *nand;
-
- nand = atmel_nand_create(nc, nand_np, reg_cells);
- if (IS_ERR(nand)) {
- ret = PTR_ERR(nand);
- goto err;
- }
-
- ret = atmel_nand_controller_add_nand(nc, nand);
- if (ret)
- goto err;
- }
-
- return 0;
-
-err:
- atmel_nand_controller_remove_nands(nc);
-
- return ret;
-}
-
-static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc)
-{
- if (nc->dmac)
- dma_release_channel(nc->dmac);
-
- clk_put(nc->mck);
-}
-
-static const struct of_device_id atmel_matrix_of_ids[] = {
- {
- .compatible = "atmel,at91sam9260-matrix",
- .data = (void *)AT91SAM9260_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9261-matrix",
- .data = (void *)AT91SAM9261_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9263-matrix",
- .data = (void *)AT91SAM9263_MATRIX_EBI0CSA,
- },
- {
- .compatible = "atmel,at91sam9rl-matrix",
- .data = (void *)AT91SAM9RL_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9g45-matrix",
- .data = (void *)AT91SAM9G45_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9n12-matrix",
- .data = (void *)AT91SAM9N12_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9x5-matrix",
- .data = (void *)AT91SAM9X5_MATRIX_EBICSA,
- },
- { /* sentinel */ },
-};
-
-static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
- struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps)
-{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- int ret;
-
- nand_hw_control_init(&nc->base);
- INIT_LIST_HEAD(&nc->chips);
- nc->dev = dev;
- nc->caps = caps;
-
- platform_set_drvdata(pdev, nc);
-
- nc->pmecc = devm_atmel_pmecc_get(dev);
- if (IS_ERR(nc->pmecc)) {
- ret = PTR_ERR(nc->pmecc);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Could not get PMECC object (err = %d)\n",
- ret);
- return ret;
- }
-
- if (nc->caps->has_dma) {
- dma_cap_mask_t mask;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_MEMCPY, mask);
-
- nc->dmac = dma_request_channel(mask, NULL, NULL);
- if (!nc->dmac)
- dev_err(nc->dev, "Failed to request DMA channel\n");
- }
-
- /* We do not retrieve the SMC syscon when parsing old DTs. */
- if (nc->caps->legacy_of_bindings)
- return 0;
-
- nc->mck = of_clk_get(dev->parent->of_node, 0);
- if (IS_ERR(nc->mck)) {
- dev_err(dev, "Failed to retrieve MCK clk\n");
- return PTR_ERR(nc->mck);
- }
-
- np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
- if (!np) {
- dev_err(dev, "Missing or invalid atmel,smc property\n");
- return -EINVAL;
- }
-
- nc->smc = syscon_node_to_regmap(np);
- of_node_put(np);
- if (IS_ERR(nc->smc)) {
- ret = PTR_ERR(nc->smc);
- dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int
-atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
-{
- struct device *dev = nc->base.dev;
- const struct of_device_id *match;
- struct device_node *np;
- int ret;
-
- /* We do not retrieve the matrix syscon when parsing old DTs. */
- if (nc->base.caps->legacy_of_bindings)
- return 0;
-
- np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0);
- if (!np)
- return 0;
-
- match = of_match_node(atmel_matrix_of_ids, np);
- if (!match) {
- of_node_put(np);
- return 0;
- }
-
- nc->matrix = syscon_node_to_regmap(np);
- of_node_put(np);
- if (IS_ERR(nc->matrix)) {
- ret = PTR_ERR(nc->matrix);
- dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret);
- return ret;
- }
-
- nc->ebi_csa_offs = (unsigned int)match->data;
-
- /*
- * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
- * add 4 to ->ebi_csa_offs.
- */
- if (of_device_is_compatible(dev->parent->of_node,
- "atmel,at91sam9263-ebi1"))
- nc->ebi_csa_offs += 4;
-
- return 0;
-}
-
-static int
-atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
-{
- struct regmap_config regmap_conf = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- };
-
- struct device *dev = nc->base.dev;
- struct device_node *nand_np, *nfc_np;
- void __iomem *iomem;
- struct resource res;
- int ret;
-
- nand_np = dev->of_node;
- nfc_np = of_find_compatible_node(dev->of_node, NULL,
- "atmel,sama5d3-nfc");
-
- nc->clk = of_clk_get(nfc_np, 0);
- if (IS_ERR(nc->clk)) {
- ret = PTR_ERR(nc->clk);
- dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = clk_prepare_enable(nc->clk);
- if (ret) {
- dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n",
- ret);
- goto out;
- }
-
- nc->irq = of_irq_get(nand_np, 0);
- if (nc->irq <= 0) {
- ret = nc->irq ?: -ENXIO;
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get IRQ number (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = of_address_to_resource(nfc_np, 0, &res);
- if (ret) {
- dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n",
- ret);
- goto out;
- }
-
- iomem = devm_ioremap_resource(dev, &res);
- if (IS_ERR(iomem)) {
- ret = PTR_ERR(iomem);
- goto out;
- }
-
- regmap_conf.name = "nfc-io";
- regmap_conf.max_register = resource_size(&res) - 4;
- nc->io = devm_regmap_init_mmio(dev, iomem, ®map_conf);
- if (IS_ERR(nc->io)) {
- ret = PTR_ERR(nc->io);
- dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = of_address_to_resource(nfc_np, 1, &res);
- if (ret) {
- dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n",
- ret);
- goto out;
- }
-
- iomem = devm_ioremap_resource(dev, &res);
- if (IS_ERR(iomem)) {
- ret = PTR_ERR(iomem);
- goto out;
- }
-
- regmap_conf.name = "smc";
- regmap_conf.max_register = resource_size(&res) - 4;
- nc->base.smc = devm_regmap_init_mmio(dev, iomem, ®map_conf);
- if (IS_ERR(nc->base.smc)) {
- ret = PTR_ERR(nc->base.smc);
- dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = of_address_to_resource(nfc_np, 2, &res);
- if (ret) {
- dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n",
- ret);
- goto out;
- }
-
- nc->sram.virt = devm_ioremap_resource(dev, &res);
- if (IS_ERR(nc->sram.virt)) {
- ret = PTR_ERR(nc->sram.virt);
- goto out;
- }
-
- nc->sram.dma = res.start;
-
-out:
- of_node_put(nfc_np);
-
- return ret;
-}
-
-static int
-atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
-{
- struct device *dev = nc->base.dev;
- struct device_node *np;
- int ret;
-
- np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
- if (!np) {
- dev_err(dev, "Missing or invalid atmel,smc property\n");
- return -EINVAL;
- }
-
- nc->hsmc_layout = atmel_hsmc_get_reg_layout(np);
-
- nc->irq = of_irq_get(np, 0);
- of_node_put(np);
- if (nc->irq <= 0) {
- ret = nc->irq ?: -ENXIO;
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get IRQ number (err = %d)\n",
- ret);
- return ret;
- }
-
- np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0);
- if (!np) {
- dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
- return -EINVAL;
- }
-
- nc->io = syscon_node_to_regmap(np);
- of_node_put(np);
- if (IS_ERR(nc->io)) {
- ret = PTR_ERR(nc->io);
- dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret);
- return ret;
- }
-
- nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node,
- "atmel,nfc-sram", 0);
- if (!nc->sram.pool) {
- dev_err(nc->base.dev, "Missing SRAM\n");
- return -ENOMEM;
- }
-
- nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool,
- ATMEL_NFC_SRAM_SIZE,
- &nc->sram.dma);
- if (!nc->sram.virt) {
- dev_err(nc->base.dev,
- "Could not allocate memory from the NFC SRAM pool\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int
-atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
-{
- struct atmel_hsmc_nand_controller *hsmc_nc;
- int ret;
-
- ret = atmel_nand_controller_remove_nands(nc);
- if (ret)
- return ret;
-
- hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
- if (hsmc_nc->sram.pool)
- gen_pool_free(hsmc_nc->sram.pool,
- (unsigned long)hsmc_nc->sram.virt,
- ATMEL_NFC_SRAM_SIZE);
-
- if (hsmc_nc->clk) {
- clk_disable_unprepare(hsmc_nc->clk);
- clk_put(hsmc_nc->clk);
- }
-
- atmel_nand_controller_cleanup(nc);
-
- return 0;
-}
-
-static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps)
-{
- struct device *dev = &pdev->dev;
- struct atmel_hsmc_nand_controller *nc;
- int ret;
-
- nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
- if (!nc)
- return -ENOMEM;
-
- ret = atmel_nand_controller_init(&nc->base, pdev, caps);
- if (ret)
- return ret;
-
- if (caps->legacy_of_bindings)
- ret = atmel_hsmc_nand_controller_legacy_init(nc);
- else
- ret = atmel_hsmc_nand_controller_init(nc);
-
- if (ret)
- return ret;
-
- /* Make sure all irqs are masked before registering our IRQ handler. */
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
- ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt,
- IRQF_SHARED, "nfc", nc);
- if (ret) {
- dev_err(dev,
- "Could not get register NFC interrupt handler (err = %d)\n",
- ret);
- goto err;
- }
-
- /* Initial NFC configuration. */
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG,
- ATMEL_HSMC_NFC_CFG_DTO_MAX);
-
- ret = atmel_nand_controller_add_nands(&nc->base);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- atmel_hsmc_nand_controller_remove(&nc->base);
-
- return ret;
-}
-
-static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
- .probe = atmel_hsmc_nand_controller_probe,
- .remove = atmel_hsmc_nand_controller_remove,
- .ecc_init = atmel_hsmc_nand_ecc_init,
- .nand_init = atmel_hsmc_nand_init,
- .setup_data_interface = atmel_hsmc_nand_setup_data_interface,
-};
-
-static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_hsmc_nc_ops,
-};
-
-/* Only used to parse old bindings. */
-static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_hsmc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static int atmel_smc_nand_controller_probe(struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps)
-{
- struct device *dev = &pdev->dev;
- struct atmel_smc_nand_controller *nc;
- int ret;
-
- nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
- if (!nc)
- return -ENOMEM;
-
- ret = atmel_nand_controller_init(&nc->base, pdev, caps);
- if (ret)
- return ret;
-
- ret = atmel_smc_nand_controller_init(nc);
- if (ret)
- return ret;
-
- return atmel_nand_controller_add_nands(&nc->base);
-}
-
-static int
-atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
-{
- int ret;
-
- ret = atmel_nand_controller_remove_nands(nc);
- if (ret)
- return ret;
-
- atmel_nand_controller_cleanup(nc);
-
- return 0;
-}
-
-/*
- * The SMC reg layout of at91rm9200 is completely different which prevents us
- * from re-using atmel_smc_nand_setup_data_interface() for the
- * ->setup_data_interface() hook.
- * At this point, there's no support for the at91rm9200 SMC IP, so we leave
- * ->setup_data_interface() unassigned.
- */
-static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
- .probe = atmel_smc_nand_controller_probe,
- .remove = atmel_smc_nand_controller_remove,
- .ecc_init = atmel_nand_ecc_init,
- .nand_init = atmel_smc_nand_init,
-};
-
-static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &at91rm9200_nc_ops,
-};
-
-static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
- .probe = atmel_smc_nand_controller_probe,
- .remove = atmel_smc_nand_controller_remove,
- .ecc_init = atmel_nand_ecc_init,
- .nand_init = atmel_smc_nand_init,
- .setup_data_interface = atmel_smc_nand_setup_data_interface,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = {
- .ale_offs = BIT(22),
- .cle_offs = BIT(21),
- .ops = &atmel_smc_nc_ops,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
-};
-
-/* Only used to parse old bindings. */
-static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = {
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = {
- .ale_offs = BIT(22),
- .cle_offs = BIT(21),
- .ops = &atmel_smc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static const struct of_device_id atmel_nand_controller_of_ids[] = {
- {
- .compatible = "atmel,at91rm9200-nand-controller",
- .data = &atmel_rm9200_nc_caps,
- },
- {
- .compatible = "atmel,at91sam9260-nand-controller",
- .data = &atmel_sam9260_nc_caps,
- },
- {
- .compatible = "atmel,at91sam9261-nand-controller",
- .data = &atmel_sam9261_nc_caps,
- },
- {
- .compatible = "atmel,at91sam9g45-nand-controller",
- .data = &atmel_sam9g45_nc_caps,
- },
- {
- .compatible = "atmel,sama5d3-nand-controller",
- .data = &atmel_sama5_nc_caps,
- },
- /* Support for old/deprecated bindings: */
- {
- .compatible = "atmel,at91rm9200-nand",
- .data = &atmel_rm9200_nand_caps,
- },
- {
- .compatible = "atmel,sama5d4-nand",
- .data = &atmel_rm9200_nand_caps,
- },
- {
- .compatible = "atmel,sama5d2-nand",
- .data = &atmel_rm9200_nand_caps,
- },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids);
-
-static int atmel_nand_controller_probe(struct platform_device *pdev)
-{
- const struct atmel_nand_controller_caps *caps;
-
- if (pdev->id_entry)
- caps = (void *)pdev->id_entry->driver_data;
- else
- caps = of_device_get_match_data(&pdev->dev);
-
- if (!caps) {
- dev_err(&pdev->dev, "Could not retrieve NFC caps\n");
- return -EINVAL;
- }
-
- if (caps->legacy_of_bindings) {
- u32 ale_offs = 21;
-
- /*
- * If we are parsing legacy DT props and the DT contains a
- * valid NFC node, forward the request to the sama5 logic.
- */
- if (of_find_compatible_node(pdev->dev.of_node, NULL,
- "atmel,sama5d3-nfc"))
- caps = &atmel_sama5_nand_caps;
-
- /*
- * Even if the compatible says we are dealing with an
- * at91rm9200 controller, the atmel,nand-has-dma specify that
- * this controller supports DMA, which means we are in fact
- * dealing with an at91sam9g45+ controller.
- */
- if (!caps->has_dma &&
- of_property_read_bool(pdev->dev.of_node,
- "atmel,nand-has-dma"))
- caps = &atmel_sam9g45_nand_caps;
-
- /*
- * All SoCs except the at91sam9261 are assigning ALE to A21 and
- * CLE to A22. If atmel,nand-addr-offset != 21 this means we're
- * actually dealing with an at91sam9261 controller.
- */
- of_property_read_u32(pdev->dev.of_node,
- "atmel,nand-addr-offset", &ale_offs);
- if (ale_offs != 21)
- caps = &atmel_sam9261_nand_caps;
- }
-
- return caps->ops->probe(pdev, caps);
-}
-
-static int atmel_nand_controller_remove(struct platform_device *pdev)
-{
- struct atmel_nand_controller *nc = platform_get_drvdata(pdev);
-
- return nc->caps->ops->remove(nc);
-}
-
-static __maybe_unused int atmel_nand_controller_resume(struct device *dev)
-{
- struct atmel_nand_controller *nc = dev_get_drvdata(dev);
- struct atmel_nand *nand;
-
- if (nc->pmecc)
- atmel_pmecc_reset(nc->pmecc);
-
- list_for_each_entry(nand, &nc->chips, node) {
- int i;
-
- for (i = 0; i < nand->numcs; i++)
- nand_reset(&nand->base, i);
- }
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL,
- atmel_nand_controller_resume);
-
-static struct platform_driver atmel_nand_controller_driver = {
- .driver = {
- .name = "atmel-nand-controller",
- .of_match_table = of_match_ptr(atmel_nand_controller_of_ids),
- .pm = &atmel_nand_controller_pm_ops,
- },
- .probe = atmel_nand_controller_probe,
- .remove = atmel_nand_controller_remove,
-};
-module_platform_driver(atmel_nand_controller_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
-MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs");
-MODULE_ALIAS("platform:atmel-nand-controller");
+++ /dev/null
-/*
- * Copyright 2017 ATMEL
- * Copyright 2017 Free Electrons
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
- *
- * Derived from the atmel_nand.c driver which contained the following
- * copyrights:
- *
- * Copyright 2003 Rick Bronson
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * Derived from drivers/mtd/spia.c
- * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
- *
- * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
- *
- * Derived from Das U-Boot source code
- * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- * Add Programmable Multibit ECC support for various AT91 SoC
- * Copyright 2012 ATMEL, Hong Xu
- *
- * Add Nand Flash Controller support for SAMA5 SoC
- * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
- *
- * 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.
- *
- * The PMECC is an hardware assisted BCH engine, which means part of the
- * ECC algorithm is left to the software. The hardware/software repartition
- * is explained in the "PMECC Controller Functional Description" chapter in
- * Atmel datasheets, and some of the functions in this file are directly
- * implementing the algorithms described in the "Software Implementation"
- * sub-section.
- *
- * TODO: it seems that the software BCH implementation in lib/bch.c is already
- * providing some of the logic we are implementing here. It would be smart
- * to expose the needed lib/bch.c helpers/functions and re-use them here.
- */
-
-#include <linux/genalloc.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include "pmecc.h"
-
-/* Galois field dimension */
-#define PMECC_GF_DIMENSION_13 13
-#define PMECC_GF_DIMENSION_14 14
-
-/* Primitive Polynomial used by PMECC */
-#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
-#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
-
-#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
-#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
-
-/* Time out value for reading PMECC status register */
-#define PMECC_MAX_TIMEOUT_MS 100
-
-/* PMECC Register Definitions */
-#define ATMEL_PMECC_CFG 0x0
-#define PMECC_CFG_BCH_STRENGTH(x) (x)
-#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0)
-#define PMECC_CFG_SECTOR512 (0 << 4)
-#define PMECC_CFG_SECTOR1024 (1 << 4)
-#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8)
-#define PMECC_CFG_READ_OP (0 << 12)
-#define PMECC_CFG_WRITE_OP (1 << 12)
-#define PMECC_CFG_SPARE_ENABLE BIT(16)
-#define PMECC_CFG_AUTO_ENABLE BIT(20)
-
-#define ATMEL_PMECC_SAREA 0x4
-#define ATMEL_PMECC_SADDR 0x8
-#define ATMEL_PMECC_EADDR 0xc
-
-#define ATMEL_PMECC_CLK 0x10
-#define PMECC_CLK_133MHZ (2 << 0)
-
-#define ATMEL_PMECC_CTRL 0x14
-#define PMECC_CTRL_RST BIT(0)
-#define PMECC_CTRL_DATA BIT(1)
-#define PMECC_CTRL_USER BIT(2)
-#define PMECC_CTRL_ENABLE BIT(4)
-#define PMECC_CTRL_DISABLE BIT(5)
-
-#define ATMEL_PMECC_SR 0x18
-#define PMECC_SR_BUSY BIT(0)
-#define PMECC_SR_ENABLE BIT(4)
-
-#define ATMEL_PMECC_IER 0x1c
-#define ATMEL_PMECC_IDR 0x20
-#define ATMEL_PMECC_IMR 0x24
-#define ATMEL_PMECC_ISR 0x28
-#define PMECC_ERROR_INT BIT(0)
-
-#define ATMEL_PMECC_ECC(sector, n) \
- ((((sector) + 1) * 0x40) + (n))
-
-#define ATMEL_PMECC_REM(sector, n) \
- ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200)
-
-/* PMERRLOC Register Definitions */
-#define ATMEL_PMERRLOC_ELCFG 0x0
-#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
-#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
-#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
-
-#define ATMEL_PMERRLOC_ELPRIM 0x4
-#define ATMEL_PMERRLOC_ELEN 0x8
-#define ATMEL_PMERRLOC_ELDIS 0xc
-#define PMERRLOC_DISABLE BIT(0)
-
-#define ATMEL_PMERRLOC_ELSR 0x10
-#define PMERRLOC_ELSR_BUSY BIT(0)
-
-#define ATMEL_PMERRLOC_ELIER 0x14
-#define ATMEL_PMERRLOC_ELIDR 0x18
-#define ATMEL_PMERRLOC_ELIMR 0x1c
-#define ATMEL_PMERRLOC_ELISR 0x20
-#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8)
-#define PMERRLOC_CALC_DONE BIT(0)
-
-#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28)
-
-#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs))
-
-struct atmel_pmecc_gf_tables {
- u16 *alpha_to;
- u16 *index_of;
-};
-
-struct atmel_pmecc_caps {
- const int *strengths;
- int nstrengths;
- int el_offset;
- bool correct_erased_chunks;
-};
-
-struct atmel_pmecc {
- struct device *dev;
- const struct atmel_pmecc_caps *caps;
-
- struct {
- void __iomem *base;
- void __iomem *errloc;
- } regs;
-
- struct mutex lock;
-};
-
-struct atmel_pmecc_user_conf_cache {
- u32 cfg;
- u32 sarea;
- u32 saddr;
- u32 eaddr;
-};
-
-struct atmel_pmecc_user {
- struct atmel_pmecc_user_conf_cache cache;
- struct atmel_pmecc *pmecc;
- const struct atmel_pmecc_gf_tables *gf_tables;
- int eccbytes;
- s16 *partial_syn;
- s16 *si;
- s16 *lmu;
- s16 *smu;
- s32 *mu;
- s32 *dmu;
- s32 *delta;
- u32 isr;
-};
-
-static DEFINE_MUTEX(pmecc_gf_tables_lock);
-static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512;
-static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024;
-
-static inline int deg(unsigned int poly)
-{
- /* polynomial degree is the most-significant bit index */
- return fls(poly) - 1;
-}
-
-static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly,
- struct atmel_pmecc_gf_tables *gf_tables)
-{
- unsigned int i, x = 1;
- const unsigned int k = BIT(deg(poly));
- unsigned int nn = BIT(mm) - 1;
-
- /* primitive polynomial must be of degree m */
- if (k != (1u << mm))
- return -EINVAL;
-
- for (i = 0; i < nn; i++) {
- gf_tables->alpha_to[i] = x;
- gf_tables->index_of[x] = i;
- if (i && (x == 1))
- /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
- return -EINVAL;
- x <<= 1;
- if (x & k)
- x ^= poly;
- }
- gf_tables->alpha_to[nn] = 1;
- gf_tables->index_of[0] = 0;
-
- return 0;
-}
-
-static const struct atmel_pmecc_gf_tables *
-atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req)
-{
- struct atmel_pmecc_gf_tables *gf_tables;
- unsigned int poly, degree, table_size;
- int ret;
-
- if (req->ecc.sectorsize == 512) {
- degree = PMECC_GF_DIMENSION_13;
- poly = PMECC_GF_13_PRIMITIVE_POLY;
- table_size = PMECC_LOOKUP_TABLE_SIZE_512;
- } else {
- degree = PMECC_GF_DIMENSION_14;
- poly = PMECC_GF_14_PRIMITIVE_POLY;
- table_size = PMECC_LOOKUP_TABLE_SIZE_1024;
- }
-
- gf_tables = kzalloc(sizeof(*gf_tables) +
- (2 * table_size * sizeof(u16)),
- GFP_KERNEL);
- if (!gf_tables)
- return ERR_PTR(-ENOMEM);
-
- gf_tables->alpha_to = (void *)(gf_tables + 1);
- gf_tables->index_of = gf_tables->alpha_to + table_size;
-
- ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables);
- if (ret) {
- kfree(gf_tables);
- return ERR_PTR(ret);
- }
-
- return gf_tables;
-}
-
-static const struct atmel_pmecc_gf_tables *
-atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req)
-{
- const struct atmel_pmecc_gf_tables **gf_tables, *ret;
-
- mutex_lock(&pmecc_gf_tables_lock);
- if (req->ecc.sectorsize == 512)
- gf_tables = &pmecc_gf_tables_512;
- else
- gf_tables = &pmecc_gf_tables_1024;
-
- ret = *gf_tables;
-
- if (!ret) {
- ret = atmel_pmecc_create_gf_tables(req);
- if (!IS_ERR(ret))
- *gf_tables = ret;
- }
- mutex_unlock(&pmecc_gf_tables_lock);
-
- return ret;
-}
-
-static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc,
- struct atmel_pmecc_user_req *req)
-{
- int i, max_eccbytes, eccbytes = 0, eccstrength = 0;
-
- if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0)
- return -EINVAL;
-
- if (req->ecc.ooboffset >= 0 &&
- req->ecc.ooboffset + req->ecc.bytes > req->oobsize)
- return -EINVAL;
-
- if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) {
- if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
- return -EINVAL;
-
- if (req->pagesize > 512)
- req->ecc.sectorsize = 1024;
- else
- req->ecc.sectorsize = 512;
- }
-
- if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024)
- return -EINVAL;
-
- if (req->pagesize % req->ecc.sectorsize)
- return -EINVAL;
-
- req->ecc.nsectors = req->pagesize / req->ecc.sectorsize;
-
- max_eccbytes = req->ecc.bytes;
-
- for (i = 0; i < pmecc->caps->nstrengths; i++) {
- int nbytes, strength = pmecc->caps->strengths[i];
-
- if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH &&
- strength < req->ecc.strength)
- continue;
-
- nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize),
- 8);
- nbytes *= req->ecc.nsectors;
-
- if (nbytes > max_eccbytes)
- break;
-
- eccstrength = strength;
- eccbytes = nbytes;
-
- if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
- break;
- }
-
- if (!eccstrength)
- return -EINVAL;
-
- req->ecc.bytes = eccbytes;
- req->ecc.strength = eccstrength;
-
- if (req->ecc.ooboffset < 0)
- req->ecc.ooboffset = req->oobsize - eccbytes;
-
- return 0;
-}
-
-struct atmel_pmecc_user *
-atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
- struct atmel_pmecc_user_req *req)
-{
- struct atmel_pmecc_user *user;
- const struct atmel_pmecc_gf_tables *gf_tables;
- int strength, size, ret;
-
- ret = atmel_pmecc_prepare_user_req(pmecc, req);
- if (ret)
- return ERR_PTR(ret);
-
- size = sizeof(*user);
- size = ALIGN(size, sizeof(u16));
- /* Reserve space for partial_syn, si and smu */
- size += ((2 * req->ecc.strength) + 1) * sizeof(u16) *
- (2 + req->ecc.strength + 2);
- /* Reserve space for lmu. */
- size += (req->ecc.strength + 1) * sizeof(u16);
- /* Reserve space for mu, dmu and delta. */
- size = ALIGN(size, sizeof(s32));
- size += (req->ecc.strength + 1) * sizeof(s32) * 3;
-
- user = kzalloc(size, GFP_KERNEL);
- if (!user)
- return ERR_PTR(-ENOMEM);
-
- user->pmecc = pmecc;
-
- user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16));
- user->si = user->partial_syn + ((2 * req->ecc.strength) + 1);
- user->lmu = user->si + ((2 * req->ecc.strength) + 1);
- user->smu = user->lmu + (req->ecc.strength + 1);
- user->mu = (s32 *)PTR_ALIGN(user->smu +
- (((2 * req->ecc.strength) + 1) *
- (req->ecc.strength + 2)),
- sizeof(s32));
- user->dmu = user->mu + req->ecc.strength + 1;
- user->delta = user->dmu + req->ecc.strength + 1;
-
- gf_tables = atmel_pmecc_get_gf_tables(req);
- if (IS_ERR(gf_tables)) {
- kfree(user);
- return ERR_CAST(gf_tables);
- }
-
- user->gf_tables = gf_tables;
-
- user->eccbytes = req->ecc.bytes / req->ecc.nsectors;
-
- for (strength = 0; strength < pmecc->caps->nstrengths; strength++) {
- if (pmecc->caps->strengths[strength] == req->ecc.strength)
- break;
- }
-
- user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) |
- PMECC_CFG_NSECTORS(req->ecc.nsectors);
-
- if (req->ecc.sectorsize == 1024)
- user->cache.cfg |= PMECC_CFG_SECTOR1024;
-
- user->cache.sarea = req->oobsize - 1;
- user->cache.saddr = req->ecc.ooboffset;
- user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1;
-
- return user;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_create_user);
-
-void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user)
-{
- kfree(user);
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user);
-
-static int get_strength(struct atmel_pmecc_user *user)
-{
- const int *strengths = user->pmecc->caps->strengths;
-
- return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK];
-}
-
-static int get_sectorsize(struct atmel_pmecc_user *user)
-{
- return user->cache.cfg & PMECC_LOOKUP_TABLE_SIZE_1024 ? 1024 : 512;
-}
-
-static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector)
-{
- int strength = get_strength(user);
- u32 value;
- int i;
-
- /* Fill odd syndromes */
- for (i = 0; i < strength; i++) {
- value = readl_relaxed(user->pmecc->regs.base +
- ATMEL_PMECC_REM(sector, i / 2));
- if (i & 1)
- value >>= 16;
-
- user->partial_syn[(2 * i) + 1] = value;
- }
-}
-
-static void atmel_pmecc_substitute(struct atmel_pmecc_user *user)
-{
- int degree = get_sectorsize(user) == 512 ? 13 : 14;
- int cw_len = BIT(degree) - 1;
- int strength = get_strength(user);
- s16 *alpha_to = user->gf_tables->alpha_to;
- s16 *index_of = user->gf_tables->index_of;
- s16 *partial_syn = user->partial_syn;
- s16 *si;
- int i, j;
-
- /*
- * si[] is a table that holds the current syndrome value,
- * an element of that table belongs to the field
- */
- si = user->si;
-
- memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1));
-
- /* Computation 2t syndromes based on S(x) */
- /* Odd syndromes */
- for (i = 1; i < 2 * strength; i += 2) {
- for (j = 0; j < degree; j++) {
- if (partial_syn[i] & BIT(j))
- si[i] = alpha_to[i * j] ^ si[i];
- }
- }
- /* Even syndrome = (Odd syndrome) ** 2 */
- for (i = 2, j = 1; j <= strength; i = ++j << 1) {
- if (si[j] == 0) {
- si[i] = 0;
- } else {
- s16 tmp;
-
- tmp = index_of[si[j]];
- tmp = (tmp * 2) % cw_len;
- si[i] = alpha_to[tmp];
- }
- }
-}
-
-static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user)
-{
- s16 *lmu = user->lmu;
- s16 *si = user->si;
- s32 *mu = user->mu;
- s32 *dmu = user->dmu;
- s32 *delta = user->delta;
- int degree = get_sectorsize(user) == 512 ? 13 : 14;
- int cw_len = BIT(degree) - 1;
- int strength = get_strength(user);
- int num = 2 * strength + 1;
- s16 *index_of = user->gf_tables->index_of;
- s16 *alpha_to = user->gf_tables->alpha_to;
- int i, j, k;
- u32 dmu_0_count, tmp;
- s16 *smu = user->smu;
-
- /* index of largest delta */
- int ro;
- int largest;
- int diff;
-
- dmu_0_count = 0;
-
- /* First Row */
-
- /* Mu */
- mu[0] = -1;
-
- memset(smu, 0, sizeof(s16) * num);
- smu[0] = 1;
-
- /* discrepancy set to 1 */
- dmu[0] = 1;
- /* polynom order set to 0 */
- lmu[0] = 0;
- delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
-
- /* Second Row */
-
- /* Mu */
- mu[1] = 0;
- /* Sigma(x) set to 1 */
- memset(&smu[num], 0, sizeof(s16) * num);
- smu[num] = 1;
-
- /* discrepancy set to S1 */
- dmu[1] = si[1];
-
- /* polynom order set to 0 */
- lmu[1] = 0;
-
- delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
-
- /* Init the Sigma(x) last row */
- memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num);
-
- for (i = 1; i <= strength; i++) {
- mu[i + 1] = i << 1;
- /* Begin Computing Sigma (Mu+1) and L(mu) */
- /* check if discrepancy is set to 0 */
- if (dmu[i] == 0) {
- dmu_0_count++;
-
- tmp = ((strength - (lmu[i] >> 1) - 1) / 2);
- if ((strength - (lmu[i] >> 1) - 1) & 0x1)
- tmp += 2;
- else
- tmp += 1;
-
- if (dmu_0_count == tmp) {
- for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
- smu[(strength + 1) * num + j] =
- smu[i * num + j];
-
- lmu[strength + 1] = lmu[i];
- return;
- }
-
- /* copy polynom */
- for (j = 0; j <= lmu[i] >> 1; j++)
- smu[(i + 1) * num + j] = smu[i * num + j];
-
- /* copy previous polynom order to the next */
- lmu[i + 1] = lmu[i];
- } else {
- ro = 0;
- largest = -1;
- /* find largest delta with dmu != 0 */
- for (j = 0; j < i; j++) {
- if ((dmu[j]) && (delta[j] > largest)) {
- largest = delta[j];
- ro = j;
- }
- }
-
- /* compute difference */
- diff = (mu[i] - mu[ro]);
-
- /* Compute degree of the new smu polynomial */
- if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
- lmu[i + 1] = lmu[i];
- else
- lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
-
- /* Init smu[i+1] with 0 */
- for (k = 0; k < num; k++)
- smu[(i + 1) * num + k] = 0;
-
- /* Compute smu[i+1] */
- for (k = 0; k <= lmu[ro] >> 1; k++) {
- s16 a, b, c;
-
- if (!(smu[ro * num + k] && dmu[i]))
- continue;
-
- a = index_of[dmu[i]];
- b = index_of[dmu[ro]];
- c = index_of[smu[ro * num + k]];
- tmp = a + (cw_len - b) + c;
- a = alpha_to[tmp % cw_len];
- smu[(i + 1) * num + (k + diff)] = a;
- }
-
- for (k = 0; k <= lmu[i] >> 1; k++)
- smu[(i + 1) * num + k] ^= smu[i * num + k];
- }
-
- /* End Computing Sigma (Mu+1) and L(mu) */
- /* In either case compute delta */
- delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
-
- /* Do not compute discrepancy for the last iteration */
- if (i >= strength)
- continue;
-
- for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
- tmp = 2 * (i - 1);
- if (k == 0) {
- dmu[i + 1] = si[tmp + 3];
- } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
- s16 a, b, c;
-
- a = index_of[smu[(i + 1) * num + k]];
- b = si[2 * (i - 1) + 3 - k];
- c = index_of[b];
- tmp = a + c;
- tmp %= cw_len;
- dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1];
- }
- }
- }
-}
-
-static int atmel_pmecc_err_location(struct atmel_pmecc_user *user)
-{
- int sector_size = get_sectorsize(user);
- int degree = sector_size == 512 ? 13 : 14;
- struct atmel_pmecc *pmecc = user->pmecc;
- int strength = get_strength(user);
- int ret, roots_nbr, i, err_nbr = 0;
- int num = (2 * strength) + 1;
- s16 *smu = user->smu;
- u32 val;
-
- writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS);
-
- for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) {
- writel_relaxed(smu[(strength + 1) * num + i],
- pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i));
- err_nbr++;
- }
-
- val = (err_nbr - 1) << 16;
- if (sector_size == 1024)
- val |= 1;
-
- writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG);
- writel((sector_size * 8) + (degree * strength),
- pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN);
-
- ret = readl_relaxed_poll_timeout(pmecc->regs.errloc +
- ATMEL_PMERRLOC_ELISR,
- val, val & PMERRLOC_CALC_DONE, 0,
- PMECC_MAX_TIMEOUT_MS * 1000);
- if (ret) {
- dev_err(pmecc->dev,
- "PMECC: Timeout to calculate error location.\n");
- return ret;
- }
-
- roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8;
- /* Number of roots == degree of smu hence <= cap */
- if (roots_nbr == user->lmu[strength + 1] >> 1)
- return err_nbr - 1;
-
- /*
- * Number of roots does not match the degree of smu
- * unable to correct error.
- */
- return -EBADMSG;
-}
-
-int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
- void *data, void *ecc)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- int sectorsize = get_sectorsize(user);
- int eccbytes = user->eccbytes;
- int i, nerrors;
-
- if (!(user->isr & BIT(sector)))
- return 0;
-
- atmel_pmecc_gen_syndrome(user, sector);
- atmel_pmecc_substitute(user);
- atmel_pmecc_get_sigma(user);
-
- nerrors = atmel_pmecc_err_location(user);
- if (nerrors < 0)
- return nerrors;
-
- for (i = 0; i < nerrors; i++) {
- const char *area;
- int byte, bit;
- u32 errpos;
- u8 *ptr;
-
- errpos = readl_relaxed(pmecc->regs.errloc +
- ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i));
- errpos--;
-
- byte = errpos / 8;
- bit = errpos % 8;
-
- if (byte < sectorsize) {
- ptr = data + byte;
- area = "data";
- } else if (byte < sectorsize + eccbytes) {
- ptr = ecc + byte - sectorsize;
- area = "ECC";
- } else {
- dev_dbg(pmecc->dev,
- "Invalid errpos value (%d, max is %d)\n",
- errpos, (sectorsize + eccbytes) * 8);
- return -EINVAL;
- }
-
- dev_dbg(pmecc->dev,
- "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n",
- area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit)));
-
- *ptr ^= BIT(bit);
- }
-
- return nerrors;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector);
-
-bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user)
-{
- return user->pmecc->caps->correct_erased_chunks;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks);
-
-void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
- int sector, void *ecc)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- u8 *ptr = ecc;
- int i;
-
- for (i = 0; i < user->eccbytes; i++)
- ptr[i] = readb_relaxed(pmecc->regs.base +
- ATMEL_PMECC_ECC(sector, i));
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
-
-void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
-{
- writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
- writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_reset);
-
-int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- u32 cfg;
-
- if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) {
- dev_err(pmecc->dev, "Bad ECC operation!");
- return -EINVAL;
- }
-
- mutex_lock(&user->pmecc->lock);
-
- cfg = user->cache.cfg;
- if (op == NAND_ECC_WRITE)
- cfg |= PMECC_CFG_WRITE_OP;
- else
- cfg |= PMECC_CFG_AUTO_ENABLE;
-
- writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG);
- writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA);
- writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR);
- writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR);
-
- writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
- writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
-
-void atmel_pmecc_disable(struct atmel_pmecc_user *user)
-{
- atmel_pmecc_reset(user->pmecc);
- mutex_unlock(&user->pmecc->lock);
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
-
-int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- u32 status;
- int ret;
-
- ret = readl_relaxed_poll_timeout(pmecc->regs.base +
- ATMEL_PMECC_SR,
- status, !(status & PMECC_SR_BUSY), 0,
- PMECC_MAX_TIMEOUT_MS * 1000);
- if (ret) {
- dev_err(pmecc->dev,
- "Timeout while waiting for PMECC ready.\n");
- return ret;
- }
-
- user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy);
-
-static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev,
- const struct atmel_pmecc_caps *caps,
- int pmecc_res_idx, int errloc_res_idx)
-{
- struct device *dev = &pdev->dev;
- struct atmel_pmecc *pmecc;
- struct resource *res;
-
- pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL);
- if (!pmecc)
- return ERR_PTR(-ENOMEM);
-
- pmecc->caps = caps;
- pmecc->dev = dev;
- mutex_init(&pmecc->lock);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx);
- pmecc->regs.base = devm_ioremap_resource(dev, res);
- if (IS_ERR(pmecc->regs.base))
- return ERR_CAST(pmecc->regs.base);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx);
- pmecc->regs.errloc = devm_ioremap_resource(dev, res);
- if (IS_ERR(pmecc->regs.errloc))
- return ERR_CAST(pmecc->regs.errloc);
-
- /* Disable all interrupts before registering the PMECC handler. */
- writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
- atmel_pmecc_reset(pmecc);
-
- return pmecc;
-}
-
-static void devm_atmel_pmecc_put(struct device *dev, void *res)
-{
- struct atmel_pmecc **pmecc = res;
-
- put_device((*pmecc)->dev);
-}
-
-static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev,
- struct device_node *np)
-{
- struct platform_device *pdev;
- struct atmel_pmecc *pmecc, **ptr;
-
- pdev = of_find_device_by_node(np);
- if (!pdev || !platform_get_drvdata(pdev))
- return ERR_PTR(-EPROBE_DEFER);
-
- ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- get_device(&pdev->dev);
- pmecc = platform_get_drvdata(pdev);
-
- *ptr = pmecc;
-
- devres_add(userdev, ptr);
-
- return pmecc;
-}
-
-static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
-
-static struct atmel_pmecc_caps at91sam9g45_caps = {
- .strengths = atmel_pmecc_strengths,
- .nstrengths = 5,
- .el_offset = 0x8c,
-};
-
-static struct atmel_pmecc_caps sama5d4_caps = {
- .strengths = atmel_pmecc_strengths,
- .nstrengths = 5,
- .el_offset = 0x8c,
- .correct_erased_chunks = true,
-};
-
-static struct atmel_pmecc_caps sama5d2_caps = {
- .strengths = atmel_pmecc_strengths,
- .nstrengths = 6,
- .el_offset = 0xac,
- .correct_erased_chunks = true,
-};
-
-static const struct of_device_id atmel_pmecc_legacy_match[] = {
- { .compatible = "atmel,sama5d4-nand", &sama5d4_caps },
- { .compatible = "atmel,sama5d2-nand", &sama5d2_caps },
- { /* sentinel */ }
-};
-
-struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev)
-{
- struct atmel_pmecc *pmecc;
- struct device_node *np;
-
- if (!userdev)
- return ERR_PTR(-EINVAL);
-
- if (!userdev->of_node)
- return NULL;
-
- np = of_parse_phandle(userdev->of_node, "ecc-engine", 0);
- if (np) {
- pmecc = atmel_pmecc_get_by_node(userdev, np);
- of_node_put(np);
- } else {
- /*
- * Support old DT bindings: in this case the PMECC iomem
- * resources are directly defined in the user pdev at position
- * 1 and 2. Extract all relevant information from there.
- */
- struct platform_device *pdev = to_platform_device(userdev);
- const struct atmel_pmecc_caps *caps;
- const struct of_device_id *match;
-
- /* No PMECC engine available. */
- if (!of_property_read_bool(userdev->of_node,
- "atmel,has-pmecc"))
- return NULL;
-
- caps = &at91sam9g45_caps;
-
- /* Find the caps associated to the NAND dev node. */
- match = of_match_node(atmel_pmecc_legacy_match,
- userdev->of_node);
- if (match && match->data)
- caps = match->data;
-
- pmecc = atmel_pmecc_create(pdev, caps, 1, 2);
- }
-
- return pmecc;
-}
-EXPORT_SYMBOL(devm_atmel_pmecc_get);
-
-static const struct of_device_id atmel_pmecc_match[] = {
- { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps },
- { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps },
- { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, atmel_pmecc_match);
-
-static int atmel_pmecc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- const struct atmel_pmecc_caps *caps;
- struct atmel_pmecc *pmecc;
-
- caps = of_device_get_match_data(&pdev->dev);
- if (!caps) {
- dev_err(dev, "Invalid caps\n");
- return -EINVAL;
- }
-
- pmecc = atmel_pmecc_create(pdev, caps, 0, 1);
- if (IS_ERR(pmecc))
- return PTR_ERR(pmecc);
-
- platform_set_drvdata(pdev, pmecc);
-
- return 0;
-}
-
-static struct platform_driver atmel_pmecc_driver = {
- .driver = {
- .name = "atmel-pmecc",
- .of_match_table = of_match_ptr(atmel_pmecc_match),
- },
- .probe = atmel_pmecc_probe,
-};
-module_platform_driver(atmel_pmecc_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
-MODULE_DESCRIPTION("PMECC engine driver");
-MODULE_ALIAS("platform:atmel_pmecc");
+++ /dev/null
-/*
- * © Copyright 2016 ATMEL
- * © Copyright 2016 Free Electrons
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
- *
- * Derived from the atmel_nand.c driver which contained the following
- * copyrights:
- *
- * Copyright © 2003 Rick Bronson
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * Derived from drivers/mtd/spia.c
- * Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
- *
- *
- * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
- *
- * Derived from Das U-Boot source code
- * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- * Add Programmable Multibit ECC support for various AT91 SoC
- * © Copyright 2012 ATMEL, Hong Xu
- *
- * Add Nand Flash Controller support for SAMA5 SoC
- * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
- *
- * 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.
- *
- */
-
-#ifndef ATMEL_PMECC_H
-#define ATMEL_PMECC_H
-
-#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0
-#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0
-#define ATMEL_PMECC_OOBOFFSET_AUTO -1
-
-struct atmel_pmecc_user_req {
- int pagesize;
- int oobsize;
- struct {
- int strength;
- int bytes;
- int sectorsize;
- int nsectors;
- int ooboffset;
- } ecc;
-};
-
-struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev);
-
-struct atmel_pmecc_user *
-atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
- struct atmel_pmecc_user_req *req);
-void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
-
-void atmel_pmecc_reset(struct atmel_pmecc *pmecc);
-int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
-void atmel_pmecc_disable(struct atmel_pmecc_user *user);
-int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
-int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
- void *data, void *ecc);
-bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user);
-void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
- int sector, void *ecc);
-
-#endif /* ATMEL_PMECC_H */
+++ /dev/null
-/*
- * drivers/mtd/nand/au1550nd.c
- *
- * Copyright (C) 2004 Embedded Edge, LLC
- *
- * 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/slab.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/platform_device.h>
-#include <asm/io.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1550nd.h>
-
-
-struct au1550nd_ctx {
- struct nand_chip chip;
-
- int cs;
- void __iomem *base;
- void (*write_byte)(struct mtd_info *, u_char);
-};
-
-/**
- * au_read_byte - read one byte from the chip
- * @mtd: MTD device structure
- *
- * read function for 8bit buswidth
- */
-static u_char au_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- u_char ret = readb(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- return ret;
-}
-
-/**
- * au_write_byte - write one byte to the chip
- * @mtd: MTD device structure
- * @byte: pointer to data byte to write
- *
- * write function for 8it buswidth
- */
-static void au_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- writeb(byte, this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
-}
-
-/**
- * au_read_byte16 - read one byte endianness aware from the chip
- * @mtd: MTD device structure
- *
- * read function for 16bit buswidth with endianness conversion
- */
-static u_char au_read_byte16(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
- wmb(); /* drain writebuffer */
- return ret;
-}
-
-/**
- * au_write_byte16 - write one byte endianness aware to the chip
- * @mtd: MTD device structure
- * @byte: pointer to data byte to write
- *
- * write function for 16bit buswidth with endianness conversion
- */
-static void au_write_byte16(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
-}
-
-/**
- * au_read_word - read one word from the chip
- * @mtd: MTD device structure
- *
- * read function for 16bit buswidth without endianness conversion
- */
-static u16 au_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- u16 ret = readw(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- return ret;
-}
-
-/**
- * au_write_buf - write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * write function for 8bit buswidth
- */
-static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
-
- for (i = 0; i < len; i++) {
- writeb(buf[i], this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
- }
-}
-
-/**
- * au_read_buf - read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- *
- * read function for 8bit buswidth
- */
-static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
-
- for (i = 0; i < len; i++) {
- buf[i] = readb(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- }
-}
-
-/**
- * au_write_buf16 - write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * write function for 16bit buswidth
- */
-static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++) {
- writew(p[i], this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
- }
-
-}
-
-/**
- * au_read_buf16 - read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- *
- * read function for 16bit buswidth
- */
-static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++) {
- p[i] = readw(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- }
-}
-
-/* Select the chip by setting nCE to low */
-#define NAND_CTL_SETNCE 1
-/* Deselect the chip by setting nCE to high */
-#define NAND_CTL_CLRNCE 2
-/* Select the command latch by setting CLE to high */
-#define NAND_CTL_SETCLE 3
-/* Deselect the command latch by setting CLE to low */
-#define NAND_CTL_CLRCLE 4
-/* Select the address latch by setting ALE to high */
-#define NAND_CTL_SETALE 5
-/* Deselect the address latch by setting ALE to low */
-#define NAND_CTL_CLRALE 6
-
-static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
- chip);
-
- switch (cmd) {
-
- case NAND_CTL_SETCLE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_CMD;
- break;
-
- case NAND_CTL_CLRCLE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA;
- break;
-
- case NAND_CTL_SETALE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_ADDR;
- break;
-
- case NAND_CTL_CLRALE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA;
- /* FIXME: Nobody knows why this is necessary,
- * but it works only that way */
- udelay(1);
- break;
-
- case NAND_CTL_SETNCE:
- /* assert (force assert) chip enable */
- alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL);
- break;
-
- case NAND_CTL_CLRNCE:
- /* deassert chip enable */
- alchemy_wrsmem(0, AU1000_MEM_STNDCTL);
- break;
- }
-
- this->IO_ADDR_R = this->IO_ADDR_W;
-
- wmb(); /* Drain the writebuffer */
-}
-
-int au1550_device_ready(struct mtd_info *mtd)
-{
- return (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) ? 1 : 0;
-}
-
-/**
- * au1550_select_chip - control -CE line
- * Forbid driving -CE manually permitting the NAND controller to do this.
- * Keeping -CE asserted during the whole sector reads interferes with the
- * NOR flash and PCMCIA drivers as it causes contention on the static bus.
- * We only have to hold -CE low for the NAND read commands since the flash
- * chip needs it to be asserted during chip not ready time but the NAND
- * controller keeps it released.
- *
- * @mtd: MTD device structure
- * @chip: chipnumber to select, -1 for deselect
- */
-static void au1550_select_chip(struct mtd_info *mtd, int chip)
-{
-}
-
-/**
- * au1550_command - Send command to NAND device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
- */
-static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
- chip);
- int ce_override = 0, i;
- unsigned long flags = 0;
-
- /* Begin command latch cycle */
- au1550_hwcontrol(mtd, NAND_CTL_SETCLE);
- /*
- * Write out the command to the device.
- */
- if (command == NAND_CMD_SEQIN) {
- int readcmd;
-
- if (column >= mtd->writesize) {
- /* OOB area */
- column -= mtd->writesize;
- readcmd = NAND_CMD_READOOB;
- } else if (column < 256) {
- /* First 256 bytes --> READ0 */
- readcmd = NAND_CMD_READ0;
- } else {
- column -= 256;
- readcmd = NAND_CMD_READ1;
- }
- ctx->write_byte(mtd, readcmd);
- }
- ctx->write_byte(mtd, command);
-
- /* Set ALE and clear CLE to start address cycle */
- au1550_hwcontrol(mtd, NAND_CTL_CLRCLE);
-
- if (column != -1 || page_addr != -1) {
- au1550_hwcontrol(mtd, NAND_CTL_SETALE);
-
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (this->options & NAND_BUSWIDTH_16 &&
- !nand_opcode_8bits(command))
- column >>= 1;
- ctx->write_byte(mtd, column);
- }
- if (page_addr != -1) {
- ctx->write_byte(mtd, (u8)(page_addr & 0xff));
-
- if (command == NAND_CMD_READ0 ||
- command == NAND_CMD_READ1 ||
- command == NAND_CMD_READOOB) {
- /*
- * NAND controller will release -CE after
- * the last address byte is written, so we'll
- * have to forcibly assert it. No interrupts
- * are allowed while we do this as we don't
- * want the NOR flash or PCMCIA drivers to
- * steal our precious bytes of data...
- */
- ce_override = 1;
- local_irq_save(flags);
- au1550_hwcontrol(mtd, NAND_CTL_SETNCE);
- }
-
- ctx->write_byte(mtd, (u8)(page_addr >> 8));
-
- if (this->options & NAND_ROW_ADDR_3)
- ctx->write_byte(mtd,
- ((page_addr >> 16) & 0x0f));
- }
- /* Latch in address */
- au1550_hwcontrol(mtd, NAND_CTL_CLRALE);
- }
-
- /*
- * Program and erase have their own busy handlers.
- * Status and sequential in need no delay.
- */
- switch (command) {
-
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_STATUS:
- return;
-
- case NAND_CMD_RESET:
- break;
-
- case NAND_CMD_READ0:
- case NAND_CMD_READ1:
- case NAND_CMD_READOOB:
- /* Check if we're really driving -CE low (just in case) */
- if (unlikely(!ce_override))
- break;
-
- /* Apply a short delay always to ensure that we do wait tWB. */
- ndelay(100);
- /* Wait for a chip to become ready... */
- for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i)
- udelay(1);
-
- /* Release -CE and re-enable interrupts. */
- au1550_hwcontrol(mtd, NAND_CTL_CLRNCE);
- local_irq_restore(flags);
- return;
- }
- /* Apply this short delay always to ensure that we do wait tWB. */
- ndelay(100);
-
- while(!this->dev_ready(mtd));
-}
-
-static int find_nand_cs(unsigned long nand_base)
-{
- void __iomem *base =
- (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR);
- unsigned long addr, staddr, start, mask, end;
- int i;
-
- for (i = 0; i < 4; i++) {
- addr = 0x1000 + (i * 0x10); /* CSx */
- staddr = __raw_readl(base + addr + 0x08); /* STADDRx */
- /* figure out the decoded range of this CS */
- start = (staddr << 4) & 0xfffc0000;
- mask = (staddr << 18) & 0xfffc0000;
- end = (start | (start - 1)) & ~(start ^ mask);
- if ((nand_base >= start) && (nand_base < end))
- return i;
- }
-
- return -ENODEV;
-}
-
-static int au1550nd_probe(struct platform_device *pdev)
-{
- struct au1550nd_platdata *pd;
- struct au1550nd_ctx *ctx;
- struct nand_chip *this;
- struct mtd_info *mtd;
- struct resource *r;
- int ret, cs;
-
- pd = dev_get_platdata(&pdev->dev);
- if (!pd) {
- dev_err(&pdev->dev, "missing platform data\n");
- return -ENODEV;
- }
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- dev_err(&pdev->dev, "no NAND memory resource\n");
- ret = -ENODEV;
- goto out1;
- }
- if (request_mem_region(r->start, resource_size(r), "au1550-nand")) {
- dev_err(&pdev->dev, "cannot claim NAND memory area\n");
- ret = -ENOMEM;
- goto out1;
- }
-
- ctx->base = ioremap_nocache(r->start, 0x1000);
- if (!ctx->base) {
- dev_err(&pdev->dev, "cannot remap NAND memory area\n");
- ret = -ENODEV;
- goto out2;
- }
-
- this = &ctx->chip;
- mtd = nand_to_mtd(this);
- mtd->dev.parent = &pdev->dev;
-
- /* figure out which CS# r->start belongs to */
- cs = find_nand_cs(r->start);
- if (cs < 0) {
- dev_err(&pdev->dev, "cannot detect NAND chipselect\n");
- ret = -ENODEV;
- goto out3;
- }
- ctx->cs = cs;
-
- this->dev_ready = au1550_device_ready;
- this->select_chip = au1550_select_chip;
- this->cmdfunc = au1550_command;
-
- /* 30 us command delay time */
- this->chip_delay = 30;
- this->ecc.mode = NAND_ECC_SOFT;
- this->ecc.algo = NAND_ECC_HAMMING;
-
- if (pd->devwidth)
- this->options |= NAND_BUSWIDTH_16;
-
- this->read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte;
- ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte;
- this->read_word = au_read_word;
- this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf;
- this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf;
-
- ret = nand_scan(mtd, 1);
- if (ret) {
- dev_err(&pdev->dev, "NAND scan failed with %d\n", ret);
- goto out3;
- }
-
- mtd_device_register(mtd, pd->parts, pd->num_parts);
-
- platform_set_drvdata(pdev, ctx);
-
- return 0;
-
-out3:
- iounmap(ctx->base);
-out2:
- release_mem_region(r->start, resource_size(r));
-out1:
- kfree(ctx);
- return ret;
-}
-
-static int au1550nd_remove(struct platform_device *pdev)
-{
- struct au1550nd_ctx *ctx = platform_get_drvdata(pdev);
- struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- nand_release(nand_to_mtd(&ctx->chip));
- iounmap(ctx->base);
- release_mem_region(r->start, 0x1000);
- kfree(ctx);
- return 0;
-}
-
-static struct platform_driver au1550nd_driver = {
- .driver = {
- .name = "au1550-nand",
- },
- .probe = au1550nd_probe,
- .remove = au1550nd_remove,
-};
-
-module_platform_driver(au1550nd_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Embedded Edge, LLC");
-MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Free Electrons
+ *
+ * Authors:
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Peter Pan <peterpandong@micron.com>
+ */
+
+#define pr_fmt(fmt) "nand-bbt: " fmt
+
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+
+/**
+ * nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
+ * @nand: NAND device
+ *
+ * Initialize the in-memory BBT.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_bbt_init(struct nand_device *nand)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned int nblocks = nanddev_neraseblocks(nand);
+ unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
+ BITS_PER_LONG);
+
+ nand->bbt.cache = kzalloc(nwords, GFP_KERNEL);
+ if (!nand->bbt.cache)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_init);
+
+/**
+ * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
+ * @nand: NAND device
+ *
+ * Undoes what has been done in nanddev_bbt_init()
+ */
+void nanddev_bbt_cleanup(struct nand_device *nand)
+{
+ kfree(nand->bbt.cache);
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
+
+/**
+ * nanddev_bbt_update() - Update a BBT
+ * @nand: nand device
+ *
+ * Update the BBT. Currently a NOP function since on-flash bbt is not yet
+ * supported.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_bbt_update(struct nand_device *nand)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_update);
+
+/**
+ * nanddev_bbt_get_block_status() - Return the status of an eraseblock
+ * @nand: nand device
+ * @entry: the BBT entry
+ *
+ * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
+ * is bigger than the BBT size.
+ */
+int nanddev_bbt_get_block_status(const struct nand_device *nand,
+ unsigned int entry)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned long *pos = nand->bbt.cache +
+ ((entry * bits_per_block) / BITS_PER_LONG);
+ unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
+ unsigned long status;
+
+ if (entry >= nanddev_neraseblocks(nand))
+ return -ERANGE;
+
+ status = pos[0] >> offs;
+ if (bits_per_block + offs > BITS_PER_LONG)
+ status |= pos[1] << (BITS_PER_LONG - offs);
+
+ return status & GENMASK(bits_per_block - 1, 0);
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
+
+/**
+ * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
+ * in-memory BBT
+ * @nand: nand device
+ * @entry: the BBT entry to update
+ * @status: the new status
+ *
+ * Update an entry of the in-memory BBT. If you want to push the updated BBT
+ * the NAND you should call nanddev_bbt_update().
+ *
+ * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
+ * size.
+ */
+int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
+ enum nand_bbt_block_status status)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned long *pos = nand->bbt.cache +
+ ((entry * bits_per_block) / BITS_PER_LONG);
+ unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
+ unsigned long val = status & GENMASK(bits_per_block - 1, 0);
+
+ if (entry >= nanddev_neraseblocks(nand))
+ return -ERANGE;
+
+ pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
+ pos[0] |= val << offs;
+
+ if (bits_per_block + offs > BITS_PER_LONG) {
+ unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
+
+ pos[1] &= ~GENMASK(rbits - 1, 0);
+ pos[1] |= val >> rbits;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);
+++ /dev/null
-bcm47xxnflash-y += main.o
-bcm47xxnflash-y += ops_bcm4706.o
-
-obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash.o
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __BCM47XXNFLASH_H
-#define __BCM47XXNFLASH_H
-
-#ifndef pr_fmt
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#endif
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-
-struct bcm47xxnflash {
- struct bcma_drv_cc *cc;
-
- struct nand_chip nand_chip;
-
- unsigned curr_command;
- int curr_page_addr;
- int curr_column;
-
- u8 id_data[8];
-};
-
-int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n);
-
-#endif /* BCM47XXNFLASH */
+++ /dev/null
-/*
- * BCM47XX NAND flash driver
- *
- * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com>
- *
- * 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 "bcm47xxnflash.h"
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/bcma/bcma.h>
-
-MODULE_DESCRIPTION("NAND flash driver for BCMA bus");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Rafał Miłecki");
-
-static const char *probes[] = { "bcm47xxpart", NULL };
-
-static int bcm47xxnflash_probe(struct platform_device *pdev)
-{
- struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
- struct bcm47xxnflash *b47n;
- struct mtd_info *mtd;
- int err = 0;
-
- b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL);
- if (!b47n)
- return -ENOMEM;
-
- nand_set_controller_data(&b47n->nand_chip, b47n);
- mtd = nand_to_mtd(&b47n->nand_chip);
- mtd->dev.parent = &pdev->dev;
- b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash);
-
- if (b47n->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
- err = bcm47xxnflash_ops_bcm4706_init(b47n);
- } else {
- pr_err("Device not supported\n");
- err = -ENOTSUPP;
- }
- if (err) {
- pr_err("Initialization failed: %d\n", err);
- return err;
- }
-
- platform_set_drvdata(pdev, b47n);
-
- err = mtd_device_parse_register(mtd, probes, NULL, NULL, 0);
- if (err) {
- pr_err("Failed to register MTD device: %d\n", err);
- return err;
- }
-
- return 0;
-}
-
-static int bcm47xxnflash_remove(struct platform_device *pdev)
-{
- struct bcm47xxnflash *nflash = platform_get_drvdata(pdev);
-
- nand_release(nand_to_mtd(&nflash->nand_chip));
-
- return 0;
-}
-
-static struct platform_driver bcm47xxnflash_driver = {
- .probe = bcm47xxnflash_probe,
- .remove = bcm47xxnflash_remove,
- .driver = {
- .name = "bcma_nflash",
- },
-};
-
-module_platform_driver(bcm47xxnflash_driver);
+++ /dev/null
-/*
- * BCM47XX NAND flash driver
- *
- * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com>
- *
- * 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 "bcm47xxnflash.h"
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/bcma/bcma.h>
-
-/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
- * shown ~1000 retries as maxiumum. */
-#define NFLASH_READY_RETRIES 10000
-
-#define NFLASH_SECTOR_SIZE 512
-
-#define NCTL_CMD0 0x00010000
-#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */
-#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */
-#define NCTL_CMD1W 0x00080000
-#define NCTL_READ 0x00100000
-#define NCTL_WRITE 0x00200000
-#define NCTL_SPECADDR 0x01000000
-#define NCTL_READY 0x04000000
-#define NCTL_ERR 0x08000000
-#define NCTL_CSA 0x40000000
-#define NCTL_START 0x80000000
-
-/**************************************************
- * Various helpers
- **************************************************/
-
-static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock)
-{
- return ((ns * 1000 * clock) / 1000000) + 1;
-}
-
-static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code)
-{
- int i = 0;
-
- bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code);
- for (i = 0; i < NFLASH_READY_RETRIES; i++) {
- if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) {
- i = 0;
- break;
- }
- }
- if (i) {
- pr_err("NFLASH control command not ready!\n");
- return -EBUSY;
- }
- return 0;
-}
-
-static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc)
-{
- int i;
-
- for (i = 0; i < NFLASH_READY_RETRIES; i++) {
- if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) {
- if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) &
- BCMA_CC_NFLASH_CTL_ERR) {
- pr_err("Error on polling\n");
- return -EBUSY;
- } else {
- return 0;
- }
- }
- }
-
- pr_err("Polling timeout!\n");
- return -EBUSY;
-}
-
-/**************************************************
- * R/W
- **************************************************/
-
-static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf,
- int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- u32 ctlcode;
- u32 *dest = (u32 *)buf;
- int i;
- int toread;
-
- BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask);
- /* Don't validate column using nand_chip->page_shift, it may be bigger
- * when accessing OOB */
-
- while (len) {
- /* We can read maximum of 0x200 bytes at once */
- toread = min(len, 0x200);
-
- /* Set page and column */
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR,
- b47n->curr_column);
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR,
- b47n->curr_page_addr);
-
- /* Prepare to read */
- ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL |
- NCTL_CMD0;
- ctlcode |= NAND_CMD_READSTART << 8;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode))
- return;
- if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc))
- return;
-
- /* Eventually read some data :) */
- for (i = 0; i < toread; i += 4, dest++) {
- ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ;
- if (i == toread - 4) /* Last read goes without that */
- ctlcode &= ~NCTL_CSA;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc,
- ctlcode))
- return;
- *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA);
- }
-
- b47n->curr_column += toread;
- len -= toread;
- }
-}
-
-static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- struct bcma_drv_cc *cc = b47n->cc;
-
- u32 ctlcode;
- const u32 *data = (u32 *)buf;
- int i;
-
- BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask);
- /* Don't validate column using nand_chip->page_shift, it may be bigger
- * when accessing OOB */
-
- for (i = 0; i < len; i += 4, data++) {
- bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data);
-
- ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE;
- if (i == len - 4) /* Last read goes without that */
- ctlcode &= ~NCTL_CSA;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) {
- pr_err("%s ctl_cmd didn't work!\n", __func__);
- return;
- }
- }
-
- b47n->curr_column += len;
-}
-
-/**************************************************
- * NAND chip ops
- **************************************************/
-
-static void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- u32 code = 0;
-
- if (cmd == NAND_CMD_NONE)
- return;
-
- if (cmd & NAND_CTRL_CLE)
- code = cmd | NCTL_CMD0;
-
- /* nCS is not needed for reset command */
- if (cmd != NAND_CMD_RESET)
- code |= NCTL_CSA;
-
- bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code);
-}
-
-/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */
-static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd,
- int chip)
-{
- return;
-}
-
-static int bcm47xxnflash_ops_bcm4706_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY);
-}
-
-/*
- * Default nand_command and nand_command_lp don't match BCM4706 hardware layout.
- * For example, reading chip id is performed in a non-standard way.
- * Setting column and page is also handled differently, we use a special
- * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert
- * standard commands would be much more complicated.
- */
-static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd,
- unsigned command, int column,
- int page_addr)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- struct bcma_drv_cc *cc = b47n->cc;
- u32 ctlcode;
- int i;
-
- if (column != -1)
- b47n->curr_column = column;
- if (page_addr != -1)
- b47n->curr_page_addr = page_addr;
-
- switch (command) {
- case NAND_CMD_RESET:
- nand_chip->cmd_ctrl(mtd, command, NAND_CTRL_CLE);
-
- ndelay(100);
- nand_wait_ready(mtd);
- break;
- case NAND_CMD_READID:
- ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0;
- ctlcode |= NAND_CMD_READID;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) {
- pr_err("READID error\n");
- break;
- }
-
- /*
- * Reading is specific, last one has to go without NCTL_CSA
- * bit. We don't know how many reads NAND subsystem is going
- * to perform, so cache everything.
- */
- for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) {
- ctlcode = NCTL_CSA | NCTL_READ;
- if (i == ARRAY_SIZE(b47n->id_data) - 1)
- ctlcode &= ~NCTL_CSA;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc,
- ctlcode)) {
- pr_err("READID error\n");
- break;
- }
- b47n->id_data[i] =
- bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA)
- & 0xFF;
- }
-
- break;
- case NAND_CMD_STATUS:
- ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
- pr_err("STATUS command error\n");
- break;
- case NAND_CMD_READ0:
- break;
- case NAND_CMD_READOOB:
- if (page_addr != -1)
- b47n->curr_column += mtd->writesize;
- break;
- case NAND_CMD_ERASE1:
- bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
- b47n->curr_page_addr);
- ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 |
- NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8);
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
- pr_err("ERASE1 failed\n");
- break;
- case NAND_CMD_ERASE2:
- break;
- case NAND_CMD_SEQIN:
- /* Set page and column */
- bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR,
- b47n->curr_column);
- bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
- b47n->curr_page_addr);
-
- /* Prepare to write */
- ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0;
- ctlcode |= NAND_CMD_SEQIN;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
- pr_err("SEQIN failed\n");
- break;
- case NAND_CMD_PAGEPROG:
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 |
- NAND_CMD_PAGEPROG))
- pr_err("PAGEPROG failed\n");
- if (bcm47xxnflash_ops_bcm4706_poll(cc))
- pr_err("PAGEPROG not ready\n");
- break;
- default:
- pr_err("Command 0x%X unsupported\n", command);
- break;
- }
- b47n->curr_command = command;
-}
-
-static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- struct bcma_drv_cc *cc = b47n->cc;
- u32 tmp = 0;
-
- switch (b47n->curr_command) {
- case NAND_CMD_READID:
- if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) {
- pr_err("Requested invalid id_data: %d\n",
- b47n->curr_column);
- return 0;
- }
- return b47n->id_data[b47n->curr_column++];
- case NAND_CMD_STATUS:
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ))
- return 0;
- return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff;
- case NAND_CMD_READOOB:
- bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4);
- return tmp & 0xFF;
- }
-
- pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command);
- return 0;
-}
-
-static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd,
- uint8_t *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- switch (b47n->curr_command) {
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- bcm47xxnflash_ops_bcm4706_read(mtd, buf, len);
- return;
- }
-
- pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command);
-}
-
-static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- switch (b47n->curr_command) {
- case NAND_CMD_SEQIN:
- bcm47xxnflash_ops_bcm4706_write(mtd, buf, len);
- return;
- }
-
- pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command);
-}
-
-/**************************************************
- * Init
- **************************************************/
-
-int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
-{
- struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip;
- int err;
- u32 freq;
- u16 clock;
- u8 w0, w1, w2, w3, w4;
-
- unsigned long chipsize; /* MiB */
- u8 tbits, col_bits, col_size, row_bits, row_bsize;
- u32 val;
-
- b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip;
- nand_chip->cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl;
- nand_chip->dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready;
- b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc;
- b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
- b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf;
- b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf;
- b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp;
- b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- nand_chip->chip_delay = 50;
- b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
- b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */
-
- /* Enable NAND flash access */
- bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
- BCMA_CC_4706_FLASHSCFG_NF1);
-
- /* Configure wait counters */
- if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) {
- /* 400 MHz */
- freq = 400000000 / 4;
- } else {
- freq = bcma_chipco_pll_read(b47n->cc, 4);
- freq = (freq & 0xFFF) >> 3;
- /* Fixed reference clock 25 MHz and m = 2 */
- freq = (freq * 25000000 / 2) / 4;
- }
- clock = freq / 1000000;
- w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock);
- w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock);
- w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
- w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
- w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock);
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0,
- (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0));
-
- /* Scan NAND */
- err = nand_scan(nand_to_mtd(&b47n->nand_chip), 1);
- if (err) {
- pr_err("Could not scan NAND flash: %d\n", err);
- goto exit;
- }
-
- /* Configure FLASH */
- chipsize = b47n->nand_chip.chipsize >> 20;
- tbits = ffs(chipsize); /* find first bit set */
- if (!tbits || tbits != fls(chipsize)) {
- pr_err("Invalid flash size: 0x%lX\n", chipsize);
- err = -ENOTSUPP;
- goto exit;
- }
- tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */
-
- col_bits = b47n->nand_chip.page_shift + 1;
- col_size = (col_bits + 7) / 8;
-
- row_bits = tbits - col_bits + 1;
- row_bsize = (row_bits + 7) / 8;
-
- val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2;
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val);
-
-exit:
- if (err)
- bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
- ~BCMA_CC_4706_FLASHSCFG_NF1);
- return err;
-}
+++ /dev/null
-/* linux/drivers/mtd/nand/bf5xx_nand.c
- *
- * Copyright 2006-2008 Analog Devices Inc.
- * http://blackfin.uclinux.org/
- * Bryan Wu <bryan.wu@analog.com>
- *
- * Blackfin BF5xx on-chip NAND flash controller driver
- *
- * Derived from drivers/mtd/nand/s3c2410.c
- * Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk>
- *
- * Derived from drivers/mtd/nand/cafe.c
- * Copyright © 2006 Red Hat, Inc.
- * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
- *
- * Changelog:
- * 12-Jun-2007 Bryan Wu: Initial version
- * 18-Jul-2007 Bryan Wu:
- * - ECC_HW and ECC_SW supported
- * - DMA supported in ECC_HW
- * - YAFFS tested as rootfs in both ECC_HW and ECC_SW
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/bitops.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/blackfin.h>
-#include <asm/dma.h>
-#include <asm/cacheflush.h>
-#include <asm/nand.h>
-#include <asm/portmux.h>
-
-#define DRV_NAME "bf5xx-nand"
-#define DRV_VERSION "1.2"
-#define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>"
-#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver"
-
-/* NFC_STAT Masks */
-#define NBUSY 0x01 /* Not Busy */
-#define WB_FULL 0x02 /* Write Buffer Full */
-#define PG_WR_STAT 0x04 /* Page Write Pending */
-#define PG_RD_STAT 0x08 /* Page Read Pending */
-#define WB_EMPTY 0x10 /* Write Buffer Empty */
-
-/* NFC_IRQSTAT Masks */
-#define NBUSYIRQ 0x01 /* Not Busy IRQ */
-#define WB_OVF 0x02 /* Write Buffer Overflow */
-#define WB_EDGE 0x04 /* Write Buffer Edge Detect */
-#define RD_RDY 0x08 /* Read Data Ready */
-#define WR_DONE 0x10 /* Page Write Done */
-
-/* NFC_RST Masks */
-#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */
-
-/* NFC_PGCTL Masks */
-#define PG_RD_START 0x01 /* Page Read Start */
-#define PG_WR_START 0x02 /* Page Write Start */
-
-#ifdef CONFIG_MTD_NAND_BF5XX_HWECC
-static int hardware_ecc = 1;
-#else
-static int hardware_ecc;
-#endif
-
-static const unsigned short bfin_nfc_pin_req[] =
- {P_NAND_CE,
- P_NAND_RB,
- P_NAND_D0,
- P_NAND_D1,
- P_NAND_D2,
- P_NAND_D3,
- P_NAND_D4,
- P_NAND_D5,
- P_NAND_D6,
- P_NAND_D7,
- P_NAND_WE,
- P_NAND_RE,
- P_NAND_CLE,
- P_NAND_ALE,
- 0};
-
-#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
-static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 7)
- return -ERANGE;
-
- oobregion->offset = section * 8;
- oobregion->length = 3;
-
- return 0;
-}
-
-static int bootrom_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 7)
- return -ERANGE;
-
- oobregion->offset = (section * 8) + 3;
- oobregion->length = 5;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = {
- .ecc = bootrom_ooblayout_ecc,
- .free = bootrom_ooblayout_free,
-};
-#endif
-
-/*
- * Data structures for bf5xx nand flash controller driver
- */
-
-/* bf5xx nand info */
-struct bf5xx_nand_info {
- /* mtd info */
- struct nand_hw_control controller;
- struct nand_chip chip;
-
- /* platform info */
- struct bf5xx_nand_platform *platform;
-
- /* device info */
- struct device *device;
-
- /* DMA stuff */
- struct completion dma_completion;
-};
-
-/*
- * Conversion functions
- */
-static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info,
- chip);
-}
-
-static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
-{
- return platform_get_drvdata(pdev);
-}
-
-static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
-{
- return dev_get_platdata(&pdev->dev);
-}
-
-/*
- * struct nand_chip interface function pointers
- */
-
-/*
- * bf5xx_nand_hwcontrol
- *
- * Issue command and address cycles to the chip
- */
-static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- if (cmd == NAND_CMD_NONE)
- return;
-
- while (bfin_read_NFC_STAT() & WB_FULL)
- cpu_relax();
-
- if (ctrl & NAND_CLE)
- bfin_write_NFC_CMD(cmd);
- else if (ctrl & NAND_ALE)
- bfin_write_NFC_ADDR(cmd);
- SSYNC();
-}
-
-/*
- * bf5xx_nand_devready()
- *
- * returns 0 if the nand is busy, 1 if it is ready
- */
-static int bf5xx_nand_devready(struct mtd_info *mtd)
-{
- unsigned short val = bfin_read_NFC_STAT();
-
- if ((val & NBUSY) == NBUSY)
- return 1;
- else
- return 0;
-}
-
-/*
- * ECC functions
- * These allow the bf5xx to use the controller's ECC
- * generator block to ECC the data as it passes through
- */
-
-/*
- * ECC error correction function
- */
-static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- u32 syndrome[5];
- u32 calced, stored;
- int i;
- unsigned short failing_bit, failing_byte;
- u_char data;
-
- calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
- stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
-
- syndrome[0] = (calced ^ stored);
-
- /*
- * syndrome 0: all zero
- * No error in data
- * No action
- */
- if (!syndrome[0] || !calced || !stored)
- return 0;
-
- /*
- * sysdrome 0: only one bit is one
- * ECC data was incorrect
- * No action
- */
- if (hweight32(syndrome[0]) == 1) {
- dev_err(info->device, "ECC data was incorrect!\n");
- return -EBADMSG;
- }
-
- syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
- syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
- syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
- syndrome[4] = syndrome[2] ^ syndrome[3];
-
- for (i = 0; i < 5; i++)
- dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);
-
- dev_info(info->device,
- "calced[0x%08x], stored[0x%08x]\n",
- calced, stored);
-
- /*
- * sysdrome 0: exactly 11 bits are one, each parity
- * and parity' pair is 1 & 0 or 0 & 1.
- * 1-bit correctable error
- * Correct the error
- */
- if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
- dev_info(info->device,
- "1-bit correctable error, correct it.\n");
- dev_info(info->device,
- "syndrome[1] 0x%08x\n", syndrome[1]);
-
- failing_bit = syndrome[1] & 0x7;
- failing_byte = syndrome[1] >> 0x3;
- data = *(dat + failing_byte);
- data = data ^ (0x1 << failing_bit);
- *(dat + failing_byte) = data;
-
- return 1;
- }
-
- /*
- * sysdrome 0: random data
- * More than 1-bit error, non-correctable error
- * Discard data, mark bad block
- */
- dev_err(info->device,
- "More than 1-bit error, non-correctable error.\n");
- dev_err(info->device,
- "Please discard data, mark bad block\n");
-
- return -EBADMSG;
-}
-
-static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret, bitflips = 0;
-
- ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
- if (ret < 0)
- return ret;
-
- bitflips = ret;
-
- /* If ecc size is 512, correct second 256 bytes */
- if (chip->ecc.size == 512) {
- dat += 256;
- read_ecc += 3;
- calc_ecc += 3;
- ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
- if (ret < 0)
- return ret;
-
- bitflips += ret;
- }
-
- return bitflips;
-}
-
-static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- return;
-}
-
-static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_code)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
- u16 ecc0, ecc1;
- u32 code[2];
- u8 *p;
-
- /* first 3 bytes ECC code for 256 page size */
- ecc0 = bfin_read_NFC_ECC0();
- ecc1 = bfin_read_NFC_ECC1();
-
- code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
-
- dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
-
- p = (u8 *) code;
- memcpy(ecc_code, p, 3);
-
- /* second 3 bytes ECC code for 512 ecc size */
- if (chip->ecc.size == 512) {
- ecc0 = bfin_read_NFC_ECC2();
- ecc1 = bfin_read_NFC_ECC3();
- code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
-
- /* second 3 bytes in ecc_code for second 256
- * bytes of 512 page size
- */
- p = (u8 *) (code + 1);
- memcpy((ecc_code + 3), p, 3);
- dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);
- }
-
- return 0;
-}
-
-/*
- * PIO mode for buffer writing and reading
- */
-static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
- unsigned short val;
-
- /*
- * Data reads are requested by first writing to NFC_DATA_RD
- * and then reading back from NFC_READ.
- */
- for (i = 0; i < len; i++) {
- while (bfin_read_NFC_STAT() & WB_FULL)
- cpu_relax();
-
- /* Contents do not matter */
- bfin_write_NFC_DATA_RD(0x0000);
- SSYNC();
-
- while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)
- cpu_relax();
-
- buf[i] = bfin_read_NFC_READ();
-
- val = bfin_read_NFC_IRQSTAT();
- val |= RD_RDY;
- bfin_write_NFC_IRQSTAT(val);
- SSYNC();
- }
-}
-
-static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd)
-{
- uint8_t val;
-
- bf5xx_nand_read_buf(mtd, &val, 1);
-
- return val;
-}
-
-static void bf5xx_nand_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- while (bfin_read_NFC_STAT() & WB_FULL)
- cpu_relax();
-
- bfin_write_NFC_DATA_WR(buf[i]);
- SSYNC();
- }
-}
-
-static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- /*
- * Data reads are requested by first writing to NFC_DATA_RD
- * and then reading back from NFC_READ.
- */
- bfin_write_NFC_DATA_RD(0x5555);
-
- SSYNC();
-
- for (i = 0; i < len; i++)
- p[i] = bfin_read_NFC_READ();
-}
-
-static void bf5xx_nand_write_buf16(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- int i;
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++)
- bfin_write_NFC_DATA_WR(p[i]);
-
- SSYNC();
-}
-
-/*
- * DMA functions for buffer writing and reading
- */
-static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
-{
- struct bf5xx_nand_info *info = dev_id;
-
- clear_dma_irqstat(CH_NFC);
- disable_dma(CH_NFC);
- complete(&info->dma_completion);
-
- return IRQ_HANDLED;
-}
-
-static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
- uint8_t *buf, int is_read)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
- unsigned short val;
-
- dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
- mtd, buf, is_read);
-
- /*
- * Before starting a dma transfer, be sure to invalidate/flush
- * the cache over the address range of your DMA buffer to
- * prevent cache coherency problems. Otherwise very subtle bugs
- * can be introduced to your driver.
- */
- if (is_read)
- invalidate_dcache_range((unsigned int)buf,
- (unsigned int)(buf + chip->ecc.size));
- else
- flush_dcache_range((unsigned int)buf,
- (unsigned int)(buf + chip->ecc.size));
-
- /*
- * This register must be written before each page is
- * transferred to generate the correct ECC register
- * values.
- */
- bfin_write_NFC_RST(ECC_RST);
- SSYNC();
- while (bfin_read_NFC_RST() & ECC_RST)
- cpu_relax();
-
- disable_dma(CH_NFC);
- clear_dma_irqstat(CH_NFC);
-
- /* setup DMA register with Blackfin DMA API */
- set_dma_config(CH_NFC, 0x0);
- set_dma_start_addr(CH_NFC, (unsigned long) buf);
-
- /* The DMAs have different size on BF52x and BF54x */
-#ifdef CONFIG_BF52x
- set_dma_x_count(CH_NFC, (chip->ecc.size >> 1));
- set_dma_x_modify(CH_NFC, 2);
- val = DI_EN | WDSIZE_16;
-#endif
-
-#ifdef CONFIG_BF54x
- set_dma_x_count(CH_NFC, (chip->ecc.size >> 2));
- set_dma_x_modify(CH_NFC, 4);
- val = DI_EN | WDSIZE_32;
-#endif
- /* setup write or read operation */
- if (is_read)
- val |= WNR;
- set_dma_config(CH_NFC, val);
- enable_dma(CH_NFC);
-
- /* Start PAGE read/write operation */
- if (is_read)
- bfin_write_NFC_PGCTL(PG_RD_START);
- else
- bfin_write_NFC_PGCTL(PG_WR_START);
- wait_for_completion(&info->dma_completion);
-}
-
-static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
- uint8_t *buf, int len)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
-
- if (len == chip->ecc.size)
- bf5xx_nand_dma_rw(mtd, buf, 1);
- else
- bf5xx_nand_read_buf(mtd, buf, len);
-}
-
-static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
-
- if (len == chip->ecc.size)
- bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0);
- else
- bf5xx_nand_write_buf(mtd, buf, len);
-}
-
-static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- bf5xx_nand_read_buf(mtd, buf, mtd->writesize);
- bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return 0;
-}
-
-static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required,
- int page)
-{
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-/*
- * System initialization functions
- */
-static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
-{
- int ret;
-
- /* Do not use dma */
- if (!hardware_ecc)
- return 0;
-
- init_completion(&info->dma_completion);
-
- /* Request NFC DMA channel */
- ret = request_dma(CH_NFC, "BF5XX NFC driver");
- if (ret < 0) {
- dev_err(info->device, " unable to get DMA channel\n");
- return ret;
- }
-
-#ifdef CONFIG_BF54x
- /* Setup DMAC1 channel mux for NFC which shared with SDH */
- bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1);
- SSYNC();
-#endif
-
- set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info);
-
- /* Turn off the DMA channel first */
- disable_dma(CH_NFC);
- return 0;
-}
-
-static void bf5xx_nand_dma_remove(struct bf5xx_nand_info *info)
-{
- /* Free NFC DMA channel */
- if (hardware_ecc)
- free_dma(CH_NFC);
-}
-
-/*
- * BF5XX NFC hardware initialization
- * - pin mux setup
- * - clear interrupt status
- */
-static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
-{
- int err = 0;
- unsigned short val;
- struct bf5xx_nand_platform *plat = info->platform;
-
- /* setup NFC_CTL register */
- dev_info(info->device,
- "data_width=%d, wr_dly=%d, rd_dly=%d\n",
- (plat->data_width ? 16 : 8),
- plat->wr_dly, plat->rd_dly);
-
- val = (1 << NFC_PG_SIZE_OFFSET) |
- (plat->data_width << NFC_NWIDTH_OFFSET) |
- (plat->rd_dly << NFC_RDDLY_OFFSET) |
- (plat->wr_dly << NFC_WRDLY_OFFSET);
- dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val);
-
- bfin_write_NFC_CTL(val);
- SSYNC();
-
- /* clear interrupt status */
- bfin_write_NFC_IRQMASK(0x0);
- SSYNC();
- val = bfin_read_NFC_IRQSTAT();
- bfin_write_NFC_IRQSTAT(val);
- SSYNC();
-
- /* DMA initialization */
- if (bf5xx_nand_dma_init(info))
- err = -ENXIO;
-
- return err;
-}
-
-/*
- * Device management interface
- */
-static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
-{
- struct mtd_info *mtd = nand_to_mtd(&info->chip);
- struct mtd_partition *parts = info->platform->partitions;
- int nr = info->platform->nr_partitions;
-
- return mtd_device_register(mtd, parts, nr);
-}
-
-static int bf5xx_nand_remove(struct platform_device *pdev)
-{
- struct bf5xx_nand_info *info = to_nand_info(pdev);
-
- /* first thing we need to do is release all our mtds
- * and their partitions, then go through freeing the
- * resources used
- */
- nand_release(nand_to_mtd(&info->chip));
-
- peripheral_free_list(bfin_nfc_pin_req);
- bf5xx_nand_dma_remove(info);
-
- return 0;
-}
-
-static int bf5xx_nand_scan(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- return ret;
-
- if (hardware_ecc) {
- /*
- * for nand with page size > 512B, think it as several sections with 512B
- */
- if (likely(mtd->writesize >= 512)) {
- chip->ecc.size = 512;
- chip->ecc.bytes = 6;
- chip->ecc.strength = 2;
- } else {
- chip->ecc.size = 256;
- chip->ecc.bytes = 3;
- chip->ecc.strength = 1;
- bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET));
- SSYNC();
- }
- }
-
- return nand_scan_tail(mtd);
-}
-
-/*
- * bf5xx_nand_probe
- *
- * called by device layer when it finds a device matching
- * one our driver can handled. This code checks to see if
- * it can allocate all necessary resources then calls the
- * nand layer to look for devices
- */
-static int bf5xx_nand_probe(struct platform_device *pdev)
-{
- struct bf5xx_nand_platform *plat = to_nand_plat(pdev);
- struct bf5xx_nand_info *info = NULL;
- struct nand_chip *chip = NULL;
- struct mtd_info *mtd = NULL;
- int err = 0;
-
- dev_dbg(&pdev->dev, "(%p)\n", pdev);
-
- if (!plat) {
- dev_err(&pdev->dev, "no platform specific information\n");
- return -EINVAL;
- }
-
- if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) {
- dev_err(&pdev->dev, "requesting Peripherals failed\n");
- return -EFAULT;
- }
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- err = -ENOMEM;
- goto out_err;
- }
-
- platform_set_drvdata(pdev, info);
-
- nand_hw_control_init(&info->controller);
-
- info->device = &pdev->dev;
- info->platform = plat;
-
- /* initialise chip data struct */
- chip = &info->chip;
- mtd = nand_to_mtd(&info->chip);
-
- if (plat->data_width)
- chip->options |= NAND_BUSWIDTH_16;
-
- chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN;
-
- &n