diff options
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 569 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 71 | ||||
-rw-r--r-- | drivers/mtd/nand/bbt.c | 130 | ||||
-rw-r--r-- | drivers/mtd/nand/bf5xx_nand.c | 862 | ||||
-rw-r--r-- | drivers/mtd/nand/core.c | 244 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/Kconfig | 71 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/Makefile | 14 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/generic.c | 116 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/omap2.c | 660 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/onenand_base.c | 4014 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/onenand_bbt.c | 248 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/samsung.c | 1012 | ||||
-rw-r--r-- | drivers/mtd/nand/onenand/samsung.h | 59 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Kconfig | 537 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Makefile | 66 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/ams-delta.c (renamed from drivers/mtd/nand/ams-delta.c) | 13 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/Makefile (renamed from drivers/mtd/nand/atmel/Makefile) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/nand-controller.c (renamed from drivers/mtd/nand/atmel/nand-controller.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/pmecc.c (renamed from drivers/mtd/nand/atmel/pmecc.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/pmecc.h (renamed from drivers/mtd/nand/atmel/pmecc.h) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/au1550nd.c (renamed from drivers/mtd/nand/au1550nd.c) | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/bcm47xxnflash/Makefile (renamed from drivers/mtd/nand/bcm47xxnflash/Makefile) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h (renamed from drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/bcm47xxnflash/main.c (renamed from drivers/mtd/nand/bcm47xxnflash/main.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c (renamed from drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/Makefile (renamed from drivers/mtd/nand/brcmnand/Makefile) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c (renamed from drivers/mtd/nand/brcmnand/bcm63138_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c (renamed from drivers/mtd/nand/brcmnand/bcm6368_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/brcmnand.c (renamed from drivers/mtd/nand/brcmnand/brcmnand.c) | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/brcmnand.h (renamed from drivers/mtd/nand/brcmnand/brcmnand.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c (renamed from drivers/mtd/nand/brcmnand/brcmstb_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/iproc_nand.c (renamed from drivers/mtd/nand/brcmnand/iproc_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/cafe_nand.c (renamed from drivers/mtd/nand/cafe_nand.c) | 14 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/cmx270_nand.c (renamed from drivers/mtd/nand/cmx270_nand.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/cs553x_nand.c (renamed from drivers/mtd/nand/cs553x_nand.c) | 11 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/davinci_nand.c (renamed from drivers/mtd/nand/davinci_nand.c) | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/denali.c (renamed from drivers/mtd/nand/denali.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/denali.h (renamed from drivers/mtd/nand/denali.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/denali_dt.c (renamed from drivers/mtd/nand/denali_dt.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/denali_pci.c (renamed from drivers/mtd/nand/denali_pci.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/diskonchip.c (renamed from drivers/mtd/nand/diskonchip.c) | 78 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/docg4.c (renamed from drivers/mtd/nand/docg4.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/fsl_elbc_nand.c (renamed from drivers/mtd/nand/fsl_elbc_nand.c) | 8 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/fsl_ifc_nand.c (renamed from drivers/mtd/nand/fsl_ifc_nand.c) | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/fsl_upm.c (renamed from drivers/mtd/nand/fsl_upm.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/fsmc_nand.c (renamed from drivers/mtd/nand/fsmc_nand.c) | 252 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpio.c (renamed from drivers/mtd/nand/gpio.c) | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/Makefile (renamed from drivers/mtd/nand/gpmi-nand/Makefile) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/bch-regs.h (renamed from drivers/mtd/nand/gpmi-nand/bch-regs.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c (renamed from drivers/mtd/nand/gpmi-nand/gpmi-lib.c) | 793 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c (renamed from drivers/mtd/nand/gpmi-nand/gpmi-nand.c) | 82 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h (renamed from drivers/mtd/nand/gpmi-nand/gpmi-nand.h) | 131 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h (renamed from drivers/mtd/nand/gpmi-nand/gpmi-regs.h) | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/hisi504_nand.c (renamed from drivers/mtd/nand/hisi504_nand.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/jz4740_nand.c (renamed from drivers/mtd/nand/jz4740_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/jz4780_bch.c (renamed from drivers/mtd/nand/jz4780_bch.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/jz4780_bch.h (renamed from drivers/mtd/nand/jz4780_bch.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/jz4780_nand.c (renamed from drivers/mtd/nand/jz4780_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/lpc32xx_mlc.c (renamed from drivers/mtd/nand/lpc32xx_mlc.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/lpc32xx_slc.c (renamed from drivers/mtd/nand/lpc32xx_slc.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/marvell_nand.c (renamed from drivers/mtd/nand/marvell_nand.c) | 89 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mpc5121_nfc.c (renamed from drivers/mtd/nand/mpc5121_nfc.c) | 9 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mtk_ecc.c (renamed from drivers/mtd/nand/mtk_ecc.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mtk_ecc.h (renamed from drivers/mtd/nand/mtk_ecc.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mtk_nand.c (renamed from drivers/mtd/nand/mtk_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxc_nand.c (renamed from drivers/mtd/nand/mxc_nand.c) | 544 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_amd.c (renamed from drivers/mtd/nand/nand_amd.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_base.c (renamed from drivers/mtd/nand/nand_base.c) | 335 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_bbt.c (renamed from drivers/mtd/nand/nand_bbt.c) | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_bch.c (renamed from drivers/mtd/nand/nand_bch.c) | 12 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_ecc.c (renamed from drivers/mtd/nand/nand_ecc.c) | 22 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_hynix.c (renamed from drivers/mtd/nand/nand_hynix.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_ids.c (renamed from drivers/mtd/nand/nand_ids.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_macronix.c (renamed from drivers/mtd/nand/nand_macronix.c) | 13 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_micron.c (renamed from drivers/mtd/nand/nand_micron.c) | 41 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_samsung.c (renamed from drivers/mtd/nand/nand_samsung.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_timings.c (renamed from drivers/mtd/nand/nand_timings.c) | 12 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_toshiba.c (renamed from drivers/mtd/nand/nand_toshiba.c) | 26 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nandsim.c (renamed from drivers/mtd/nand/nandsim.c) | 15 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/ndfc.c (renamed from drivers/mtd/nand/ndfc.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nuc900_nand.c (renamed from drivers/mtd/nand/nuc900_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/omap2.c (renamed from drivers/mtd/nand/omap2.c) | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/omap_elm.c (renamed from drivers/mtd/nand/omap_elm.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/orion_nand.c (renamed from drivers/mtd/nand/orion_nand.c) | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/oxnas_nand.c (renamed from drivers/mtd/nand/oxnas_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/pasemi_nand.c (renamed from drivers/mtd/nand/pasemi_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/plat_nand.c (renamed from drivers/mtd/nand/plat_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/qcom_nandc.c (renamed from drivers/mtd/nand/qcom_nandc.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/r852.c (renamed from drivers/mtd/nand/r852.c) | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/r852.h (renamed from drivers/mtd/nand/r852.h) | 9 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/s3c2410.c (renamed from drivers/mtd/nand/s3c2410.c) | 27 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/sh_flctl.c (renamed from drivers/mtd/nand/sh_flctl.c) | 10 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/sharpsl.c (renamed from drivers/mtd/nand/sharpsl.c) | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/sm_common.c (renamed from drivers/mtd/nand/sm_common.c) | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/sm_common.h (renamed from drivers/mtd/nand/sm_common.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/socrates_nand.c (renamed from drivers/mtd/nand/socrates_nand.c) | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/sunxi_nand.c (renamed from drivers/mtd/nand/sunxi_nand.c) | 155 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/tango_nand.c (renamed from drivers/mtd/nand/tango_nand.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/tmio_nand.c (renamed from drivers/mtd/nand/tmio_nand.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/txx9ndfmc.c (renamed from drivers/mtd/nand/txx9ndfmc.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/vf610_nfc.c (renamed from drivers/mtd/nand/vf610_nfc.c) | 677 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/xway_nand.c (renamed from drivers/mtd/nand/xway_nand.c) | 0 |
102 files changed, 8740 insertions, 3399 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 605ec8cce67b..88c7d3b4ff8b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,569 +1,6 @@ -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_MARVELL - tristate "NAND controller support on Marvell boards" - depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ - COMPILE_TEST - depends on HAS_IOMEM && HAS_DMA - 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" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index c882d5ef192a..3f0cb87f1a57 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,70 +1,7 @@ # 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_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/ diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c new file mode 100644 index 000000000000..56cde38b92c0 --- /dev/null +++ b/drivers/mtd/nand/bbt.c @@ -0,0 +1,130 @@ +// 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); diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c deleted file mode 100644 index 87bbd177b3e5..000000000000 --- a/drivers/mtd/nand/bf5xx_nand.c +++ /dev/null @@ -1,862 +0,0 @@ -/* 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; - - chip->read_buf = (plat->data_width) ? - bf5xx_nand_read_buf16 : bf5xx_nand_read_buf; - chip->write_buf = (plat->data_width) ? - bf5xx_nand_write_buf16 : bf5xx_nand_write_buf; - - chip->read_byte = bf5xx_nand_read_byte; - - chip->cmd_ctrl = bf5xx_nand_hwcontrol; - chip->dev_ready = bf5xx_nand_devready; - - nand_set_controller_data(chip, mtd); - chip->controller = &info->controller; - - chip->IO_ADDR_R = (void __iomem *) NFC_READ; - chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR; - - chip->chip_delay = 0; - - /* initialise mtd info data struct */ - mtd->dev.parent = &pdev->dev; - - /* initialise the hardware */ - err = bf5xx_nand_hw_init(info); - if (err) - goto out_err; - - /* setup hardware ECC data struct */ - if (hardware_ecc) { -#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC - mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops); -#endif - chip->read_buf = bf5xx_nand_dma_read_buf; - chip->write_buf = bf5xx_nand_dma_write_buf; - chip->ecc.calculate = bf5xx_nand_calculate_ecc; - chip->ecc.correct = bf5xx_nand_correct_data; - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.hwctl = bf5xx_nand_enable_hwecc; - chip->ecc.read_page_raw = bf5xx_nand_read_page_raw; - chip->ecc.write_page_raw = bf5xx_nand_write_page_raw; - } else { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - } - - /* scan hardware nand chip and setup mtd info data struct */ - if (bf5xx_nand_scan(mtd)) { - err = -ENXIO; - goto out_err_nand_scan; - } - -#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC - chip->badblockpos = 63; -#endif - - /* add NAND partition */ - bf5xx_nand_add_partition(info); - - dev_dbg(&pdev->dev, "initialised ok\n"); - return 0; - -out_err_nand_scan: - bf5xx_nand_dma_remove(info); -out_err: - peripheral_free_list(bfin_nfc_pin_req); - - return err; -} - -/* driver device registration */ -static struct platform_driver bf5xx_nand_driver = { - .probe = bf5xx_nand_probe, - .remove = bf5xx_nand_remove, - .driver = { - .name = DRV_NAME, - }, -}; - -module_platform_driver(bf5xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR(DRV_AUTHOR); -MODULE_DESCRIPTION(DRV_DESC); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c new file mode 100644 index 000000000000..d0cd6f8635d7 --- /dev/null +++ b/drivers/mtd/nand/core.c @@ -0,0 +1,244 @@ +// 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: " fmt + +#include <linux/module.h> +#include <linux/mtd/nand.h> + +/** + * nanddev_isbad() - Check if a block is bad + * @nand: NAND device + * @pos: position pointing to the block we want to check + * + * Return: true if the block is bad, false otherwise. + */ +bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_bbt_is_initialized(nand)) { + unsigned int entry; + int status; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + /* Lazy block status retrieval */ + if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) { + if (nand->ops->isbad(nand, pos)) + status = NAND_BBT_BLOCK_FACTORY_BAD; + else + status = NAND_BBT_BLOCK_GOOD; + + nanddev_bbt_set_block_status(nand, entry, status); + } + + if (status == NAND_BBT_BLOCK_WORN || + status == NAND_BBT_BLOCK_FACTORY_BAD) + return true; + + return false; + } + + return nand->ops->isbad(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_isbad); + +/** + * nanddev_markbad() - Mark a block as bad + * @nand: NAND device + * @pos: position of the block to mark bad + * + * Mark a block bad. This function is updating the BBT if available and + * calls the low-level markbad hook (nand->ops->markbad()). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + unsigned int entry; + int ret = 0; + + if (nanddev_isbad(nand, pos)) + return 0; + + ret = nand->ops->markbad(nand, pos); + if (ret) + pr_warn("failed to write BBM to block @%llx (err = %d)\n", + nanddev_pos_to_offs(nand, pos), ret); + + if (!nanddev_bbt_is_initialized(nand)) + goto out; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN); + if (ret) + goto out; + + ret = nanddev_bbt_update(nand); + +out: + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} +EXPORT_SYMBOL_GPL(nanddev_markbad); + +/** + * nanddev_isreserved() - Check whether an eraseblock is reserved or not + * @nand: NAND device + * @pos: NAND position to test + * + * Checks whether the eraseblock pointed by @pos is reserved or not. + * + * Return: true if the eraseblock is reserved, false otherwise. + */ +bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos) +{ + unsigned int entry; + int status; + + if (!nanddev_bbt_is_initialized(nand)) + return false; + + /* Return info from the table */ + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + return status == NAND_BBT_BLOCK_RESERVED; +} +EXPORT_SYMBOL_GPL(nanddev_isreserved); + +/** + * nanddev_erase() - Erase a NAND portion + * @nand: NAND device + * @pos: position of the block to erase + * + * Erases the block if it's not bad. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { + pr_warn("attempt to erase a bad/reserved block @%llx\n", + nanddev_pos_to_offs(nand, pos)); + return -EIO; + } + + return nand->ops->erase(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_erase); + +/** + * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices + * @mtd: MTD device + * @einfo: erase request + * + * This is a simple mtd->_erase() implementation iterating over all blocks + * concerned by @einfo and calling nand->ops->erase() on each of them. + * + * Note that mtd->_erase should not be directly assigned to this helper, + * because there's no locking here. NAND specialized layers should instead + * implement there own wrapper around nanddev_mtd_erase() taking the + * appropriate lock before calling nanddev_mtd_erase(). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_pos pos, last; + int ret; + + nanddev_offs_to_pos(nand, einfo->addr, &pos); + nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last); + while (nanddev_pos_cmp(&pos, &last) <= 0) { + ret = nanddev_erase(nand, &pos); + if (ret) { + einfo->fail_addr = nanddev_pos_to_offs(nand, &pos); + einfo->state = MTD_ERASE_FAILED; + + return ret; + } + + nanddev_pos_next_eraseblock(nand, &pos); + } + + einfo->state = MTD_ERASE_DONE; + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_mtd_erase); + +/** + * nanddev_init() - Initialize a NAND device + * @nand: NAND device + * @ops: NAND device operations + * @owner: NAND device owner + * + * Initializes a NAND device object. Consistency checks are done on @ops and + * @nand->memorg. Also takes care of initializing the BBT. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_memory_organization *memorg = nanddev_get_memorg(nand); + + if (!nand || !ops) + return -EINVAL; + + if (!ops->erase || !ops->markbad || !ops->isbad) + return -EINVAL; + + if (!memorg->bits_per_cell || !memorg->pagesize || + !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun || + !memorg->planes_per_lun || !memorg->luns_per_target || + !memorg->ntargets) + return -EINVAL; + + nand->rowconv.eraseblock_addr_shift = + fls(memorg->pages_per_eraseblock - 1); + nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) + + nand->rowconv.eraseblock_addr_shift; + + nand->ops = ops; + + mtd->type = memorg->bits_per_cell == 1 ? + MTD_NANDFLASH : MTD_MLCNANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock; + mtd->writesize = memorg->pagesize; + mtd->writebufsize = memorg->pagesize; + mtd->oobsize = memorg->oobsize; + mtd->size = nanddev_size(nand); + mtd->owner = owner; + + return nanddev_bbt_init(nand); +} +EXPORT_SYMBOL_GPL(nanddev_init); + +/** + * nanddev_cleanup() - Release resources allocated in nanddev_init() + * @nand: NAND device + * + * Basically undoes what has been done in nanddev_init(). + */ +void nanddev_cleanup(struct nand_device *nand) +{ + if (nanddev_bbt_is_initialized(nand)) + nanddev_bbt_cleanup(nand); +} +EXPORT_SYMBOL_GPL(nanddev_cleanup); + +MODULE_DESCRIPTION("Generic NAND framework"); +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/onenand/Kconfig b/drivers/mtd/nand/onenand/Kconfig new file mode 100644 index 000000000000..9dc15748947b --- /dev/null +++ b/drivers/mtd/nand/onenand/Kconfig @@ -0,0 +1,71 @@ +menuconfig MTD_ONENAND + tristate "OneNAND Device Support" + depends on MTD + depends on HAS_IOMEM + help + This enables support for accessing all type of OneNAND flash + devices. + +if MTD_ONENAND + +config MTD_ONENAND_VERIFY_WRITE + bool "Verify OneNAND page writes" + help + This adds an extra check when data is written to the flash. The + OneNAND flash device internally checks only bits transitioning + from 1 to 0. There is a rare possibility that even though the + device thinks the write was successful, a bit could have been + flipped accidentally due to device wear or something else. + +config MTD_ONENAND_GENERIC + tristate "OneNAND Flash device via platform device driver" + help + Support for OneNAND flash via platform device driver. + +config MTD_ONENAND_OMAP2 + tristate "OneNAND on OMAP2/OMAP3 support" + depends on ARCH_OMAP2 || ARCH_OMAP3 + depends on OF || COMPILE_TEST + help + Support for a OneNAND flash device connected to an OMAP2/OMAP3 SoC + via the GPMC memory controller. + Enable dmaengine and gpiolib for better performance. + +config MTD_ONENAND_SAMSUNG + tristate "OneNAND on Samsung SOC controller support" + depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS4 + help + Support for a OneNAND flash device connected to an Samsung SOC. + S3C64XX uses command mapping method. + S5PC110/S5PC210 use generic OneNAND method. + +config MTD_ONENAND_OTP + bool "OneNAND OTP Support" + help + One Block of the NAND Flash Array memory is reserved as + a One-Time Programmable Block memory area. + Also, 1st Block of NAND Flash Array can be used as OTP. + + The OTP block can be read, programmed and locked using the same + operations as any other NAND Flash Array memory block. + OTP block cannot be erased. + + OTP block is fully-guaranteed to be a valid block. + +config MTD_ONENAND_2X_PROGRAM + bool "OneNAND 2X program support" + help + The 2X Program is an extension of Program Operation. + Since the device is equipped with two DataRAMs, and two-plane NAND + Flash memory array, these two component enables simultaneous program + of 4KiB. Plane1 has only even blocks such as block0, block2, block4 + while Plane2 has only odd blocks such as block1, block3, block5. + So MTD regards it as 4KiB page size and 256KiB block size + + Now the following chips support it. (KFXXX16Q2M) + Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, + Mux: KFM2G16Q2M, KFN4G16Q2M, + + And more recent chips + +endif # MTD_ONENAND diff --git a/drivers/mtd/nand/onenand/Makefile b/drivers/mtd/nand/onenand/Makefile new file mode 100644 index 000000000000..f8b624aca9cc --- /dev/null +++ b/drivers/mtd/nand/onenand/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the OneNAND MTD +# + +# Core functionality. +obj-$(CONFIG_MTD_ONENAND) += onenand.o + +# Board specific. +obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o +obj-$(CONFIG_MTD_ONENAND_OMAP2) += omap2.o +obj-$(CONFIG_MTD_ONENAND_SAMSUNG) += samsung.o + +onenand-objs = onenand_base.o onenand_bbt.o diff --git a/drivers/mtd/nand/onenand/generic.c b/drivers/mtd/nand/onenand/generic.c new file mode 100644 index 000000000000..d5ccaf943b91 --- /dev/null +++ b/drivers/mtd/nand/onenand/generic.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.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. + * + * Overview: + * This is a device driver for the OneNAND flash for generic boards. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <linux/mtd/partitions.h> +#include <linux/io.h> + +/* + * Note: Driver name and platform data format have been updated! + * + * This version of the driver is named "onenand-flash" and takes struct + * onenand_platform_data as platform data. The old ARM-specific version + * with the name "onenand" used to take struct flash_platform_data. + */ +#define DRIVER_NAME "onenand-flash" + +struct onenand_info { + struct mtd_info mtd; + struct onenand_chip onenand; +}; + +static int generic_onenand_probe(struct platform_device *pdev) +{ + struct onenand_info *info; + struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct resource *res = pdev->resource; + unsigned long size = resource_size(res); + int err; + + info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { + err = -EBUSY; + goto out_free_info; + } + + info->onenand.base = ioremap(res->start, size); + if (!info->onenand.base) { + err = -ENOMEM; + goto out_release_mem_region; + } + + info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL; + info->onenand.irq = platform_get_irq(pdev, 0); + + info->mtd.dev.parent = &pdev->dev; + info->mtd.priv = &info->onenand; + + if (onenand_scan(&info->mtd, 1)) { + err = -ENXIO; + goto out_iounmap; + } + + err = mtd_device_parse_register(&info->mtd, NULL, NULL, + pdata ? pdata->parts : NULL, + pdata ? pdata->nr_parts : 0); + + platform_set_drvdata(pdev, info); + + return 0; + +out_iounmap: + iounmap(info->onenand.base); +out_release_mem_region: + release_mem_region(res->start, size); +out_free_info: + kfree(info); + + return err; +} + +static int generic_onenand_remove(struct platform_device *pdev) +{ + struct onenand_info *info = platform_get_drvdata(pdev); + struct resource *res = pdev->resource; + unsigned long size = resource_size(res); + + if (info) { + onenand_release(&info->mtd); + release_mem_region(res->start, size); + iounmap(info->onenand.base); + kfree(info); + } + + return 0; +} + +static struct platform_driver generic_onenand_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = generic_onenand_probe, + .remove = generic_onenand_remove, +}; + +module_platform_driver(generic_onenand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); +MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/mtd/nand/onenand/omap2.c b/drivers/mtd/nand/onenand/omap2.c new file mode 100644 index 000000000000..9c159f0dd9a6 --- /dev/null +++ b/drivers/mtd/nand/onenand/omap2.c @@ -0,0 +1,660 @@ +/* + * OneNAND driver for OMAP2 / OMAP3 + * + * Copyright © 2005-2006 Nokia Corporation + * + * Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and Juha Yrjölä + * IRQ and DMA support written by Timo Teras + * + * 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. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <linux/mtd/partitions.h> +#include <linux/of_device.h> +#include <linux/omap-gpmc.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/gpio/consumer.h> + +#include <asm/mach/flash.h> + +#define DRIVER_NAME "omap2-onenand" + +#define ONENAND_BUFRAM_SIZE (1024 * 5) + +struct omap2_onenand { + struct platform_device *pdev; + int gpmc_cs; + unsigned long phys_base; + struct gpio_desc *int_gpiod; + struct mtd_info mtd; + struct onenand_chip onenand; + struct completion irq_done; + struct completion dma_done; + struct dma_chan *dma_chan; +}; + +static void omap2_onenand_dma_complete_func(void *completion) +{ + complete(completion); +} + +static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id) +{ + struct omap2_onenand *c = dev_id; + + complete(&c->irq_done); + + return IRQ_HANDLED; +} + +static inline unsigned short read_reg(struct omap2_onenand *c, int reg) +{ + return readw(c->onenand.base + reg); +} + +static inline void write_reg(struct omap2_onenand *c, unsigned short value, + int reg) +{ + writew(value, c->onenand.base + reg); +} + +static int omap2_onenand_set_cfg(struct omap2_onenand *c, + bool sr, bool sw, + int latency, int burst_len) +{ + unsigned short reg = ONENAND_SYS_CFG1_RDY | ONENAND_SYS_CFG1_INT; + + reg |= latency << ONENAND_SYS_CFG1_BRL_SHIFT; + + switch (burst_len) { + case 0: /* continuous */ + break; + case 4: + reg |= ONENAND_SYS_CFG1_BL_4; + break; + case 8: + reg |= ONENAND_SYS_CFG1_BL_8; + break; + case 16: + reg |= ONENAND_SYS_CFG1_BL_16; + break; + case 32: + reg |= ONENAND_SYS_CFG1_BL_32; + break; + default: + return -EINVAL; + } + + if (latency > 5) + reg |= ONENAND_SYS_CFG1_HF; + if (latency > 7) + reg |= ONENAND_SYS_CFG1_VHF; + if (sr) + reg |= ONENAND_SYS_CFG1_SYNC_READ; + if (sw) + reg |= ONENAND_SYS_CFG1_SYNC_WRITE; + + write_reg(c, reg, ONENAND_REG_SYS_CFG1); + + return 0; +} + +static int omap2_onenand_get_freq(int ver) +{ + switch ((ver >> 4) & 0xf) { + case 0: + return 40; + case 1: + return 54; + case 2: + return 66; + case 3: + return 83; + case 4: + return 104; + } + + return -EINVAL; +} + +static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr) +{ + printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n", + msg, state, ctrl, intr); +} + +static void wait_warn(char *msg, int state, unsigned int ctrl, + unsigned int intr) +{ + printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x " + "intr 0x%04x\n", msg, state, ctrl, intr); +} + +static int omap2_onenand_wait(struct mtd_info *mtd, int state) +{ + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); + struct onenand_chip *this = mtd->priv; + unsigned int intr = 0; + unsigned int ctrl, ctrl_mask; + unsigned long timeout; + u32 syscfg; + + if (state == FL_RESETING || state == FL_PREPARING_ERASE || + state == FL_VERIFYING_ERASE) { + int i = 21; + unsigned int intr_flags = ONENAND_INT_MASTER; + + switch (state) { + case FL_RESETING: + intr_flags |= ONENAND_INT_RESET; + break; + case FL_PREPARING_ERASE: + intr_flags |= ONENAND_INT_ERASE; + break; + case FL_VERIFYING_ERASE: + i = 101; + break; + } + + while (--i) { + udelay(1); + intr = read_reg(c, ONENAND_REG_INTERRUPT); + if (intr & ONENAND_INT_MASTER) + break; + } + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); + if (ctrl & ONENAND_CTRL_ERROR) { + wait_err("controller error", state, ctrl, intr); + return -EIO; + } + if ((intr & intr_flags) == intr_flags) + return 0; + /* Continue in wait for interrupt branch */ + } + + if (state != FL_READING) { + int result; + + /* Turn interrupts on */ + syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); + if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) { + syscfg |= ONENAND_SYS_CFG1_IOBE; + write_reg(c, syscfg, ONENAND_REG_SYS_CFG1); + /* Add a delay to let GPIO settle */ + syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); + } + + reinit_completion(&c->irq_done); + result = gpiod_get_value(c->int_gpiod); + if (result < 0) { + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); + intr = read_reg(c, ONENAND_REG_INTERRUPT); + wait_err("gpio error", state, ctrl, intr); + return result; + } else if (result == 0) { + int retry_cnt = 0; +retry: + if (!wait_for_completion_io_timeout(&c->irq_done, + msecs_to_jiffies(20))) { + /* Timeout after 20ms */ + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); + if (ctrl & ONENAND_CTRL_ONGO && + !this->ongoing) { + /* + * The operation seems to be still going + * so give it some more time. + */ + retry_cnt += 1; + if (retry_cnt < 3) + goto retry; + intr = read_reg(c, + ONENAND_REG_INTERRUPT); + wait_err("timeout", state, ctrl, intr); + return -EIO; + } + intr = read_reg(c, ONENAND_REG_INTERRUPT); + if ((intr & ONENAND_INT_MASTER) == 0) + wait_warn("timeout", state, ctrl, intr); + } + } + } else { + int retry_cnt = 0; + + /* Turn interrupts off */ + syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); + syscfg &= ~ONENAND_SYS_CFG1_IOBE; + write_reg(c, syscfg, ONENAND_REG_SYS_CFG1); + + timeout = jiffies + msecs_to_jiffies(20); + while (1) { + if (time_before(jiffies, timeout)) { + intr = read_reg(c, ONENAND_REG_INTERRUPT); + if (intr & ONENAND_INT_MASTER) + break; + } else { + /* Timeout after 20ms */ + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); + if (ctrl & ONENAND_CTRL_ONGO) { + /* + * The operation seems to be still going + * so give it some more time. + */ + retry_cnt += 1; + if (retry_cnt < 3) { + timeout = jiffies + + msecs_to_jiffies(20); + continue; + } + } + break; + } + } + } + + intr = read_reg(c, ONENAND_REG_INTERRUPT); + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); + + if (intr & ONENAND_INT_READ) { + int ecc = read_reg(c, ONENAND_REG_ECC_STATUS); + + if (ecc) { + unsigned int addr1, addr8; + + addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1); + addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_ERR "onenand_wait: ECC error = " + "0x%04x, addr1 %#x, addr8 %#x\n", + ecc, addr1, addr8); + mtd->ecc_stats.failed++; + return -EBADMSG; + } else if (ecc & ONENAND_ECC_1BIT_ALL) { + printk(KERN_NOTICE "onenand_wait: correctable " + "ECC error = 0x%04x, addr1 %#x, " + "addr8 %#x\n", ecc, addr1, addr8); + mtd->ecc_stats.corrected++; + } + } + } else if (state == FL_READING) { + wait_err("timeout", state, ctrl, intr); + return -EIO; + } + + if (ctrl & ONENAND_CTRL_ERROR) { + wait_err("controller error", state, ctrl, intr); + if (ctrl & ONENAND_CTRL_LOCK) + printk(KERN_ERR "onenand_wait: " + "Device is write protected!!!\n"); + return -EIO; + } + + ctrl_mask = 0xFE9F; + if (this->ongoing) + ctrl_mask &= ~0x8000; + + if (ctrl & ctrl_mask) + wait_warn("unexpected controller status", state, ctrl, intr); + + return 0; +} + +static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + + if (ONENAND_CURRENT_BUFFERRAM(this)) { + if (area == ONENAND_DATARAM) + return this->writesize; + if (area == ONENAND_SPARERAM) + return mtd->oobsize; + } + + return 0; +} + +static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, + dma_addr_t src, dma_addr_t dst, + size_t count) +{ + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + + tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count, 0); + if (!tx) { + dev_err(&c->pdev->dev, "Failed to prepare DMA memcpy\n"); + return -EIO; + } + + reinit_completion(&c->dma_done); + + tx->callback = omap2_onenand_dma_complete_func; + tx->callback_param = &c->dma_done; + + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { + dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n"); + return -EIO; + } + + dma_async_issue_pending(c->dma_chan); + + if (!wait_for_completion_io_timeout(&c->dma_done, + msecs_to_jiffies(20))) { + dmaengine_terminate_sync(c->dma_chan); + return -ETIMEDOUT; + } + + return 0; +} + +static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); + struct onenand_chip *this = mtd->priv; + dma_addr_t dma_src, dma_dst; + int bram_offset; + void *buf = (void *)buffer; + size_t xtra; + int ret; + + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) + goto out_copy; + + /* panic_write() may be in an interrupt context */ + if (in_interrupt() || oops_in_progress) + goto out_copy; + + if (buf >= high_memory) { + struct page *p1; + + if (((size_t)buf & PAGE_MASK) != + ((size_t)(buf + count - 1) & PAGE_MASK)) + goto out_copy; + p1 = vmalloc_to_page(buf); + if (!p1) + goto out_copy; + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); + } + + xtra = count & 3; + if (xtra) { + count -= xtra; + memcpy(buf + count, this->base + bram_offset + count, xtra); + } + + dma_src = c->phys_base + bram_offset; + dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE); + if (dma_mapping_error(&c->pdev->dev, dma_dst)) { + dev_err(&c->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", + count); + goto out_copy; + } + + ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); + dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + + if (ret) { + dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); + goto out_copy; + } + + return 0; + +out_copy: + memcpy(buf, this->base + bram_offset, count); + return 0; +} + +static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, + int offset, size_t count) +{ + struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); + struct onenand_chip *this = mtd->priv; + dma_addr_t dma_src, dma_dst; + int bram_offset; + void *buf = (void *)buffer; + int ret; + + bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; + if (bram_offset & 3 || (size_t)buf & 3 || count < 384) + goto out_copy; + + /* panic_write() may be in an interrupt context */ + if (in_interrupt() || oops_in_progress) + goto out_copy; + + if (buf >= high_memory) { + struct page *p1; + + if (((size_t)buf & PAGE_MASK) != + ((size_t)(buf + count - 1) & PAGE_MASK)) + goto out_copy; + p1 = vmalloc_to_page(buf); + if (!p1) + goto out_copy; + buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); + } + + dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE); + dma_dst = c->phys_base + bram_offset; + if (dma_mapping_error(&c->pdev->dev, dma_src)) { + dev_err(&c->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", + count); + return -1; + } + + ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); + dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); + + if (ret) { + dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); + goto out_copy; + } + + return 0; + +out_copy: + memcpy(this->base + bram_offset, buf, count); + return 0; +} + +static void omap2_onenand_shutdown(struct platform_device *pdev) +{ + struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); + + /* With certain content in the buffer RAM, the OMAP boot ROM code + * can recognize the flash chip incorrectly. Zero it out before + * soft reset. + */ + memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE); +} + +static int omap2_onenand_probe(struct platform_device *pdev) +{ + u32 val; + dma_cap_mask_t mask; + int freq, latency, r; + struct resource *res; + struct omap2_onenand *c; + struct gpmc_onenand_info info; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "error getting memory resource\n"); + return -EINVAL; + } + + r = of_property_read_u32(np, "reg", &val); + if (r) { + dev_err(dev, "reg not found in DT\n"); + return r; + } + + c = devm_kzalloc(dev, sizeof(struct omap2_onenand), GFP_KERNEL); + if (!c) + return -ENOMEM; + + init_completion(&c->irq_done); + init_completion(&c->dma_done); + c->gpmc_cs = val; + c->phys_base = res->start; + + c->onenand.base = devm_ioremap_resource(dev, res); + if (IS_ERR(c->onenand.base)) + return PTR_ERR(c->onenand.base); + + c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN); + if (IS_ERR(c->int_gpiod)) { + r = PTR_ERR(c->int_gpiod); + /* Just try again if this happens */ + if (r != -EPROBE_DEFER) + dev_err(dev, "error getting gpio: %d\n", r); + return r; + } + + if (c->int_gpiod) { + r = devm_request_irq(dev, gpiod_to_irq(c->int_gpiod), + omap2_onenand_interrupt, + IRQF_TRIGGER_RISING, "onenand", c); + if (r) + return r; + + c->onenand.wait = omap2_onenand_wait; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + c->dma_chan = dma_request_channel(mask, NULL, NULL); + if (c->dma_chan) { + c->onenand.read_bufferram = omap2_onenand_read_bufferram; + c->onenand.write_bufferram = omap2_onenand_write_bufferram; + } + + c->pdev = pdev; + c->mtd.priv = &c->onenand; + c->mtd.dev.parent = dev; + mtd_set_of_node(&c->mtd, dev->of_node); + + dev_info(dev, "initializing on CS%d (0x%08lx), va %p, %s mode\n", + c->gpmc_cs, c->phys_base, c->onenand.base, + c->dma_chan ? "DMA" : "PIO"); + + if ((r = onenand_scan(&c->mtd, 1)) < 0) + goto err_release_dma; + + freq = omap2_onenand_get_freq(c->onenand.version_id); + if (freq > 0) { + switch (freq) { + case 104: + latency = 7; + break; + case 83: + latency = 6; + break; + case 66: + latency = 5; + break; + case 56: + latency = 4; + break; + default: /* 40 MHz or lower */ + latency = 3; + break; + } + + r = gpmc_omap_onenand_set_timings(dev, c->gpmc_cs, + freq, latency, &info); + if (r) + goto err_release_onenand; + + r = omap2_onenand_set_cfg(c, info.sync_read, info.sync_write, + latency, info.burst_len); + if (r) + goto err_release_onenand; + + if (info.sync_read || info.sync_write) + dev_info(dev, "optimized timings for %d MHz\n", freq); + } + + r = mtd_device_register(&c->mtd, NULL, 0); + if (r) + goto err_release_onenand; + + platform_set_drvdata(pdev, c); + + return 0; + +err_release_onenand: + onenand_release(&c->mtd); +err_release_dma: + if (c->dma_chan) + dma_release_channel(c->dma_chan); + + return r; +} + +static int omap2_onenand_remove(struct platform_device *pdev) +{ + struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); + + onenand_release(&c->mtd); + if (c->dma_chan) + dma_release_channel(c->dma_chan); + omap2_onenand_shutdown(pdev); + + return 0; +} + +static const struct of_device_id omap2_onenand_id_table[] = { + { .compatible = "ti,omap2-onenand", }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap2_onenand_id_table); + +static struct platform_driver omap2_onenand_driver = { + .probe = omap2_onenand_probe, + .remove = omap2_onenand_remove, + .shutdown = omap2_onenand_shutdown, + .driver = { + .name = DRIVER_NAME, + .of_match_table = omap2_onenand_id_table, + }, +}; + +module_platform_driver(omap2_onenand_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>"); +MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3"); diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c new file mode 100644 index 000000000000..b7105192cb12 --- /dev/null +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -0,0 +1,4014 @@ +/* + * Copyright © 2005-2009 Samsung Electronics + * Copyright © 2007 Nokia Corporation + * + * Kyungmin Park <kyungmin.park@samsung.com> + * + * Credits: + * Adrian Hunter <ext-adrian.hunter@nokia.com>: + * auto-placement support, read-while load support, various fixes + * + * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com> + * Flex-OneNAND support + * Amul Kumar Saha <amul.saha at samsung.com> + * OTP support + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> + +/* + * Multiblock erase if number of blocks to erase is 2 or more. + * Maximum number of blocks for simultaneous erase is 64. + */ +#define MB_ERASE_MIN_BLK_COUNT 2 +#define MB_ERASE_MAX_BLK_COUNT 64 + +/* Default Flex-OneNAND boundary and lock respectively */ +static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; + +module_param_array(flex_bdry, int, NULL, 0400); +MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND" + "Syntax:flex_bdry=DIE_BDRY,LOCK,..." + "DIE_BDRY: SLC boundary of the die" + "LOCK: Locking information for SLC boundary" + " : 0->Set boundary in unlocked status" + " : 1->Set boundary in locked status"); + +/* Default OneNAND/Flex-OneNAND OTP options*/ +static int otp; + +module_param(otp, int, 0400); +MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP" + "Syntax : otp=LOCK_TYPE" + "LOCK_TYPE : Keys issued, for specific OTP Lock type" + " : 0 -> Default (No Blocks Locked)" + " : 1 -> OTP Block lock" + " : 2 -> 1st Block lock" + " : 3 -> BOTH OTP Block and 1st Block lock"); + +/* + * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page + * For now, we expose only 64 out of 80 ecc bytes + */ +static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = 10; + + return 0; +} + +static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 16) + 2; + oobregion->length = 4; + + return 0; +} + +static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = { + .ecc = flexonenand_ooblayout_ecc, + .free = flexonenand_ooblayout_free, +}; + +/* + * onenand_oob_128 - oob info for OneNAND with 4KB page + * + * Based on specification: + * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010 + * + */ +static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 16) + 7; + oobregion->length = 9; + + return 0; +} + +static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section >= 8) + return -ERANGE; + + /* + * free bytes are using the spare area fields marked as + * "Managed by internal ECC logic for Logical Sector Number area" + */ + oobregion->offset = (section * 16) + 2; + oobregion->length = 3; + + return 0; +} + +static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = { + .ecc = onenand_ooblayout_128_ecc, + .free = onenand_ooblayout_128_free, +}; + +/** + * onenand_oob_32_64 - oob info for large (2KB) page + */ +static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 3) + return -ERANGE; + + oobregion->offset = (section * 16) + 8; + oobregion->length = 5; + + return 0; +} + +static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + int sections = (mtd->oobsize / 32) * 2; + + if (section >= sections) + return -ERANGE; + + if (section & 1) { + oobregion->offset = ((section - 1) * 16) + 14; + oobregion->length = 2; + } else { + oobregion->offset = (section * 16) + 2; + oobregion->length = 3; + } + + return 0; +} + +static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = { + .ecc = onenand_ooblayout_32_64_ecc, + .free = onenand_ooblayout_32_64_free, +}; + +static const unsigned char ffchars[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ +}; + +/** + * onenand_readw - [OneNAND Interface] Read OneNAND register + * @param addr address to read + * + * Read OneNAND register + */ +static unsigned short onenand_readw(void __iomem *addr) +{ + return readw(addr); +} + +/** + * onenand_writew - [OneNAND Interface] Write OneNAND register with value + * @param value value to write + * @param addr address to write + * + * Write OneNAND register with value + */ +static void onenand_writew(unsigned short value, void __iomem *addr) +{ + writew(value, addr); +} + +/** + * onenand_block_address - [DEFAULT] Get block address + * @param this onenand chip data structure + * @param block the block + * @return translated block address if DDP, otherwise same + * + * Setup Start Address 1 Register (F100h) + */ +static int onenand_block_address(struct onenand_chip *this, int block) +{ + /* Device Flash Core select, NAND Flash Block Address */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); + + return block; +} + +/** + * onenand_bufferram_address - [DEFAULT] Get bufferram address + * @param this onenand chip data structure + * @param block the block + * @return set DBS value if DDP, otherwise 0 + * + * Setup Start Address 2 Register (F101h) for DDP + */ +static int onenand_bufferram_address(struct onenand_chip *this, int block) +{ + /* Device BufferRAM Select */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1; + + return ONENAND_DDP_CHIP0; +} + +/** + * onenand_page_address - [DEFAULT] Get page address + * @param page the page address + * @param sector the sector address + * @return combined page and sector address + * + * Setup Start Address 8 Register (F107h) + */ +static int onenand_page_address(int page, int sector) +{ + /* Flash Page Address, Flash Sector Address */ + int fpa, fsa; + + fpa = page & ONENAND_FPA_MASK; + fsa = sector & ONENAND_FSA_MASK; + + return ((fpa << ONENAND_FPA_SHIFT) | fsa); +} + +/** + * onenand_buffer_address - [DEFAULT] Get buffer address + * @param dataram1 DataRAM index + * @param sectors the sector address + * @param count the number of sectors + * @return the start buffer value + * + * Setup Start Buffer Register (F200h) + */ +static int onenand_buffer_address(int dataram1, int sectors, int count) +{ + int bsa, bsc; + + /* BufferRAM Sector Address */ + bsa = sectors & ONENAND_BSA_MASK; + + if (dataram1) + bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ + else + bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ + + /* BufferRAM Sector Count */ + bsc = count & ONENAND_BSC_MASK; + + return ((bsa << ONENAND_BSA_SHIFT) | bsc); +} + +/** + * flexonenand_block- For given address return block number + * @param this - OneNAND device structure + * @param addr - Address for which block number is needed + */ +static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr) +{ + unsigned boundary, blk, die = 0; + + if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { + die = 1; + addr -= this->diesize[0]; + } + + boundary = this->boundary[die]; + + blk = addr >> (this->erase_shift - 1); + if (blk > boundary) + blk = (blk + boundary + 1) >> 1; + + blk += die ? this->density_mask : 0; + return blk; +} + +inline unsigned onenand_block(struct onenand_chip *this, loff_t addr) +{ + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + return flexonenand_block(this, addr); +} + +/** + * flexonenand_addr - Return address of the block + * @this: OneNAND device structure + * @block: Block number on Flex-OneNAND + * + * Return address of the block + */ +static loff_t flexonenand_addr(struct onenand_chip *this, int block) +{ + loff_t ofs = 0; + int die = 0, boundary; + + if (ONENAND_IS_DDP(this) && block >= this->density_mask) { + block -= this->density_mask; + die = 1; + ofs = this->diesize[0]; + } + + boundary = this->boundary[die]; + ofs += (loff_t)block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1); + return ofs; +} + +loff_t onenand_addr(struct onenand_chip *this, int block) +{ + if (!FLEXONENAND(this)) + return (loff_t)block << this->erase_shift; + return flexonenand_addr(this, block); +} +EXPORT_SYMBOL(onenand_addr); + +/** + * onenand_get_density - [DEFAULT] Get OneNAND density + * @param dev_id OneNAND device ID + * + * Get OneNAND density from device ID + */ +static inline int onenand_get_density(int dev_id) +{ + int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; + return (density & ONENAND_DEVICE_DENSITY_MASK); +} + +/** + * flexonenand_region - [Flex-OneNAND] Return erase region of addr + * @param mtd MTD device structure + * @param addr address whose erase region needs to be identified + */ +int flexonenand_region(struct mtd_info *mtd, loff_t addr) +{ + int i; + + for (i = 0; i < mtd->numeraseregions; i++) + if (addr < mtd->eraseregions[i].offset) + break; + return i - 1; +} +EXPORT_SYMBOL(flexonenand_region); + +/** + * onenand_command - [DEFAULT] Send command to OneNAND device + * @param mtd MTD device structure + * @param cmd the command to be sent + * @param addr offset to read from or write to + * @param len number of bytes to read or write + * + * Send command to OneNAND device. This function is used for middle/large page + * devices (1KB/2KB Bytes per page) + */ +static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) +{ + struct onenand_chip *this = mtd->priv; + int value, block, page; + + /* Address translation */ + switch (cmd) { + case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_LOCK: + case ONENAND_CMD_LOCK_TIGHT: + case ONENAND_CMD_UNLOCK_ALL: + block = -1; + page = -1; + break; + + case FLEXONENAND_CMD_PI_ACCESS: + /* addr contains die index */ + block = addr * this->density_mask; + page = -1; + break; + + case ONENAND_CMD_ERASE: + case ONENAND_CMD_MULTIBLOCK_ERASE: + case ONENAND_CMD_ERASE_VERIFY: + case ONENAND_CMD_BUFFERRAM: + case ONENAND_CMD_OTP_ACCESS: + block = onenand_block(this, addr); + page = -1; + break; + + case FLEXONENAND_CMD_READ_PI: + cmd = ONENAND_CMD_READ; + block = addr * this->density_mask; + page = 0; + break; + + default: + block = onenand_block(this, addr); + if (FLEXONENAND(this)) + page = (int) (addr - onenand_addr(this, block))>>\ + this->page_shift; + else + page = (int) (addr >> this->page_shift); + if (ONENAND_IS_2PLANE(this)) { + /* Make the even block number */ + block &= ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page >>= 1; + } + page &= this->page_mask; + break; + } + + /* NOTE: The setting order of the registers is very important! */ + if (cmd == ONENAND_CMD_BUFFERRAM) { + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + + if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) + /* It is always BufferRAM0 */ + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this); + + return 0; + } + + if (block != -1) { + /* Write 'DFS, FBA' of Flash */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + } + + if (page != -1) { + /* Now we use page size operation */ + int sectors = 0, count = 0; + int dataram; + + switch (cmd) { + case FLEXONENAND_CMD_RECOVER_LSB: + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + if (ONENAND_IS_4KB_PAGE(this)) + /* It is always BufferRAM0 */ + dataram = ONENAND_SET_BUFFERRAM0(this); + else + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); + break; + + default: + if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) + cmd = ONENAND_CMD_2X_PROG; + dataram = ONENAND_CURRENT_BUFFERRAM(this); + break; + } + + /* Write 'FPA, FSA' of Flash */ + value = onenand_page_address(page, sectors); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8); + + /* Write 'BSA, BSC' of DataRAM */ + value = onenand_buffer_address(dataram, sectors, count); + this->write_word(value, this->base + ONENAND_REG_START_BUFFER); + } + + /* Interrupt clear */ + this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); + + /* Write command */ + this->write_word(cmd, this->base + ONENAND_REG_COMMAND); + + return 0; +} + +/** + * onenand_read_ecc - return ecc status + * @param this onenand chip structure + */ +static inline int onenand_read_ecc(struct onenand_chip *this) +{ + int ecc, i, result = 0; + + if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this)) + return this->read_word(this->base + ONENAND_REG_ECC_STATUS); + + for (i = 0; i < 4; i++) { + ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2); + if (likely(!ecc)) + continue; + if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) + return ONENAND_ECC_2BIT_ALL; + else + result = ONENAND_ECC_1BIT_ALL; + } + + return result; +} + +/** + * onenand_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. This applies to all OneNAND command + * Read can take up to 30us, erase up to 2ms and program up to 350us + * according to general OneNAND specs + */ +static int onenand_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip * this = mtd->priv; + unsigned long timeout; + unsigned int flags = ONENAND_INT_MASTER; + unsigned int interrupt = 0; + unsigned int ctrl; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + + if (interrupt & flags) + break; + + if (state != FL_READING && state != FL_PREPARING_ERASE) + cond_resched(); + } + /* To get correct interrupt status in timeout case */ + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + + /* + * In the Spec. it checks the controller status first + * However if you get the correct information in case of + * power off recovery (POR) test, it should read ECC status first + */ + if (interrupt & ONENAND_INT_READ) { + int ecc = onenand_read_ecc(this); + if (ecc) { + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_ERR "%s: ECC error = 0x%04x\n", + __func__, ecc); + mtd->ecc_stats.failed++; + return -EBADMSG; + } else if (ecc & ONENAND_ECC_1BIT_ALL) { + printk(KERN_DEBUG "%s: correctable ECC error = 0x%04x\n", + __func__, ecc); + mtd->ecc_stats.corrected++; + } + } + } else if (state == FL_READING) { + printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n", + __func__, ctrl, interrupt); + return -EIO; + } + + if (state == FL_PREPARING_ERASE && !(interrupt & ONENAND_INT_ERASE)) { + printk(KERN_ERR "%s: mb erase timeout! ctrl=0x%04x intr=0x%04x\n", + __func__, ctrl, interrupt); + return -EIO; + } + + if (!(interrupt & ONENAND_INT_MASTER)) { + printk(KERN_ERR "%s: timeout! ctrl=0x%04x intr=0x%04x\n", + __func__, ctrl, interrupt); + return -EIO; + } + + /* If there's controller error, it's a real error */ + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_ERR "%s: controller error = 0x%04x\n", + __func__, ctrl); + if (ctrl & ONENAND_CTRL_LOCK) + printk(KERN_ERR "%s: it's locked error.\n", __func__); + return -EIO; + } + + return 0; +} + +/* + * onenand_interrupt - [DEFAULT] onenand interrupt handler + * @param irq onenand interrupt number + * @param dev_id interrupt data + * + * complete the work + */ +static irqreturn_t onenand_interrupt(int irq, void *data) +{ + struct onenand_chip *this = data; + + /* To handle shared interrupt */ + if (!this->complete.done) + complete(&this->complete); + + return IRQ_HANDLED; +} + +/* + * onenand_interrupt_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. + */ +static int onenand_interrupt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + + wait_for_completion(&this->complete); + + return onenand_wait(mtd, state); +} + +/* + * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Try interrupt based wait (It is used one-time) + */ +static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned long remain, timeout; + + /* We use interrupt wait first */ + this->wait = onenand_interrupt_wait; + + timeout = msecs_to_jiffies(100); + remain = wait_for_completion_timeout(&this->complete, timeout); + if (!remain) { + printk(KERN_INFO "OneNAND: There's no interrupt. " + "We use the normal wait\n"); + + /* Release the irq */ + free_irq(this->irq, this); + + this->wait = onenand_wait; + } + + return onenand_wait(mtd, state); +} + +/* + * onenand_setup_wait - [OneNAND Interface] setup onenand wait method + * @param mtd MTD device structure + * + * There's two method to wait onenand work + * 1. polling - read interrupt status register + * 2. interrupt - use the kernel interrupt method + */ +static void onenand_setup_wait(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int syscfg; + + init_completion(&this->complete); + + if (this->irq <= 0) { + this->wait = onenand_wait; + return; + } + + if (request_irq(this->irq, &onenand_interrupt, + IRQF_SHARED, "onenand", this)) { + /* If we can't get irq, use the normal wait */ + this->wait = onenand_wait; + return; + } + + /* Enable interrupt */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + syscfg |= ONENAND_SYS_CFG1_IOBE; + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + + this->wait = onenand_try_interrupt_wait; +} + +/** + * onenand_bufferram_offset - [DEFAULT] BufferRAM offset + * @param mtd MTD data structure + * @param area BufferRAM area + * @return offset given area + * + * Return BufferRAM offset given area + */ +static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + + if (ONENAND_CURRENT_BUFFERRAM(this)) { + /* Note: the 'this->writesize' is a real page size */ + if (area == ONENAND_DATARAM) + return this->writesize; + if (area == ONENAND_SPARERAM) + return mtd->oobsize; + } + + return 0; +} + +/** + * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Read the BufferRAM area + */ +static int onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + + bufferram += onenand_bufferram_offset(mtd, area); + + if (ONENAND_CHECK_BYTE_ACCESS(count)) { + unsigned short word; + + /* Align with word(16-bit) size */ + count--; + + /* Read word and save byte */ + word = this->read_word(bufferram + offset + count); + buffer[count] = (word & 0xff); + } + + memcpy(buffer, bufferram + offset, count); + + return 0; +} + +/** + * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Read the BufferRAM area with Sync. Burst Mode + */ +static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + + bufferram += onenand_bufferram_offset(mtd, area); + + this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); + + if (ONENAND_CHECK_BYTE_ACCESS(count)) { + unsigned short word; + + /* Align with word(16-bit) size */ + count--; + + /* Read word and save byte */ + word = this->read_word(bufferram + offset + count); + buffer[count] = (word & 0xff); + } + + memcpy(buffer, bufferram + offset, count); + + this->mmcontrol(mtd, 0); + + return 0; +} + +/** + * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Write the BufferRAM area + */ +static int onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, int offset, size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + + bufferram += onenand_bufferram_offset(mtd, area); + + if (ONENAND_CHECK_BYTE_ACCESS(count)) { + unsigned short word; + int byte_offset; + + /* Align with word(16-bit) size */ + count--; + + /* Calculate byte access offset */ + byte_offset = offset + count; + + /* Read word and save byte */ + word = this->read_word(bufferram + byte_offset); + word = (word & ~0xff) | buffer[count]; + this->write_word(word, bufferram + byte_offset); + } + + memcpy(bufferram + offset, buffer, count); + + return 0; +} + +/** + * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode + * @param mtd MTD data structure + * @param addr address to check + * @return blockpage address + * + * Get blockpage address at 2x program mode + */ +static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int blockpage, block, page; + + /* Calculate the even block number */ + block = (int) (addr >> this->erase_shift) & ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; + blockpage = (block << 7) | page; + + return blockpage; +} + +/** + * onenand_check_bufferram - [GENERIC] Check BufferRAM information + * @param mtd MTD data structure + * @param addr address to check + * @return 1 if there are valid data, otherwise 0 + * + * Check bufferram if there is data we required + */ +static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int blockpage, found = 0; + unsigned int i; + + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); + + /* Is there valid data? */ + i = ONENAND_CURRENT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + found = 1; + else { + /* Check another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + ONENAND_SET_NEXT_BUFFERRAM(this); + found = 1; + } + } + + if (found && ONENAND_IS_DDP(this)) { + /* Select DataRAM for DDP */ + int block = onenand_block(this, addr); + int value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + } + + return found; +} + +/** + * onenand_update_bufferram - [GENERIC] Update BufferRAM information + * @param mtd MTD data structure + * @param addr address to update + * @param valid valid flag + * + * Update BufferRAM information + */ +static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, + int valid) +{ + struct onenand_chip *this = mtd->priv; + int blockpage; + unsigned int i; + + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); + + /* Invalidate another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + this->bufferram[i].blockpage = -1; + + /* Update BufferRAM */ + i = ONENAND_CURRENT_BUFFERRAM(this); + if (valid) + this->bufferram[i].blockpage = blockpage; + else + this->bufferram[i].blockpage = -1; +} + +/** + * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information + * @param mtd MTD data structure + * @param addr start address to invalidate + * @param len length to invalidate + * + * Invalidate BufferRAM information + */ +static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, + unsigned int len) +{ + struct onenand_chip *this = mtd->priv; + int i; + loff_t end_addr = addr + len; + + /* Invalidate BufferRAM */ + for (i = 0; i < MAX_BUFFERRAM; i++) { + loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; + if (buf_addr >= addr && buf_addr < end_addr) + this->bufferram[i].blockpage = -1; + } +} + +/** + * onenand_get_device - [GENERIC] Get chip for selected access + * @param mtd MTD device structure + * @param new_state the state which is requested + * + * Get the device and lock it for exclusive access + */ +static int onenand_get_device(struct mtd_info *mtd, int new_state) +{ + struct onenand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); + + /* + * Grab the lock and see if the device is available + */ + while (1) { + spin_lock(&this->chip_lock); + if (this->state == FL_READY) { + this->state = new_state; + spin_unlock(&this->chip_lock); + if (new_state != FL_PM_SUSPENDED && this->enable) + this->enable(mtd); + break; + } + if (new_state == FL_PM_SUSPENDED) { + spin_unlock(&this->chip_lock); + return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&this->wq, &wait); + spin_unlock(&this->chip_lock); + schedule(); + remove_wait_queue(&this->wq, &wait); + } + + return 0; +} + +/** + * onenand_release_device - [GENERIC] release chip + * @param mtd MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +static void onenand_release_device(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + if (this->state != FL_PM_SUSPENDED && this->disable) + this->disable(mtd); + /* Release the chip */ + spin_lock(&this->chip_lock); + this->state = FL_READY; + wake_up(&this->wq); + spin_unlock(&this->chip_lock); +} + +/** + * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer + * @param mtd MTD device structure + * @param buf destination address + * @param column oob offset to read from + * @param thislen oob length to read + */ +static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, + int thislen) +{ + struct onenand_chip *this = mtd->priv; + int ret; + + this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0, + mtd->oobsize); + ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf, + column, thislen); + if (ret) + return ret; + + return 0; +} + +/** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param mtd MTD device structure + * @param addr address to recover + * @param status return value from onenand_wait / onenand_bbt_wait + * + * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has + * lower page address and MSB page has higher page address in paired pages. + * If power off occurs during MSB page program, the paired LSB page data can + * become corrupt. LSB page recovery read is a way to read LSB page though page + * data are corrupted. When uncorrectable error occurs as a result of LSB page + * read after power up, issue LSB page recovery read. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) +{ + struct onenand_chip *this = mtd->priv; + int i; + + /* Recovery is only for Flex-OneNAND */ + if (!FLEXONENAND(this)) + return status; + + /* check if we failed due to uncorrectable error */ + if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR) + return status; + + /* check if address lies in MLC region */ + i = flexonenand_region(mtd, addr); + if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) + return status; + + /* We are attempting to reread, so decrement stats.failed + * which was incremented by onenand_wait due to read failure + */ + printk(KERN_INFO "%s: Attempting to recover from uncorrectable read\n", + __func__); + mtd->ecc_stats.failed--; + + /* Issue the LSB page recovery command */ + this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); + return this->wait(mtd, FL_READING); +} + +/** + * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops: oob operation description structure + * + * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram. + * So, read-while-load is not present. + */ +static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + u_char *buf = ops->datbuf; + u_char *oobbuf = ops->oobbuf; + int read = 0, column, thislen; + int oobread = 0, oobcolumn, thisooblen, oobsize; + int ret = 0; + int writesize = this->writesize; + + pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, + (int)len); + + oobsize = mtd_oobavail(mtd, ops); + oobcolumn = from & (mtd->oobsize - 1); + + /* Do not allow reads past end of device */ + if (from + len > mtd->size) { + printk(KERN_ERR "%s: Attempt read beyond end of device\n", + __func__); + ops->retlen = 0; + ops->oobretlen = 0; + return -EINVAL; + } + + stats = mtd->ecc_stats; + + while (read < len) { + cond_resched(); + + thislen = min_t(int, writesize, len - read); + + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; + + if (!onenand_check_bufferram(mtd, from)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + onenand_update_bufferram(mtd, from, !ret); + if (mtd_is_eccerr(ret)) + ret = 0; + if (ret) + break; + } + + this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); + if (oobbuf) { + thisooblen = oobsize - oobcolumn; + thisooblen = min_t(int, thisooblen, ooblen - oobread); + + if (ops->mode == MTD_OPS_AUTO_OOB) + onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); + oobread += thisooblen; + oobbuf += thisooblen; + oobcolumn = 0; + } + + read += thislen; + if (read == len) + break; + + from += thislen; + buf += thislen; + } + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + ops->retlen = read; + ops->oobretlen = oobread; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; +} + +/** + * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops: oob operation description structure + * + * OneNAND read main and/or out-of-band data + */ +static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + u_char *buf = ops->datbuf; + u_char *oobbuf = ops->oobbuf; + int read = 0, column, thislen; + int oobread = 0, oobcolumn, thisooblen, oobsize; + int ret = 0, boundary = 0; + int writesize = this->writesize; + + pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, + (int)len); + + oobsize = mtd_oobavail(mtd, ops); + oobcolumn = from & (mtd->oobsize - 1); + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + printk(KERN_ERR "%s: Attempt read beyond end of device\n", + __func__); + ops->retlen = 0; + ops->oobretlen = 0; + return -EINVAL; + } + + stats = mtd->ecc_stats; + + /* Read-while-load method */ + + /* Do first load to bufferRAM */ + if (read < len) { + if (!onenand_check_bufferram(mtd, from)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); + if (mtd_is_eccerr(ret)) + ret = 0; + } + } + + thislen = min_t(int, writesize, len - read); + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; + + while (!ret) { + /* If there is more to load then start next load */ + from += thislen; + if (read + thislen < len) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + /* + * Chip boundary handling in DDP + * Now we issued chip 1 read and pointed chip 1 + * bufferram so we have to point chip 0 bufferram. + */ + if (ONENAND_IS_DDP(this) && + unlikely(from == (this->chipsize >> 1))) { + this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); + boundary = 1; + } else + boundary = 0; + ONENAND_SET_PREV_BUFFERRAM(this); + } + /* While load is going, read from last bufferRAM */ + this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); + + /* Read oob area if needed */ + if (oobbuf) { + thisooblen = oobsize - oobcolumn; + thisooblen = min_t(int, thisooblen, ooblen - oobread); + + if (ops->mode == MTD_OPS_AUTO_OOB) + onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); + oobread += thisooblen; + oobbuf += thisooblen; + oobcolumn = 0; + } + + /* See if we are done */ + read += thislen; + if (read == len) + break; + /* Set up for next read from bufferRAM */ + if (unlikely(boundary)) + this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); + ONENAND_SET_NEXT_BUFFERRAM(this); + buf += thislen; + thislen = min_t(int, writesize, len - read); + column = 0; + cond_resched(); + /* Now wait for load */ + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); + if (mtd_is_eccerr(ret)) + ret = 0; + } + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + ops->retlen = read; + ops->oobretlen = oobread; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; +} + +/** + * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops: oob operation description structure + * + * OneNAND read out-of-band data from the spare area + */ +static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; + int read = 0, thislen, column, oobsize; + size_t len = ops->ooblen; + unsigned int mode = ops->mode; + u_char *buf = ops->oobbuf; + int ret = 0, readcmd; + + from += ops->ooboffs; + + pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, + (int)len); + + /* Initialize return length value */ + ops->oobretlen = 0; + + if (mode == MTD_OPS_AUTO_OOB) + oobsize = mtd->oobavail; + else + oobsize = mtd->oobsize; + + column = from & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "%s: Attempted to start read outside oob\n", + __func__); + return -EINVAL; + } + + stats = mtd->ecc_stats; + + readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + while (read < len) { + cond_resched(); + + thislen = oobsize - column; + thislen = min_t(int, thislen, len); + + this->command(mtd, readcmd, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + + if (ret && !mtd_is_eccerr(ret)) { + printk(KERN_ERR "%s: read failed = 0x%x\n", + __func__, ret); + break; + } + + if (mode == MTD_OPS_AUTO_OOB) + onenand_transfer_auto_oob(mtd, buf, column, thislen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + + read += thislen; + + if (read == len) + break; + + buf += thislen; + + /* Read more? */ + if (read < len) { + /* Page size */ + from += mtd->writesize; + column = 0; + } + } + + ops->oobretlen = read; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return 0; +} + +/** + * onenand_read_oob - [MTD Interface] Read main and/or out-of-band + * @param mtd: MTD device structure + * @param from: offset to read from + * @param ops: oob operation description structure + + * Read main and/or out-of-band + */ +static int onenand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int ret; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + break; + case MTD_OPS_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } + + onenand_get_device(mtd, FL_READING); + if (ops->datbuf) + ret = ONENAND_IS_4KB_PAGE(this) ? + onenand_mlc_read_ops_nolock(mtd, from, ops) : + onenand_read_ops_nolock(mtd, from, ops); + else + ret = onenand_read_oob_nolock(mtd, from, ops); + onenand_release_device(mtd); + + return ret; +} + +/** + * onenand_bbt_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. + */ +static int onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned long timeout; + unsigned int interrupt, ctrl, ecc, addr1, addr8; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & ONENAND_INT_MASTER) + break; + } + /* To get correct interrupt status in timeout case */ + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + addr1 = this->read_word(this->base + ONENAND_REG_START_ADDRESS1); + addr8 = this->read_word(this->base + ONENAND_REG_START_ADDRESS8); + + if (interrupt & ONENAND_INT_READ) { + ecc = onenand_read_ecc(this); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_DEBUG "%s: ecc 0x%04x ctrl 0x%04x " + "intr 0x%04x addr1 %#x addr8 %#x\n", + __func__, ecc, ctrl, interrupt, addr1, addr8); + return ONENAND_BBT_READ_ECC_ERROR; + } + } else { + printk(KERN_ERR "%s: read timeout! ctrl 0x%04x " + "intr 0x%04x addr1 %#x addr8 %#x\n", + __func__, ctrl, interrupt, addr1, addr8); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + /* Initial bad block case: 0x2400 or 0x0400 */ + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "%s: ctrl 0x%04x intr 0x%04x addr1 %#x " + "addr8 %#x\n", __func__, ctrl, interrupt, addr1, addr8); + return ONENAND_BBT_READ_ERROR; + } + + return 0; +} + +/** + * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan + * @param mtd MTD device structure + * @param from offset to read from + * @param ops oob operation description structure + * + * OneNAND read out-of-band data from the spare area for bbt scan + */ +int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int read = 0, thislen, column; + int ret = 0, readcmd; + size_t len = ops->ooblen; + u_char *buf = ops->oobbuf; + + pr_debug("%s: from = 0x%08x, len = %zi\n", __func__, (unsigned int)from, + len); + + /* Initialize return value */ + ops->oobretlen = 0; + + /* Do not allow reads past end of device */ + if (unlikely((from + len) > mtd->size)) { + printk(KERN_ERR "%s: Attempt read beyond end of device\n", + __func__); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_READING); + + column = from & (mtd->oobsize - 1); + + readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + while (read < len) { + cond_resched(); + + thislen = mtd->oobsize - column; + thislen = min_t(int, thislen, len); + + this->command(mtd, readcmd, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = this->bbt_wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + + if (ret) + break; + + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + read += thislen; + if (read == len) + break; + + buf += thislen; + + /* Read more? */ + if (read < len) { + /* Update Page size */ + from += this->writesize; + column = 0; + } + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + ops->oobretlen = read; + return ret; +} + +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE +/** + * onenand_verify_oob - [GENERIC] verify the oob contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param to offset to read from + */ +static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) +{ + struct onenand_chip *this = mtd->priv; + u_char *oob_buf = this->oob_buf; + int status, i, readcmd; + + readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + this->command(mtd, readcmd, to, mtd->oobsize); + onenand_update_bufferram(mtd, to, 0); + status = this->wait(mtd, FL_READING); + if (status) + return status; + + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); + for (i = 0; i < mtd->oobsize; i++) + if (buf[i] != 0xFF && buf[i] != oob_buf[i]) + return -EBADMSG; + + return 0; +} + +/** + * onenand_verify - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param addr offset to read from + * @param len number of bytes to read and compare + */ +static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) +{ + struct onenand_chip *this = mtd->priv; + int ret = 0; + int thislen, column; + + column = addr & (this->writesize - 1); + + while (len != 0) { + thislen = min_t(int, this->writesize - column, len); + + this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); + + onenand_update_bufferram(mtd, addr, 0); + + ret = this->wait(mtd, FL_READING); + if (ret) + return ret; + + onenand_update_bufferram(mtd, addr, 1); + + this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize); + + if (memcmp(buf, this->verify_buf + column, thislen)) + return -EBADMSG; + + len -= thislen; + buf += thislen; + addr += thislen; + column = 0; + } + + return 0; +} +#else +#define onenand_verify(...) (0) +#define onenand_verify_oob(...) (0) +#endif + +#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) + +static void onenand_panic_wait(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int interrupt; + int i; + + for (i = 0; i < 2000; i++) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & ONENAND_INT_MASTER) + break; + udelay(10); + } +} + +/** + * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * Write with ECC + */ +static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + int column, subpage; + int written = 0; + + if (this->state == FL_PM_SUSPENDED) + return -EBUSY; + + /* Wait for any existing operation to clear */ + onenand_panic_wait(mtd); + + pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, + (int)len); + + /* Reject writes, which are not page aligned */ + if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { + printk(KERN_ERR "%s: Attempt to write not page aligned data\n", + __func__); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, mtd->writesize - column, len - written); + u_char *wbuf = (u_char *) buf; + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); + + /* Partial page write */ + subpage = thislen < mtd->writesize; + if (subpage) { + memset(this->page_buf, 0xff, mtd->writesize); + memcpy(this->page_buf + column, buf, thislen); + wbuf = this->page_buf; + } + + this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); + this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); + + this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); + + onenand_panic_wait(mtd); + + /* In partial page write we don't update bufferram */ + onenand_update_bufferram(mtd, to, !subpage); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, !subpage); + } + + written += thislen; + + if (written == len) + break; + + column = 0; + to += thislen; + buf += thislen; + } + + *retlen = written; + return 0; +} + +/** + * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer + * @param mtd MTD device structure + * @param oob_buf oob buffer + * @param buf source address + * @param column oob offset to write to + * @param thislen oob length to write + */ +static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, + const u_char *buf, int column, int thislen) +{ + return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen); +} + +/** + * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band + * @param mtd MTD device structure + * @param to offset to write to + * @param ops oob operation description structure + * + * Write main and/or oob with ECC + */ +static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int written = 0, column, thislen = 0, subpage = 0; + int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; + int oobwritten = 0, oobcolumn, thisooblen, oobsize; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + const u_char *buf = ops->datbuf; + const u_char *oob = ops->oobbuf; + u_char *oobbuf; + int ret = 0, cmd; + + pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, + (int)len); + + /* Initialize retlen, in case of early exit */ + ops->retlen = 0; + ops->oobretlen = 0; + + /* Reject writes, which are not page aligned */ + if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { + printk(KERN_ERR "%s: Attempt to write not page aligned data\n", + __func__); + return -EINVAL; + } + + /* Check zero length */ + if (!len) + return 0; + oobsize = mtd_oobavail(mtd, ops); + oobcolumn = to & (mtd->oobsize - 1); + + column = to & (mtd->writesize - 1); + + /* Loop until all data write */ + while (1) { + if (written < len) { + u_char *wbuf = (u_char *) buf; + + thislen = min_t(int, mtd->writesize - column, len - written); + thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); + + cond_resched(); + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); + + /* Partial page write */ + subpage = thislen < mtd->writesize; + if (subpage) { + memset(this->page_buf, 0xff, mtd->writesize); + memcpy(this->page_buf + column, buf, thislen); + wbuf = this->page_buf; + } + + this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); + + if (oob) { + oobbuf = this->oob_buf; + + /* We send data to spare ram with oobsize + * to prevent byte access */ + memset(oobbuf, 0xff, mtd->oobsize); + if (ops->mode == MTD_OPS_AUTO_OOB) + onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); + else + memcpy(oobbuf + oobcolumn, oob, thisooblen); + + oobwritten += thisooblen; + oob += thisooblen; + oobcolumn = 0; + } else + oobbuf = (u_char *) ffchars; + + this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); + } else + ONENAND_SET_NEXT_BUFFERRAM(this); + + /* + * 2 PLANE, MLC, and Flex-OneNAND do not support + * write-while-program feature. + */ + if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) { + ONENAND_SET_PREV_BUFFERRAM(this); + + ret = this->wait(mtd, FL_WRITING); + + /* In partial page write we don't update bufferram */ + onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); + if (ret) { + written -= prevlen; + printk(KERN_ERR "%s: write failed %d\n", + __func__, ret); + break; + } + + if (written == len) { + /* Only check verify write turn on */ + ret = onenand_verify(mtd, buf - len, to - len, len); + if (ret) + printk(KERN_ERR "%s: verify failed %d\n", + __func__, ret); + break; + } + + ONENAND_SET_NEXT_BUFFERRAM(this); + } + + this->ongoing = 0; + cmd = ONENAND_CMD_PROG; + + /* Exclude 1st OTP and OTP blocks for cache program feature */ + if (ONENAND_IS_CACHE_PROGRAM(this) && + likely(onenand_block(this, to) != 0) && + ONENAND_IS_4KB_PAGE(this) && + ((written + thislen) < len)) { + cmd = ONENAND_CMD_2X_CACHE_PROG; + this->ongoing = 1; + } + + this->command(mtd, cmd, to, mtd->writesize); + + /* + * 2 PLANE, MLC, and Flex-OneNAND wait here + */ + if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) { + ret = this->wait(mtd, FL_WRITING); + + /* In partial page write we don't update bufferram */ + onenand_update_bufferram(mtd, to, !ret && !subpage); + if (ret) { + printk(KERN_ERR "%s: write failed %d\n", + __func__, ret); + break; + } + + /* Only check verify write turn on */ + ret = onenand_verify(mtd, buf, to, thislen); + if (ret) { + printk(KERN_ERR "%s: verify failed %d\n", + __func__, ret); + break; + } + + written += thislen; + + if (written == len) + break; + + } else + written += thislen; + + column = 0; + prev_subpage = subpage; + prev = to; + prevlen = thislen; + to += thislen; + buf += thislen; + first = 0; + } + + /* In error case, clear all bufferrams */ + if (written != len) + onenand_invalidate_bufferram(mtd, 0, -1); + + ops->retlen = written; + ops->oobretlen = oobwritten; + + return ret; +} + + +/** + * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * @param mode operation mode + * + * OneNAND write out-of-band + */ +static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int column, ret = 0, oobsize; + int written = 0, oobcmd; + u_char *oobbuf; + size_t len = ops->ooblen; + const u_char *buf = ops->oobbuf; + unsigned int mode = ops->mode; + + to += ops->ooboffs; + + pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, + (int)len); + + /* Initialize retlen, in case of early exit */ + ops->oobretlen = 0; + + if (mode == MTD_OPS_AUTO_OOB) + oobsize = mtd->oobavail; + else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "%s: Attempted to start write outside oob\n", + __func__); + return -EINVAL; + } + + /* For compatibility with NAND: Do not allow write past end of page */ + if (unlikely(column + len > oobsize)) { + printk(KERN_ERR "%s: Attempt to write past end of page\n", + __func__); + return -EINVAL; + } + + oobbuf = this->oob_buf; + + oobcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, oobsize, len - written); + + cond_resched(); + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); + + /* We send data to spare ram with oobsize + * to prevent byte access */ + memset(oobbuf, 0xff, mtd->oobsize); + if (mode == MTD_OPS_AUTO_OOB) + onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); + else + memcpy(oobbuf + column, buf, thislen); + this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); + + if (ONENAND_IS_4KB_PAGE(this)) { + /* Set main area of DataRAM to 0xff*/ + memset(this->page_buf, 0xff, mtd->writesize); + this->write_bufferram(mtd, ONENAND_DATARAM, + this->page_buf, 0, mtd->writesize); + } + + this->command(mtd, oobcmd, to, mtd->oobsize); + + onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } + + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "%s: write failed %d\n", __func__, ret); + break; + } + + ret = onenand_verify_oob(mtd, oobbuf, to); + if (ret) { + printk(KERN_ERR "%s: verify failed %d\n", + __func__, ret); + break; + } + + written += thislen; + if (written == len) + break; + + to += mtd->writesize; + buf += thislen; + column = 0; + } + + ops->oobretlen = written; + + return ret; +} + +/** + * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @param mtd: MTD device structure + * @param to: offset to write + * @param ops: oob operation description structure + */ +static int onenand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + break; + case MTD_OPS_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } + + onenand_get_device(mtd, FL_WRITING); + if (ops->datbuf) + ret = onenand_write_ops_nolock(mtd, to, ops); + else + ret = onenand_write_oob_nolock(mtd, to, ops); + onenand_release_device(mtd); + + return ret; +} + +/** + * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad + * @param mtd MTD device structure + * @param ofs offset from device start + * @param allowbbt 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + + /* Return info from the table */ + return bbm->isbad_bbt(mtd, ofs, allowbbt); +} + + +static int onenand_multiblock_erase_verify(struct mtd_info *mtd, + struct erase_info *instr) +{ + struct onenand_chip *this = mtd->priv; + loff_t addr = instr->addr; + int len = instr->len; + unsigned int block_size = (1 << this->erase_shift); + int ret = 0; + + while (len) { + this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size); + ret = this->wait(mtd, FL_VERIFYING_ERASE); + if (ret) { + printk(KERN_ERR "%s: Failed verify, block %d\n", + __func__, onenand_block(this, addr)); + instr->fail_addr = addr; + return -1; + } + len -= block_size; + addr += block_size; + } + return 0; +} + +/** + * onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase + * @param mtd MTD device structure + * @param instr erase instruction + * @param region erase region + * + * Erase one or more blocks up to 64 block at a time + */ +static int onenand_multiblock_erase(struct mtd_info *mtd, + struct erase_info *instr, + unsigned int block_size) +{ + struct onenand_chip *this = mtd->priv; + loff_t addr = instr->addr; + int len = instr->len; + int eb_count = 0; + int ret = 0; + int bdry_block = 0; + + if (ONENAND_IS_DDP(this)) { + loff_t bdry_addr = this->chipsize >> 1; + if (addr < bdry_addr && (addr + len) > bdry_addr) + bdry_block = bdry_addr >> this->erase_shift; + } + + /* Pre-check bbs */ + while (len) { + /* Check if we have a bad block, we do not erase bad blocks */ + if (onenand_block_isbad_nolock(mtd, addr, 0)) { + printk(KERN_WARNING "%s: attempt to erase a bad block " + "at addr 0x%012llx\n", + __func__, (unsigned long long) addr); + return -EIO; + } + len -= block_size; + addr += block_size; + } + + len = instr->len; + addr = instr->addr; + + /* loop over 64 eb batches */ + while (len) { + struct erase_info verify_instr = *instr; + int max_eb_count = MB_ERASE_MAX_BLK_COUNT; + + verify_instr.addr = addr; + verify_instr.len = 0; + + /* do not cross chip boundary */ + if (bdry_block) { + int this_block = (addr >> this->erase_shift); + + if (this_block < bdry_block) { + max_eb_count = min(max_eb_count, + (bdry_block - this_block)); + } + } + + eb_count = 0; + + while (len > block_size && eb_count < (max_eb_count - 1)) { + this->command(mtd, ONENAND_CMD_MULTIBLOCK_ERASE, + addr, block_size); + onenand_invalidate_bufferram(mtd, addr, block_size); + + ret = this->wait(mtd, FL_PREPARING_ERASE); + if (ret) { + printk(KERN_ERR "%s: Failed multiblock erase, " + "block %d\n", __func__, + onenand_block(this, addr)); + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + return -EIO; + } + + len -= block_size; + addr += block_size; + eb_count++; + } + + /* last block of 64-eb series */ + cond_resched(); + this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + onenand_invalidate_bufferram(mtd, addr, block_size); + + ret = this->wait(mtd, FL_ERASING); + /* Check if it is write protected */ + if (ret) { + printk(KERN_ERR "%s: Failed erase, block %d\n", + __func__, onenand_block(this, addr)); + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + return -EIO; + } + + len -= block_size; + addr += block_size; + eb_count++; + + /* verify */ + verify_instr.len = eb_count * block_size; + if (onenand_multiblock_erase_verify(mtd, &verify_instr)) { + instr->fail_addr = verify_instr.fail_addr; + return -EIO; + } + + } + return 0; +} + + +/** + * onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase + * @param mtd MTD device structure + * @param instr erase instruction + * @param region erase region + * @param block_size erase block size + * + * Erase one or more blocks one block at a time + */ +static int onenand_block_by_block_erase(struct mtd_info *mtd, + struct erase_info *instr, + struct mtd_erase_region_info *region, + unsigned int block_size) +{ + struct onenand_chip *this = mtd->priv; + loff_t addr = instr->addr; + int len = instr->len; + loff_t region_end = 0; + int ret = 0; + + if (region) { + /* region is set for Flex-OneNAND */ + region_end = region->offset + region->erasesize * region->numblocks; + } + + /* Loop through the blocks */ + while (len) { + cond_resched(); + + /* Check if we have a bad block, we do not erase bad blocks */ + if (onenand_block_isbad_nolock(mtd, addr, 0)) { + printk(KERN_WARNING "%s: attempt to erase a bad block " + "at addr 0x%012llx\n", + __func__, (unsigned long long) addr); + return -EIO; + } + + this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + + onenand_invalidate_bufferram(mtd, addr, block_size); + + ret = this->wait(mtd, FL_ERASING); + /* Check, if it is write protected */ + if (ret) { + printk(KERN_ERR "%s: Failed erase, block %d\n", + __func__, onenand_block(this, addr)); + instr->fail_addr = addr; + return -EIO; + } + + len -= block_size; + addr += block_size; + + if (region && addr == region_end) { + if (!len) + break; + region++; + + block_size = region->erasesize; + region_end = region->offset + region->erasesize * region->numblocks; + + if (len & (block_size - 1)) { + /* FIXME: This should be handled at MTD partitioning level. */ + printk(KERN_ERR "%s: Unaligned address\n", + __func__); + return -EIO; + } + } + } + return 0; +} + +/** + * onenand_erase - [MTD Interface] erase block(s) + * @param mtd MTD device structure + * @param instr erase instruction + * + * Erase one or more blocks + */ +static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct onenand_chip *this = mtd->priv; + unsigned int block_size; + loff_t addr = instr->addr; + loff_t len = instr->len; + int ret = 0; + struct mtd_erase_region_info *region = NULL; + loff_t region_offset = 0; + + pr_debug("%s: start=0x%012llx, len=%llu\n", __func__, + (unsigned long long)instr->addr, + (unsigned long long)instr->len); + + if (FLEXONENAND(this)) { + /* Find the eraseregion of this address */ + int i = flexonenand_region(mtd, addr); + + region = &mtd->eraseregions[i]; + block_size = region->erasesize; + + /* Start address within region must align on block boundary. + * Erase region's start offset is always block start address. + */ + region_offset = region->offset; + } else + block_size = 1 << this->erase_shift; + + /* Start address must align on block boundary */ + if (unlikely((addr - region_offset) & (block_size - 1))) { + printk(KERN_ERR "%s: Unaligned address\n", __func__); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (unlikely(len & (block_size - 1))) { + printk(KERN_ERR "%s: Length not block aligned\n", __func__); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_ERASING); + + if (ONENAND_IS_4KB_PAGE(this) || region || + instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) { + /* region is set for Flex-OneNAND (no mb erase) */ + ret = onenand_block_by_block_erase(mtd, instr, + region, block_size); + } else { + ret = onenand_multiblock_erase(mtd, instr, block_size); + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + return ret; +} + +/** + * onenand_sync - [MTD Interface] sync + * @param mtd MTD device structure + * + * Sync is actually a wait for chip ready function + */ +static void onenand_sync(struct mtd_info *mtd) +{ + pr_debug("%s: called\n", __func__); + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_SYNCING); + + /* Release it and go back */ + onenand_release_device(mtd); +} + +/** + * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * + * Check whether the block is bad + */ +static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + int ret; + + onenand_get_device(mtd, FL_READING); + ret = onenand_block_isbad_nolock(mtd, ofs, 0); + onenand_release_device(mtd); + return ret; +} + +/** + * onenand_default_block_markbad - [DEFAULT] mark a block bad + * @param mtd MTD device structure + * @param ofs offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. + */ +static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + u_char buf[2] = {0, 0}; + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooblen = 2, + .oobbuf = buf, + .ooboffs = 0, + }; + int block; + + /* Get block number */ + block = onenand_block(this, ofs); + if (bbm->bbt) + bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* We write two bytes, so we don't have to mess with 16-bit access */ + ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); + /* FIXME : What to do when marking SLC block in partition + * with MLC erasesize? For now, it is not advisable to + * create partitions containing both SLC and MLC regions. + */ + return onenand_write_oob_nolock(mtd, ofs, &ops); +} + +/** + * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * + * Mark the block as bad + */ +static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct onenand_chip *this = mtd->priv; + int ret; + + ret = onenand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + onenand_get_device(mtd, FL_WRITING); + ret = this->block_markbad(mtd, ofs); + onenand_release_device(mtd); + return ret; +} + +/** + * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to lock or unlock + * @param cmd lock or unlock command + * + * Lock or unlock one or more blocks + */ +static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) +{ + struct onenand_chip *this = mtd->priv; + int start, end, block, value, status; + int wp_status_mask; + + start = onenand_block(this, ofs); + end = onenand_block(this, ofs + len) - 1; + + if (cmd == ONENAND_CMD_LOCK) + wp_status_mask = ONENAND_WP_LS; + else + wp_status_mask = ONENAND_WP_US; + + /* Continuous lock scheme */ + if (this->options & ONENAND_HAS_CONT_LOCK) { + /* Set start block address */ + this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Set end block address */ + this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); + /* Write lock command */ + this->command(mtd, cmd, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_LOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & wp_status_mask)) + printk(KERN_ERR "%s: wp status = 0x%x\n", + __func__, status); + + return 0; + } + + /* Block lock scheme */ + for (block = start; block < end + 1; block++) { + /* Set block address */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + /* Set start block address */ + this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Write lock command */ + this->command(mtd, cmd, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_LOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & wp_status_mask)) + printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n", + __func__, block, status); + } + + return 0; +} + +/** + * onenand_lock - [MTD Interface] Lock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Lock one or more blocks + */ +static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret; + + onenand_get_device(mtd, FL_LOCKING); + ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); + onenand_release_device(mtd); + return ret; +} + +/** + * onenand_unlock - [MTD Interface] Unlock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Unlock one or more blocks + */ +static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret; + + onenand_get_device(mtd, FL_LOCKING); + ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + onenand_release_device(mtd); + return ret; +} + +/** + * onenand_check_lock_status - [OneNAND Interface] Check lock status + * @param this onenand chip data structure + * + * Check lock status + */ +static int onenand_check_lock_status(struct onenand_chip *this) +{ + unsigned int value, block, status; + unsigned int end; + + end = this->chipsize >> this->erase_shift; + for (block = 0; block < end; block++) { + /* Set block address */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + /* Set start block address */ + this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & ONENAND_WP_US)) { + printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n", + __func__, block, status); + return 0; + } + } + + return 1; +} + +/** + * onenand_unlock_all - [OneNAND Interface] unlock all blocks + * @param mtd MTD device structure + * + * Unlock all blocks + */ +static void onenand_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + loff_t len = mtd->size; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Set start block address */ + this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_LOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Don't check lock status */ + if (this->options & ONENAND_SKIP_UNLOCK_CHECK) + return; + + /* Check lock status */ + if (onenand_check_lock_status(this)) + return; + + /* Workaround for all block unlock in DDP */ + if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; + } + } + + onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); +} + +#ifdef CONFIG_MTD_ONENAND_OTP + +/** + * onenand_otp_command - Send OTP specific command to OneNAND device + * @param mtd MTD device structure + * @param cmd the command to be sent + * @param addr offset to read from or write to + * @param len number of bytes to read or write + */ +static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + int value, block, page; + + /* Address translation */ + switch (cmd) { + case ONENAND_CMD_OTP_ACCESS: + block = (int) (addr >> this->erase_shift); + page = -1; + break; + + default: + block = (int) (addr >> this->erase_shift); + page = (int) (addr >> this->page_shift); + + if (ONENAND_IS_2PLANE(this)) { + /* Make the even block number */ + block &= ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page >>= 1; + } + page &= this->page_mask; + break; + } + + if (block != -1) { + /* Write 'DFS, FBA' of Flash */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS1); + } + + if (page != -1) { + /* Now we use page size operation */ + int sectors = 4, count = 4; + int dataram; + + switch (cmd) { + default: + if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) + cmd = ONENAND_CMD_2X_PROG; + dataram = ONENAND_CURRENT_BUFFERRAM(this); + break; + } + + /* Write 'FPA, FSA' of Flash */ + value = onenand_page_address(page, sectors); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS8); + + /* Write 'BSA, BSC' of DataRAM */ + value = onenand_buffer_address(dataram, sectors, count); + this->write_word(value, this->base + ONENAND_REG_START_BUFFER); + } + + /* Interrupt clear */ + this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); + + /* Write command */ + this->write_word(cmd, this->base + ONENAND_REG_COMMAND); + + return 0; +} + +/** + * onenand_otp_write_oob_nolock - [INTERN] OneNAND write out-of-band, specific to OTP + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * OneNAND write out-of-band only for OTP + */ +static int onenand_otp_write_oob_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int column, ret = 0, oobsize; + int written = 0; + u_char *oobbuf; + size_t len = ops->ooblen; + const u_char *buf = ops->oobbuf; + int block, value, status; + + to += ops->ooboffs; + + /* Initialize retlen, in case of early exit */ + ops->oobretlen = 0; + + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + oobbuf = this->oob_buf; + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, oobsize, len - written); + + cond_resched(); + + block = (int) (to >> this->erase_shift); + /* + * Write 'DFS, FBA' of Flash + * Add: F100h DQ=DFS, FBA + */ + + value = onenand_block_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS1); + + /* + * Select DataRAM for DDP + * Add: F101h DQ=DBS + */ + + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS2); + ONENAND_SET_NEXT_BUFFERRAM(this); + + /* + * Enter OTP access mode + */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + /* We send data to spare ram with oobsize + * to prevent byte access */ + memcpy(oobbuf + column, buf, thislen); + + /* + * Write Data into DataRAM + * Add: 8th Word + * in sector0/spare/page0 + * DQ=XXFCh + */ + this->write_bufferram(mtd, ONENAND_SPARERAM, + oobbuf, 0, mtd->oobsize); + + onenand_otp_command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } + + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "%s: write failed %d\n", __func__, ret); + break; + } + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + status &= 0x60; + + if (status == 0x60) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tLOCKED\n"); + printk(KERN_DEBUG "OTP Block\tLOCKED\n"); + } else if (status == 0x20) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tLOCKED\n"); + printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n"); + } else if (status == 0x40) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tUN-LOCKED\n"); + printk(KERN_DEBUG "OTP Block\tLOCKED\n"); + } else { + printk(KERN_DEBUG "Reboot to check\n"); + } + + written += thislen; + if (written == len) + break; + + to += mtd->writesize; + buf += thislen; + column = 0; + } + + ops->oobretlen = written; + + return ret; +} + +/* Internal OTP operation */ +typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, + size_t *retlen, u_char *buf); + +/** + * do_otp_read - [DEFAULT] Read OTP block area + * @param mtd MTD device structure + * @param from The offset to read + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of readbytes + * @param buf the databuffer to put/get data + * + * Read OTP block area. + */ +static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_oob_ops ops = { + .len = len, + .ooblen = 0, + .datbuf = buf, + .oobbuf = NULL, + }; + int ret; + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + ret = ONENAND_IS_4KB_PAGE(this) ? + onenand_mlc_read_ops_nolock(mtd, from, &ops) : + onenand_read_ops_nolock(mtd, from, &ops); + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + return ret; +} + +/** + * do_otp_write - [DEFAULT] Write OTP block area + * @param mtd MTD device structure + * @param to The offset to write + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of write bytes + * @param buf the databuffer to put/get data + * + * Write OTP block area. + */ +static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + unsigned char *pbuf = buf; + int ret; + struct mtd_oob_ops ops; + + /* Force buffer page aligned */ + if (len < mtd->writesize) { + memcpy(this->page_buf, buf, len); + memset(this->page_buf + len, 0xff, mtd->writesize - len); + pbuf = this->page_buf; + len = mtd->writesize; + } + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + ops.len = len; + ops.ooblen = 0; + ops.datbuf = pbuf; + ops.oobbuf = NULL; + ret = onenand_write_ops_nolock(mtd, to, &ops); + *retlen = ops.retlen; + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + return ret; +} + +/** + * do_otp_lock - [DEFAULT] Lock OTP block area + * @param mtd MTD device structure + * @param from The offset to lock + * @param len number of bytes to lock + * @param retlen pointer to variable to store the number of lock bytes + * @param buf the databuffer to put/get data + * + * Lock OTP block area. + */ +static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_oob_ops ops; + int ret; + + if (FLEXONENAND(this)) { + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + /* + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. + */ + ops.len = mtd->writesize; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); + *retlen = ops.retlen; + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + } else { + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooblen = len; + ops.oobbuf = buf; + ops.ooboffs = 0; + ret = onenand_otp_write_oob_nolock(mtd, from, &ops); + *retlen = ops.oobretlen; + } + + return ret; +} + +/** + * onenand_otp_walk - [DEFAULT] Handle OTP operation + * @param mtd MTD device structure + * @param from The offset to read/write + * @param len number of bytes to read/write + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data + * @param action do given action + * @param mode specify user and factory + * + * Handle OTP operation. + */ +static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, + otp_op_t action, int mode) +{ + struct onenand_chip *this = mtd->priv; + int otp_pages; + int density; + int ret = 0; + + *retlen = 0; + + density = onenand_get_density(this->device_id); + if (density < ONENAND_DEVICE_DENSITY_512Mb) + otp_pages = 20; + else + otp_pages = 50; + + if (mode == MTD_OTP_FACTORY) { + from += mtd->writesize * otp_pages; + otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages; + } + + /* Check User/Factory boundary */ + if (mode == MTD_OTP_USER) { + if (mtd->writesize * otp_pages < from + len) + return 0; + } else { + if (mtd->writesize * otp_pages < len) + return 0; + } + + onenand_get_device(mtd, FL_OTPING); + while (len > 0 && otp_pages > 0) { + if (!action) { /* OTP Info functions */ + struct otp_info *otpinfo; + + len -= sizeof(struct otp_info); + if (len <= 0) { + ret = -ENOSPC; + break; + } + + otpinfo = (struct otp_info *) buf; + otpinfo->start = from; + otpinfo->length = mtd->writesize; + otpinfo->locked = 0; + + from += mtd->writesize; + buf += sizeof(struct otp_info); + *retlen += sizeof(struct otp_info); + } else { + size_t tmp_retlen; + + ret = action(mtd, from, len, &tmp_retlen, buf); + if (ret) + break; + + buf += tmp_retlen; + len -= tmp_retlen; + *retlen += tmp_retlen; + + } + otp_pages--; + } + onenand_release_device(mtd); + + return ret; +} + +/** + * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info + * @param mtd MTD device structure + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data + * + * Read factory OTP info. + */ +static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) +{ + return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL, + MTD_OTP_FACTORY); +} + +/** + * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area + * @param mtd MTD device structure + * @param from The offset to read + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data + * + * Read factory OTP area. + */ +static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY); +} + +/** + * onenand_get_user_prot_info - [MTD Interface] Read user OTP info + * @param mtd MTD device structure + * @param retlen pointer to variable to store the number of read bytes + * @param len number of bytes to read + * @param buf the databuffer to put/get data + * + * Read user OTP info. + */ +static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) +{ + return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL, + MTD_OTP_USER); +} + +/** + * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area + * @param mtd MTD device structure + * @param from The offset to read + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data + * + * Read user OTP area. + */ +static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER); +} + +/** + * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area + * @param mtd MTD device structure + * @param from The offset to write + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of write bytes + * @param buf the databuffer to put/get data + * + * Write user OTP area. + */ +static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER); +} + +/** + * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area + * @param mtd MTD device structure + * @param from The offset to lock + * @param len number of bytes to unlock + * + * Write lock mark on spare area in page 0 in OTP block + */ +static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; + size_t retlen; + int ret; + unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET; + + memset(buf, 0xff, FLEXONENAND(this) ? this->writesize + : mtd->oobsize); + /* + * Write lock mark to 8th word of sector0 of page0 of the spare0. + * We write 16 bytes spare area instead of 2 bytes. + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. + */ + + from = 0; + len = FLEXONENAND(this) ? mtd->writesize : 16; + + /* + * Note: OTP lock operation + * OTP block : 0xXXFC XX 1111 1100 + * 1st block : 0xXXF3 (If chip support) XX 1111 0011 + * Both : 0xXXF0 (If chip support) XX 1111 0000 + */ + if (FLEXONENAND(this)) + otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET; + + /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */ + if (otp == 1) + buf[otp_lock_offset] = 0xFC; + else if (otp == 2) + buf[otp_lock_offset] = 0xF3; + else if (otp == 3) + buf[otp_lock_offset] = 0xF0; + else if (otp != 0) + printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n"); + + ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); + + return ret ? : retlen; +} + +#endif /* CONFIG_MTD_ONENAND_OTP */ + +/** + * onenand_check_features - Check and set OneNAND features + * @param mtd MTD data structure + * + * Check and set OneNAND features + * - lock scheme + * - two plane + */ +static void onenand_check_features(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int density, process, numbufs; + + /* Lock scheme depends on density and process */ + density = onenand_get_density(this->device_id); + process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; + numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8; + + /* Lock scheme */ + switch (density) { + case ONENAND_DEVICE_DENSITY_4Gb: + if (ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + else if (numbufs == 1) { + this->options |= ONENAND_HAS_4KB_PAGE; + this->options |= ONENAND_HAS_CACHE_PROGRAM; + /* + * There are two different 4KiB pagesize chips + * and no way to detect it by H/W config values. + * + * To detect the correct NOP for each chips, + * It should check the version ID as workaround. + * + * Now it has as following + * KFM4G16Q4M has NOP 4 with version ID 0x0131 + * KFM4G16Q5M has NOP 1 with versoin ID 0x013e + */ + if ((this->version_id & 0xf) == 0xe) + this->options |= ONENAND_HAS_NOP_1; + } + + case ONENAND_DEVICE_DENSITY_2Gb: + /* 2Gb DDP does not have 2 plane */ + if (!ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + this->options |= ONENAND_HAS_UNLOCK_ALL; + + case ONENAND_DEVICE_DENSITY_1Gb: + /* A-Die has all block unlock */ + if (process) + this->options |= ONENAND_HAS_UNLOCK_ALL; + break; + + default: + /* Some OneNAND has continuous lock scheme */ + if (!process) + this->options |= ONENAND_HAS_CONT_LOCK; + break; + } + + /* The MLC has 4KiB pagesize. */ + if (ONENAND_IS_MLC(this)) + this->options |= ONENAND_HAS_4KB_PAGE; + + if (ONENAND_IS_4KB_PAGE(this)) + this->options &= ~ONENAND_HAS_2PLANE; + + if (FLEXONENAND(this)) { + this->options &= ~ONENAND_HAS_CONT_LOCK; + this->options |= ONENAND_HAS_UNLOCK_ALL; + } + + if (this->options & ONENAND_HAS_CONT_LOCK) + printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); + if (this->options & ONENAND_HAS_UNLOCK_ALL) + printk(KERN_DEBUG "Chip support all block unlock\n"); + if (this->options & ONENAND_HAS_2PLANE) + printk(KERN_DEBUG "Chip has 2 plane\n"); + if (this->options & ONENAND_HAS_4KB_PAGE) + printk(KERN_DEBUG "Chip has 4KiB pagesize\n"); + if (this->options & ONENAND_HAS_CACHE_PROGRAM) + printk(KERN_DEBUG "Chip has cache program feature\n"); +} + +/** + * onenand_print_device_info - Print device & version ID + * @param device device ID + * @param version version ID + * + * Print device & version ID + */ +static void onenand_print_device_info(int device, int version) +{ + int vcc, demuxed, ddp, density, flexonenand; + + vcc = device & ONENAND_DEVICE_VCC_MASK; + demuxed = device & ONENAND_DEVICE_IS_DEMUX; + ddp = device & ONENAND_DEVICE_IS_DDP; + density = onenand_get_density(device); + flexonenand = device & DEVICE_IS_FLEXONENAND; + printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", + demuxed ? "" : "Muxed ", + flexonenand ? "Flex-" : "", + ddp ? "(DDP)" : "", + (16 << density), + vcc ? "2.65/3.3" : "1.8", + device); + printk(KERN_INFO "OneNAND version = 0x%04x\n", version); +} + +static const struct onenand_manufacturers onenand_manuf_ids[] = { + {ONENAND_MFR_SAMSUNG, "Samsung"}, + {ONENAND_MFR_NUMONYX, "Numonyx"}, +}; + +/** + * onenand_check_maf - Check manufacturer ID + * @param manuf manufacturer ID + * + * Check manufacturer ID + */ +static int onenand_check_maf(int manuf) +{ + int size = ARRAY_SIZE(onenand_manuf_ids); + char *name; + int i; + + for (i = 0; i < size; i++) + if (manuf == onenand_manuf_ids[i].id) + break; + + if (i < size) + name = onenand_manuf_ids[i].name; + else + name = "Unknown"; + + printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf); + + return (i == size); +} + +/** +* flexonenand_get_boundary - Reads the SLC boundary +* @param onenand_info - onenand info structure +**/ +static int flexonenand_get_boundary(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned die, bdry; + int syscfg, locked; + + /* Disable ECC */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); + + for (die = 0; die < this->dies; die++) { + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + this->wait(mtd, FL_READING); + + bdry = this->read_word(this->base + ONENAND_DATARAM); + if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) + locked = 0; + else + locked = 1; + this->boundary[die] = bdry & FLEXONENAND_PI_MASK; + + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info + * boundary[], diesize[], mtd->size, mtd->erasesize + * @param mtd - MTD device structure + */ +static void flexonenand_get_size(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int die, i, eraseshift, density; + int blksperdie, maxbdry; + loff_t ofs; + + density = onenand_get_density(this->device_id); + blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + maxbdry = blksperdie - 1; + eraseshift = this->erase_shift - 1; + + mtd->numeraseregions = this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die = ofs = 0; + i = -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = + this->boundary[die] + 1; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -= 1; + mtd->eraseregions[i].numblocks += + this->boundary[die] + 1; + ofs += (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = maxbdry ^ + this->boundary[die]; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -= 1; + } + + /* Expose MLC erase size except when all blocks are SLC */ + mtd->erasesize = 1 << this->erase_shift; + if (mtd->numeraseregions == 1) + mtd->erasesize >>= 1; + + printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); + for (i = 0; i < mtd->numeraseregions; i++) + printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," + " numblocks: %04u]\n", + (unsigned int) mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + + for (die = 0, mtd->size = 0; die < this->dies; die++) { + this->diesize[die] = (loff_t)blksperdie << this->erase_shift; + this->diesize[die] -= (loff_t)(this->boundary[die] + 1) + << (this->erase_shift - 1); + mtd->size += this->diesize[die]; + } +} + +/** + * flexonenand_check_blocks_erased - Check if blocks are erased + * @param mtd_info - mtd info structure + * @param start - first erase block to check + * @param end - last erase block to check + * + * Converting an unerased block from MLC to SLC + * causes byte values to change. Since both data and its ECC + * have changed, reads on the block give uncorrectable error. + * This might lead to the block being detected as bad. + * + * Avoid this by ensuring that the block to be converted is + * erased. + */ +static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end) +{ + struct onenand_chip *this = mtd->priv; + int i, ret; + int block; + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooboffs = 0, + .ooblen = mtd->oobsize, + .datbuf = NULL, + .oobbuf = this->oob_buf, + }; + loff_t addr; + + printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); + + for (block = start; block <= end; block++) { + addr = flexonenand_addr(this, block); + if (onenand_block_isbad_nolock(mtd, addr, 0)) + continue; + + /* + * Since main area write results in ECC write to spare, + * it is sufficient to check only ECC bytes for change. + */ + ret = onenand_read_oob_nolock(mtd, addr, &ops); + if (ret) + return ret; + + for (i = 0; i < mtd->oobsize; i++) + if (this->oob_buf[i] != 0xff) + break; + + if (i != mtd->oobsize) { + printk(KERN_WARNING "%s: Block %d not erased.\n", + __func__, block); + return 1; + } + } + + return 0; +} + +/** + * flexonenand_set_boundary - Writes the SLC boundary + * @param mtd - mtd info structure + */ +static int flexonenand_set_boundary(struct mtd_info *mtd, int die, + int boundary, int lock) +{ + struct onenand_chip *this = mtd->priv; + int ret, density, blksperdie, old, new, thisboundary; + loff_t addr; + + /* Change only once for SDP Flex-OneNAND */ + if (die && (!ONENAND_IS_DDP(this))) + return 0; + + /* boundary value of -1 indicates no required change */ + if (boundary < 0 || boundary == this->boundary[die]) + return 0; + + density = onenand_get_density(this->device_id); + blksperdie = ((16 << density) << 20) >> this->erase_shift; + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + + if (boundary >= blksperdie) { + printk(KERN_ERR "%s: Invalid boundary value. " + "Boundary not changed.\n", __func__); + return -EINVAL; + } + + /* Check if converting blocks are erased */ + old = this->boundary[die] + (die * this->density_mask); + new = boundary + (die * this->density_mask); + ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new)); + if (ret) { + printk(KERN_ERR "%s: Please erase blocks " + "before boundary change\n", __func__); + return ret; + } + + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + /* Check is boundary is locked */ + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + this->wait(mtd, FL_READING); + + thisboundary = this->read_word(this->base + ONENAND_DATARAM); + if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { + printk(KERN_ERR "%s: boundary locked\n", __func__); + ret = 1; + goto out; + } + + printk(KERN_INFO "Changing die %d boundary: %d%s\n", + die, boundary, lock ? "(Locked)" : "(Unlocked)"); + + addr = die ? this->diesize[0] : 0; + + boundary &= FLEXONENAND_PI_MASK; + boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); + + this->command(mtd, ONENAND_CMD_ERASE, addr, 0); + ret = this->wait(mtd, FL_ERASING); + if (ret) { + printk(KERN_ERR "%s: Failed PI erase for Die %d\n", + __func__, die); + goto out; + } + + this->write_word(boundary, this->base + ONENAND_DATARAM); + this->command(mtd, ONENAND_CMD_PROG, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "%s: Failed PI write for Die %d\n", + __func__, die); + goto out; + } + + this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); + ret = this->wait(mtd, FL_WRITING); +out: + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); + this->wait(mtd, FL_RESETING); + if (!ret) + /* Recalculate device size on boundary change*/ + flexonenand_get_size(mtd); + + return ret; +} + +/** + * onenand_chip_probe - [OneNAND Interface] The generic chip probe + * @param mtd MTD device structure + * + * OneNAND detection method: + * Compare the values from command with ones from register + */ +static int onenand_chip_probe(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int bram_maf_id, bram_dev_id, maf_id, dev_id; + int syscfg; + + /* Save system configuration 1 */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + /* Clear Sync. Burst Read mode to read BootRAM */ + this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1); + + /* Send the command for reading device ID from BootRAM */ + this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); + + /* Read manufacturer and device IDs from BootRAM */ + bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); + bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); + + /* Reset OneNAND to read default register values */ + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); + /* Wait reset */ + this->wait(mtd, FL_RESETING); + + /* Restore system configuration 1 */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + + /* Check manufacturer ID */ + if (onenand_check_maf(bram_maf_id)) + return -ENXIO; + + /* Read manufacturer and device IDs from Register */ + maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); + dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + + /* Check OneNAND device */ + if (maf_id != bram_maf_id || dev_id != bram_dev_id) + return -ENXIO; + + return 0; +} + +/** + * onenand_probe - [OneNAND Interface] Probe the OneNAND device + * @param mtd MTD device structure + */ +static int onenand_probe(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int dev_id, ver_id; + int density; + int ret; + + ret = this->chip_probe(mtd); + if (ret) + return ret; + + /* Device and version IDs from Register */ + dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); + + /* Flash device information */ + onenand_print_device_info(dev_id, ver_id); + this->device_id = dev_id; + this->version_id = ver_id; + + /* Check OneNAND features */ + onenand_check_features(mtd); + + density = onenand_get_density(dev_id); + if (FLEXONENAND(this)) { + this->dies = ONENAND_IS_DDP(this) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions = this->dies << 1; + mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1), GFP_KERNEL); + if (!mtd->eraseregions) + return -ENOMEM; + } + + /* + * For Flex-OneNAND, chipsize represents maximum possible device size. + * mtd->size represents the actual device size. + */ + this->chipsize = (16 << density) << 20; + + /* OneNAND page size & block size */ + /* The data buffer size is equal to page size */ + mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + /* We use the full BufferRAM */ + if (ONENAND_IS_4KB_PAGE(this)) + mtd->writesize <<= 1; + + mtd->oobsize = mtd->writesize >> 5; + /* Pages per a block are always 64 in OneNAND */ + mtd->erasesize = mtd->writesize << 6; + /* + * Flex-OneNAND SLC area has 64 pages per block. + * Flex-OneNAND MLC area has 128 pages per block. + * Expose MLC erase size to find erase_shift and page_mask. + */ + if (FLEXONENAND(this)) + mtd->erasesize <<= 1; + + this->erase_shift = ffs(mtd->erasesize) - 1; + this->page_shift = ffs(mtd->writesize) - 1; + this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; + /* Set density mask. it is used for DDP */ + if (ONENAND_IS_DDP(this)) + this->density_mask = this->chipsize >> (this->erase_shift + 1); + /* It's real page size */ + this->writesize = mtd->writesize; + + /* REVISIT: Multichip handling */ + + if (FLEXONENAND(this)) + flexonenand_get_size(mtd); + else + mtd->size = this->chipsize; + + /* + * We emulate the 4KiB page and 256KiB erase block size + * But oobsize is still 64 bytes. + * It is only valid if you turn on 2X program support, + * Otherwise it will be ignored by compiler. + */ + if (ONENAND_IS_2PLANE(this)) { + mtd->writesize <<= 1; + mtd->erasesize <<= 1; + } + + return 0; +} + +/** + * onenand_suspend - [MTD Interface] Suspend the OneNAND flash + * @param mtd MTD device structure + */ +static int onenand_suspend(struct mtd_info *mtd) +{ + return onenand_get_device(mtd, FL_PM_SUSPENDED); +} + +/** + * onenand_resume - [MTD Interface] Resume the OneNAND flash + * @param mtd MTD device structure + */ +static void onenand_resume(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + if (this->state == FL_PM_SUSPENDED) + onenand_release_device(mtd); + else + printk(KERN_ERR "%s: resume() called for the chip which is not " + "in suspended state\n", __func__); +} + +/** + * onenand_scan - [OneNAND Interface] Scan for the OneNAND device + * @param mtd MTD device structure + * @param maxchips Number of chips to scan for + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + */ +int onenand_scan(struct mtd_info *mtd, int maxchips) +{ + int i, ret; + struct onenand_chip *this = mtd->priv; + + if (!this->read_word) + this->read_word = onenand_readw; + if (!this->write_word) + this->write_word = onenand_writew; + + if (!this->command) + this->command = onenand_command; + if (!this->wait) + onenand_setup_wait(mtd); + if (!this->bbt_wait) + this->bbt_wait = onenand_bbt_wait; + if (!this->unlock_all) + this->unlock_all = onenand_unlock_all; + + if (!this->chip_probe) + this->chip_probe = onenand_chip_probe; + + if (!this->read_bufferram) + this->read_bufferram = onenand_read_bufferram; + if (!this->write_bufferram) + this->write_bufferram = onenand_write_bufferram; + + if (!this->block_markbad) + this->block_markbad = onenand_default_block_markbad; + if (!this->scan_bbt) + this->scan_bbt = onenand_default_bbt; + + if (onenand_probe(mtd)) + return -ENXIO; + + /* Set Sync. Burst Read after probing */ + if (this->mmcontrol) { + printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); + this->read_bufferram = onenand_sync_read_bufferram; + } + + /* Allocate buffers, if necessary */ + if (!this->page_buf) { + this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); + if (!this->page_buf) + return -ENOMEM; +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL); + if (!this->verify_buf) { + kfree(this->page_buf); + return -ENOMEM; + } +#endif + this->options |= ONENAND_PAGEBUF_ALLOC; + } + if (!this->oob_buf) { + this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!this->oob_buf) { + if (this->options & ONENAND_PAGEBUF_ALLOC) { + this->options &= ~ONENAND_PAGEBUF_ALLOC; + kfree(this->page_buf); + } + return -ENOMEM; + } + this->options |= ONENAND_OOBBUF_ALLOC; + } + + this->state = FL_READY; + init_waitqueue_head(&this->wq); + spin_lock_init(&this->chip_lock); + + /* + * Allow subpage writes up to oobsize. + */ + switch (mtd->oobsize) { + case 128: + if (FLEXONENAND(this)) { + mtd_set_ooblayout(mtd, &flexonenand_ooblayout_ops); + mtd->subpage_sft = 0; + } else { + mtd_set_ooblayout(mtd, &onenand_oob_128_ooblayout_ops); + mtd->subpage_sft = 2; + } + if (ONENAND_IS_NOP_1(this)) + mtd->subpage_sft = 0; + break; + case 64: + mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); + mtd->subpage_sft = 2; + break; + + case 32: + mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); + mtd->subpage_sft = 1; + break; + + default: + printk(KERN_WARNING "%s: No OOB scheme defined for oobsize %d\n", + __func__, mtd->oobsize); + mtd->subpage_sft = 0; + /* To prevent kernel oops */ + mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); + break; + } + + this->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + ret = 0; + + mtd->oobavail = ret; + + mtd->ecc_strength = 1; + + /* Fill in remaining MTD driver data */ + mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->_erase = onenand_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read_oob = onenand_read_oob; + mtd->_write_oob = onenand_write_oob; + mtd->_panic_write = onenand_panic_write; +#ifdef CONFIG_MTD_ONENAND_OTP + mtd->_get_fact_prot_info = onenand_get_fact_prot_info; + mtd->_read_fact_prot_reg = onenand_read_fact_prot_reg; + mtd->_get_user_prot_info = onenand_get_user_prot_info; + mtd->_read_user_prot_reg = onenand_read_user_prot_reg; + mtd->_write_user_prot_reg = onenand_write_user_prot_reg; + mtd->_lock_user_prot_reg = onenand_lock_user_prot_reg; +#endif + mtd->_sync = onenand_sync; + mtd->_lock = onenand_lock; + mtd->_unlock = onenand_unlock; + mtd->_suspend = onenand_suspend; + mtd->_resume = onenand_resume; + mtd->_block_isbad = onenand_block_isbad; + mtd->_block_markbad = onenand_block_markbad; + mtd->owner = THIS_MODULE; + mtd->writebufsize = mtd->writesize; + + /* Unlock whole block */ + if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING)) + this->unlock_all(mtd); + + ret = this->scan_bbt(mtd); + if ((!FLEXONENAND(this)) || ret) + return ret; + + /* Change Flex-OneNAND boundaries if required */ + for (i = 0; i < MAX_DIES; i++) + flexonenand_set_boundary(mtd, i, flex_bdry[2 * i], + flex_bdry[(2 * i) + 1]); + + return 0; +} + +/** + * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device + * @param mtd MTD device structure + */ +void onenand_release(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + /* Deregister partitions */ + mtd_device_unregister(mtd); + + /* Free bad block table memory, if allocated */ + if (this->bbm) { + struct bbm_info *bbm = this->bbm; + kfree(bbm->bbt); + kfree(this->bbm); + } + /* Buffers allocated by onenand_scan */ + if (this->options & ONENAND_PAGEBUF_ALLOC) { + kfree(this->page_buf); +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + kfree(this->verify_buf); +#endif + } + if (this->options & ONENAND_OOBBUF_ALLOC) + kfree(this->oob_buf); + kfree(mtd->eraseregions); +} + +EXPORT_SYMBOL_GPL(onenand_scan); +EXPORT_SYMBOL_GPL(onenand_release); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); +MODULE_DESCRIPTION("Generic OneNAND flash driver code"); diff --git a/drivers/mtd/nand/onenand/onenand_bbt.c b/drivers/mtd/nand/onenand/onenand_bbt.c new file mode 100644 index 000000000000..dde20487937d --- /dev/null +++ b/drivers/mtd/nand/onenand/onenand_bbt.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bad Block Table support for the OneNAND driver + * + * Copyright(c) 2005 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * Derived from nand_bbt.c + * + * TODO: + * Split BBT core and chip specific BBT. + */ + +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <linux/export.h> + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @param buf the buffer to search + * @param len the length of buffer to search + * @param paglen the pagelength + * @param td search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check and the pattern is expected to start + * at offset 0. + * + */ +static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + return 0; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @param mtd MTD device structure + * @param buf temporary buffer + * @param bd descriptor for the good/bad block search pattern + * @param chip create the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device + * for the given good/bad block identify pattern + */ +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int i, j, numblocks, len, scanlen; + int startblock; + loff_t from; + size_t readlen, ooblen; + struct mtd_oob_ops ops; + int rgn; + + printk(KERN_INFO "Scanning device for bad blocks\n"); + + len = 2; + + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + + /* chip == -1 case only */ + /* Note that numblocks is 2 * (real numblocks) here; + * see i += 2 below as it makses shifting and masking less painful + */ + numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); + startblock = 0; + from = 0; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooblen = readlen; + ops.oobbuf = buf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + + for (i = startblock; i < numblocks; ) { + int ret; + + for (j = 0; j < len; j++) { + /* No need to read pages fully, + * just read required OOB bytes */ + ret = onenand_bbt_read_oob(mtd, + from + j * this->writesize + bd->offs, &ops); + + /* If it is a initial bad block, just ignore it */ + if (ret == ONENAND_BBT_READ_FATAL_ERROR) + return -EIO; + + if (ret || check_short_pattern(&buf[j * scanlen], + scanlen, this->writesize, bd)) { + bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk(KERN_INFO "OneNAND eraseblock %d is an " + "initial bad block\n", i >> 1); + mtd->ecc_stats.badblocks++; + break; + } + } + i += 2; + + if (FLEXONENAND(this)) { + rgn = flexonenand_region(mtd, from); + from += mtd->eraseregions[rgn].erasesize; + } else + from += (1 << bbm->bbt_erase_shift); + } + + return 0; +} + + +/** + * onenand_memory_bbt - [GENERIC] create a memory based bad block table + * @param mtd MTD device structure + * @param bd descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device + * for manufacturer / software marked good / bad blocks + */ +static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct onenand_chip *this = mtd->priv; + + return create_bbt(mtd, this->page_buf, bd, -1); +} + +/** + * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad + * @param mtd MTD device structure + * @param offs offset in the device + * @param allowbbt allow access to bad block table region + */ +static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int) (onenand_block(this, offs) << 1); + res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int) offs, block >> 1, res); + + switch ((int) res) { + case 0x00: return 0; + case 0x01: return 1; + case 0x02: return allowbbt ? 0 : 1; + } + + return 1; +} + +/** + * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s) + * @param mtd MTD device structure + * @param bd descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It is freed + * by the onenand_release function. + * + */ +static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int len, ret = 0; + + len = this->chipsize >> (this->erase_shift + 2); + /* Allocate memory (2bit per block) and clear the memory bad block table */ + bbm->bbt = kzalloc(len, GFP_KERNEL); + if (!bbm->bbt) + return -ENOMEM; + + /* Set the bad block position */ + bbm->badblockpos = ONENAND_BADBLOCK_POS; + + /* Set erase shift */ + bbm->bbt_erase_shift = this->erase_shift; + + if (!bbm->isbad_bbt) + bbm->isbad_bbt = onenand_isbad_bbt; + + /* Scan the device to build a memory based bad block table */ + if ((ret = onenand_memory_bbt(mtd, bd))) { + printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree(bbm->bbt); + bbm->bbt = NULL; + } + + return ret; +} + +/* + * Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern, +}; + +/** + * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device + * @param mtd MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the onenand_scan_bbt function + */ +int onenand_default_bbt(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm; + + this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL); + if (!this->bbm) + return -ENOMEM; + + bbm = this->bbm; + + /* 1KB page has same configuration as 2KB page */ + if (!bbm->badblock_pattern) + bbm->badblock_pattern = &largepage_memorybased; + + return onenand_scan_bbt(mtd, bbm->badblock_pattern); +} diff --git a/drivers/mtd/nand/onenand/samsung.c b/drivers/mtd/nand/onenand/samsung.c new file mode 100644 index 000000000000..2e9d076e445a --- /dev/null +++ b/drivers/mtd/nand/onenand/samsung.c @@ -0,0 +1,1012 @@ +/* + * Samsung S3C64XX/S5PC1XX OneNAND driver + * + * Copyright © 2008-2010 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * Marek Szyprowski <m.szyprowski@samsung.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. + * + * Implementation: + * S3C64XX: emulate the pseudo BufferRAM + * S5PC110: use DMA + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <linux/mtd/partitions.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include "samsung.h" + +enum soc_type { + TYPE_S3C6400, + TYPE_S3C6410, + TYPE_S5PC110, +}; + +#define ONENAND_ERASE_STATUS 0x00 +#define ONENAND_MULTI_ERASE_SET 0x01 +#define ONENAND_ERASE_START 0x03 +#define ONENAND_UNLOCK_START 0x08 +#define ONENAND_UNLOCK_END 0x09 +#define ONENAND_LOCK_START 0x0A +#define ONENAND_LOCK_END 0x0B +#define ONENAND_LOCK_TIGHT_START 0x0C +#define ONENAND_LOCK_TIGHT_END 0x0D +#define ONENAND_UNLOCK_ALL 0x0E +#define ONENAND_OTP_ACCESS 0x12 +#define ONENAND_SPARE_ACCESS_ONLY 0x13 +#define ONENAND_MAIN_ACCESS_ONLY 0x14 +#define ONENAND_ERASE_VERIFY 0x15 +#define ONENAND_MAIN_SPARE_ACCESS 0x16 +#define ONENAND_PIPELINE_READ 0x4000 + +#define MAP_00 (0x0) +#define MAP_01 (0x1) +#define MAP_10 (0x2) +#define MAP_11 (0x3) + +#define S3C64XX_CMD_MAP_SHIFT 24 + +#define S3C6400_FBA_SHIFT 10 +#define S3C6400_FPA_SHIFT 4 +#define S3C6400_FSA_SHIFT 2 + +#define S3C6410_FBA_SHIFT 12 +#define S3C6410_FPA_SHIFT 6 +#define S3C6410_FSA_SHIFT 4 + +/* S5PC110 specific definitions */ +#define S5PC110_DMA_SRC_ADDR 0x400 +#define S5PC110_DMA_SRC_CFG 0x404 +#define S5PC110_DMA_DST_ADDR 0x408 +#define S5PC110_DMA_DST_CFG 0x40C +#define S5PC110_DMA_TRANS_SIZE 0x414 +#define S5PC110_DMA_TRANS_CMD 0x418 +#define S5PC110_DMA_TRANS_STATUS 0x41C +#define S5PC110_DMA_TRANS_DIR 0x420 +#define S5PC110_INTC_DMA_CLR 0x1004 +#define S5PC110_INTC_ONENAND_CLR 0x1008 +#define S5PC110_INTC_DMA_MASK 0x1024 +#define S5PC110_INTC_ONENAND_MASK 0x1028 +#define S5PC110_INTC_DMA_PEND 0x1044 +#define S5PC110_INTC_ONENAND_PEND 0x1048 +#define S5PC110_INTC_DMA_STATUS 0x1064 +#define S5PC110_INTC_ONENAND_STATUS 0x1068 + +#define S5PC110_INTC_DMA_TD (1 << 24) +#define S5PC110_INTC_DMA_TE (1 << 16) + +#define S5PC110_DMA_CFG_SINGLE (0x0 << 16) +#define S5PC110_DMA_CFG_4BURST (0x2 << 16) +#define S5PC110_DMA_CFG_8BURST (0x3 << 16) +#define S5PC110_DMA_CFG_16BURST (0x4 << 16) + +#define S5PC110_DMA_CFG_INC (0x0 << 8) +#define S5PC110_DMA_CFG_CNT (0x1 << 8) + +#define S5PC110_DMA_CFG_8BIT (0x0 << 0) +#define S5PC110_DMA_CFG_16BIT (0x1 << 0) +#define S5PC110_DMA_CFG_32BIT (0x2 << 0) + +#define S5PC110_DMA_SRC_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) +#define S5PC110_DMA_DST_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_SRC_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_DST_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) + +#define S5PC110_DMA_TRANS_CMD_TDC (0x1 << 18) +#define S5PC110_DMA_TRANS_CMD_TEC (0x1 << 16) +#define S5PC110_DMA_TRANS_CMD_TR (0x1 << 0) + +#define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18) +#define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17) +#define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16) + +#define S5PC110_DMA_DIR_READ 0x0 +#define S5PC110_DMA_DIR_WRITE 0x1 + +struct s3c_onenand { + struct mtd_info *mtd; + struct platform_device *pdev; + enum soc_type type; + void __iomem *base; + void __iomem *ahb_addr; + int bootram_command; + void *page_buf; + void *oob_buf; + unsigned int (*mem_addr)(int fba, int fpa, int fsa); + unsigned int (*cmd_map)(unsigned int type, unsigned int val); + void __iomem *dma_addr; + unsigned long phys_base; + struct completion complete; +}; + +#define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) +#define CMD_MAP_01(dev, mem_addr) (dev->cmd_map(MAP_01, (mem_addr))) +#define CMD_MAP_10(dev, mem_addr) (dev->cmd_map(MAP_10, (mem_addr))) +#define CMD_MAP_11(dev, addr) (dev->cmd_map(MAP_11, ((addr) << 2))) + +static struct s3c_onenand *onenand; + +static inline int s3c_read_reg(int offset) +{ + return readl(onenand->base + offset); +} + +static inline void s3c_write_reg(int value, int offset) +{ + writel(value, onenand->base + offset); +} + +static inline int s3c_read_cmd(unsigned int cmd) +{ + return readl(onenand->ahb_addr + cmd); +} + +static inline void s3c_write_cmd(int value, unsigned int cmd) +{ + writel(value, onenand->ahb_addr + cmd); +} + +#ifdef SAMSUNG_DEBUG +static void s3c_dump_reg(void) +{ + int i; + + for (i = 0; i < 0x400; i += 0x40) { + printk(KERN_INFO "0x%08X: 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int) onenand->base + i, + s3c_read_reg(i), s3c_read_reg(i + 0x10), + s3c_read_reg(i + 0x20), s3c_read_reg(i + 0x30)); + } +} +#endif + +static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val) +{ + return (type << S3C64XX_CMD_MAP_SHIFT) | val; +} + +static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6400_FBA_SHIFT) | (fpa << S3C6400_FPA_SHIFT) | + (fsa << S3C6400_FSA_SHIFT); +} + +static unsigned int s3c6410_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6410_FBA_SHIFT) | (fpa << S3C6410_FPA_SHIFT) | + (fsa << S3C6410_FSA_SHIFT); +} + +static void s3c_onenand_reset(void) +{ + unsigned long timeout = 0x10000; + int stat; + + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + while (1 && timeout--) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & RST_CMP) + break; + } + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* Clear interrupt */ + s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); + /* Clear the ECC status */ + s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET); +} + +static unsigned short s3c_onenand_readw(void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + int reg = addr - this->base; + int word_addr = reg >> 1; + int value; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_MANUFACTURER_ID: + return s3c_read_reg(MANUFACT_ID_OFFSET); + case ONENAND_REG_DEVICE_ID: + return s3c_read_reg(DEVICE_ID_OFFSET); + case ONENAND_REG_VERSION_ID: + return s3c_read_reg(FLASH_VER_ID_OFFSET); + case ONENAND_REG_DATA_BUFFER_SIZE: + return s3c_read_reg(DATA_BUF_SIZE_OFFSET); + case ONENAND_REG_TECHNOLOGY: + return s3c_read_reg(TECH_OFFSET); + case ONENAND_REG_SYS_CFG1: + return s3c_read_reg(MEM_CFG_OFFSET); + + /* Used at unlock all status */ + case ONENAND_REG_CTRL_STATUS: + return 0; + + case ONENAND_REG_WP_STATUS: + return ONENAND_WP_US; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int) addr < ONENAND_DATARAM && onenand->bootram_command) { + if (word_addr == 0) + return s3c_read_reg(MANUFACT_ID_OFFSET); + if (word_addr == 1) + return s3c_read_reg(DEVICE_ID_OFFSET); + if (word_addr == 2) + return s3c_read_reg(FLASH_VER_ID_OFFSET); + } + + value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff; + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + return value; +} + +static void s3c_onenand_writew(unsigned short value, void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int reg = addr - this->base; + unsigned int word_addr = reg >> 1; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_SYS_CFG1: + s3c_write_reg(value, MEM_CFG_OFFSET); + return; + + case ONENAND_REG_START_ADDRESS1: + case ONENAND_REG_START_ADDRESS2: + return; + + /* Lock/lock-tight/unlock/unlock_all */ + case ONENAND_REG_START_BLOCK_ADDRESS: + return; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int)addr < ONENAND_DATARAM) { + if (value == ONENAND_CMD_READID) { + onenand->bootram_command = 1; + return; + } + if (value == ONENAND_CMD_RESET) { + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + onenand->bootram_command = 0; + return; + } + } + + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + + s3c_write_cmd(value, CMD_MAP_11(onenand, word_addr)); +} + +static int s3c_onenand_wait(struct mtd_info *mtd, int state) +{ + struct device *dev = &onenand->pdev->dev; + unsigned int flags = INT_ACT; + unsigned int stat, ecc; + unsigned long timeout; + + switch (state) { + case FL_READING: + flags |= BLK_RW_CMP | LOAD_CMP; + break; + case FL_WRITING: + flags |= BLK_RW_CMP | PGM_CMP; + break; + case FL_ERASING: + flags |= BLK_RW_CMP | ERS_CMP; + break; + case FL_LOCKING: + flags |= BLK_RW_CMP; + break; + default: + break; + } + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + + if (state != FL_READING) + cond_resched(); + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* + * In the Spec. it checks the controller status first + * However if you get the correct information in case of + * power off recovery (POR) test, it should read ECC status first + */ + if (stat & LOAD_CMP) { + ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + dev_info(dev, "%s: ECC error = 0x%04x\n", __func__, + ecc); + mtd->ecc_stats.failed++; + return -EBADMSG; + } + } + + if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { + dev_info(dev, "%s: controller error = 0x%04x\n", __func__, + stat); + if (stat & LOCKED_BLK) + dev_info(dev, "%s: it's locked error = 0x%04x\n", + __func__, stat); + + return -EIO; + } + + return 0; +} + +static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + unsigned int *m, *s; + int fba, fpa, fsa = 0; + unsigned int mem_addr, cmd_map_01, cmd_map_10; + int i, mcount, scount; + int index; + + fba = (int) (addr >> this->erase_shift); + fpa = (int) (addr >> this->page_shift); + fpa &= this->page_mask; + + mem_addr = onenand->mem_addr(fba, fpa, fsa); + cmd_map_01 = CMD_MAP_01(onenand, mem_addr); + cmd_map_10 = CMD_MAP_10(onenand, mem_addr); + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + case ONENAND_CMD_BUFFERRAM: + ONENAND_SET_NEXT_BUFFERRAM(this); + default: + break; + } + + index = ONENAND_CURRENT_BUFFERRAM(this); + + /* + * Emulate Two BufferRAMs and access with 4 bytes pointer + */ + m = onenand->page_buf; + s = onenand->oob_buf; + + if (index) { + m += (this->writesize >> 2); + s += (mtd->oobsize >> 2); + } + + mcount = mtd->writesize >> 2; + scount = mtd->oobsize >> 2; + + switch (cmd) { + case ONENAND_CMD_READ: + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + return 0; + + case ONENAND_CMD_READOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + *s++ = s3c_read_cmd(cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_PROG: + /* Main */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(*m++, cmd_map_01); + return 0; + + case ONENAND_CMD_PROGOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + + /* Main - dummy write */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(0xffffffff, cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + s3c_write_cmd(*s++, cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_UNLOCK_ALL: + s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10); + return 0; + + case ONENAND_CMD_ERASE: + s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10); + return 0; + + default: + break; + } + + return 0; +} + +static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + int index = ONENAND_CURRENT_BUFFERRAM(this); + unsigned char *p; + + if (area == ONENAND_DATARAM) { + p = onenand->page_buf; + if (index == 1) + p += this->writesize; + } else { + p = onenand->oob_buf; + if (index == 1) + p += mtd->oobsize; + } + + return p; +} + +static int onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(buffer, p + offset, count); + return 0; +} + +static int onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(p + offset, buffer, count); + return 0; +} + +static int (*s5pc110_dma_ops)(dma_addr_t dst, dma_addr_t src, size_t count, int direction); + +static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int direction) +{ + void __iomem *base = onenand->dma_addr; + int status; + unsigned long timeout; + + writel(src, base + S5PC110_DMA_SRC_ADDR); + writel(dst, base + S5PC110_DMA_DST_ADDR); + + if (direction == S5PC110_DMA_DIR_READ) { + writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); + } else { + writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); + } + + writel(count, base + S5PC110_DMA_TRANS_SIZE); + writel(direction, base + S5PC110_DMA_TRANS_DIR); + + writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); + + /* + * There's no exact timeout values at Spec. + * In real case it takes under 1 msec. + * So 20 msecs are enough. + */ + timeout = jiffies + msecs_to_jiffies(20); + + do { + status = readl(base + S5PC110_DMA_TRANS_STATUS); + if (status & S5PC110_DMA_TRANS_STATUS_TE) { + writel(S5PC110_DMA_TRANS_CMD_TEC, + base + S5PC110_DMA_TRANS_CMD); + return -EIO; + } + } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) && + time_before(jiffies, timeout)); + + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); + + return 0; +} + +static irqreturn_t s5pc110_onenand_irq(int irq, void *data) +{ + void __iomem *base = onenand->dma_addr; + int status, cmd = 0; + + status = readl(base + S5PC110_INTC_DMA_STATUS); + + if (likely(status & S5PC110_INTC_DMA_TD)) + cmd = S5PC110_DMA_TRANS_CMD_TDC; + + if (unlikely(status & S5PC110_INTC_DMA_TE)) + cmd = S5PC110_DMA_TRANS_CMD_TEC; + + writel(cmd, base + S5PC110_DMA_TRANS_CMD); + writel(status, base + S5PC110_INTC_DMA_CLR); + + if (!onenand->complete.done) + complete(&onenand->complete); + + return IRQ_HANDLED; +} + +static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int direction) +{ + void __iomem *base = onenand->dma_addr; + int status; + + status = readl(base + S5PC110_INTC_DMA_MASK); + if (status) { + status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE); + writel(status, base + S5PC110_INTC_DMA_MASK); + } + + writel(src, base + S5PC110_DMA_SRC_ADDR); + writel(dst, base + S5PC110_DMA_DST_ADDR); + + if (direction == S5PC110_DMA_DIR_READ) { + writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); + } else { + writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); + } + + writel(count, base + S5PC110_DMA_TRANS_SIZE); + writel(direction, base + S5PC110_DMA_TRANS_DIR); + + writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); + + wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); + + return 0; +} + +static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *p; + void *buf = (void *) buffer; + dma_addr_t dma_src, dma_dst; + int err, ofs, page_dma = 0; + struct device *dev = &onenand->pdev->dev; + + p = this->base + area; + if (ONENAND_CURRENT_BUFFERRAM(this)) { + if (area == ONENAND_DATARAM) + p += this->writesize; + else + p += mtd->oobsize; + } + + if (offset & 3 || (size_t) buf & 3 || + !onenand->dma_addr || count != mtd->writesize) + goto normal; + + /* Handle vmalloc address */ + if (buf >= high_memory) { + struct page *page; + + if (((size_t) buf & PAGE_MASK) != + ((size_t) (buf + count - 1) & PAGE_MASK)) + goto normal; + page = vmalloc_to_page(buf); + if (!page) + goto normal; + + /* Page offset */ + ofs = ((size_t) buf & ~PAGE_MASK); + page_dma = 1; + + /* DMA routine */ + dma_src = onenand->phys_base + (p - this->base); + dma_dst = dma_map_page(dev, page, ofs, count, DMA_FROM_DEVICE); + } else { + /* DMA routine */ + dma_src = onenand->phys_base + (p - this->base); + dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE); + } + if (dma_mapping_error(dev, dma_dst)) { + dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count); + goto normal; + } + err = s5pc110_dma_ops(dma_dst, dma_src, + count, S5PC110_DMA_DIR_READ); + + if (page_dma) + dma_unmap_page(dev, dma_dst, count, DMA_FROM_DEVICE); + else + dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE); + + if (!err) + return 0; + +normal: + if (count != mtd->writesize) { + /* Copy the bufferram to memory to prevent unaligned access */ + memcpy(this->page_buf, p, mtd->writesize); + p = this->page_buf + offset; + } + + memcpy(buffer, p, count); + + return 0; +} + +static int s5pc110_chip_probe(struct mtd_info *mtd) +{ + /* Now just return 0 */ + return 0; +} + +static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + unsigned int flags = INT_ACT | LOAD_CMP; + unsigned int stat; + unsigned long timeout; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + if (stat & LD_FAIL_ECC_ERR) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + + if (stat & LOAD_CMP) { + int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + } + + return 0; +} + +static void s3c_onenand_check_lock_status(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int block, end; + int tmp; + + end = this->chipsize >> this->erase_shift; + + for (block = 0; block < end; block++) { + unsigned int mem_addr = onenand->mem_addr(block, 0, 0); + tmp = s3c_read_cmd(CMD_MAP_01(onenand, mem_addr)); + + if (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) { + dev_err(dev, "block %d is write-protected!\n", block); + s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET); + } + } +} + +static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, + size_t len, int cmd) +{ + struct onenand_chip *this = mtd->priv; + int start, end, start_mem_addr, end_mem_addr; + + start = ofs >> this->erase_shift; + start_mem_addr = onenand->mem_addr(start, 0, 0); + end = start + (len >> this->erase_shift) - 1; + end_mem_addr = onenand->mem_addr(end, 0, 0); + + if (cmd == ONENAND_CMD_LOCK) { + s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } else { + s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } + + this->wait(mtd, FL_LOCKING); +} + +static void s3c_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = this->chipsize; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* No need to check return value */ + this->wait(mtd, FL_LOCKING); + + /* Workaround for all block unlock in DDP */ + if (!ONENAND_IS_DDP(this)) { + s3c_onenand_check_lock_status(mtd); + return; + } + + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; + } + + s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + + s3c_onenand_check_lock_status(mtd); +} + +static void s3c_onenand_setup(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + onenand->mtd = mtd; + + if (onenand->type == TYPE_S3C6400) { + onenand->mem_addr = s3c6400_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S3C6410) { + onenand->mem_addr = s3c6410_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S5PC110) { + /* Use generic onenand functions */ + this->read_bufferram = s5pc110_read_bufferram; + this->chip_probe = s5pc110_chip_probe; + return; + } else { + BUG(); + } + + this->read_word = s3c_onenand_readw; + this->write_word = s3c_onenand_writew; + + this->wait = s3c_onenand_wait; + this->bbt_wait = s3c_onenand_bbt_wait; + this->unlock_all = s3c_unlock_all; + this->command = s3c_onenand_command; + + this->read_bufferram = onenand_read_bufferram; + this->write_bufferram = onenand_write_bufferram; +} + +static int s3c_onenand_probe(struct platform_device *pdev) +{ + struct onenand_platform_data *pdata; + struct onenand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int size, err; + + pdata = dev_get_platdata(&pdev->dev); + /* No need to check pdata. the platform data is optional */ + + size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); + mtd = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!mtd) + return -ENOMEM; + + onenand = devm_kzalloc(&pdev->dev, sizeof(struct s3c_onenand), + GFP_KERNEL); + if (!onenand) + return -ENOMEM; + + this = (struct onenand_chip *) &mtd[1]; + mtd->priv = this; + mtd->dev.parent = &pdev->dev; + onenand->pdev = pdev; + onenand->type = platform_get_device_id(pdev)->driver_data; + + s3c_onenand_setup(mtd); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + onenand->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(onenand->base)) + return PTR_ERR(onenand->base); + + onenand->phys_base = r->start; + + /* Set onenand_chip also */ + this->base = onenand->base; + + /* Use runtime badblock check */ + this->options |= ONENAND_SKIP_UNLOCK_CHECK; + + if (onenand->type != TYPE_S5PC110) { + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + onenand->ahb_addr = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(onenand->ahb_addr)) + return PTR_ERR(onenand->ahb_addr); + + /* Allocate 4KiB BufferRAM */ + onenand->page_buf = devm_kzalloc(&pdev->dev, SZ_4K, + GFP_KERNEL); + if (!onenand->page_buf) + return -ENOMEM; + + /* Allocate 128 SpareRAM */ + onenand->oob_buf = devm_kzalloc(&pdev->dev, 128, GFP_KERNEL); + if (!onenand->oob_buf) + return -ENOMEM; + + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + + } else { /* S5PC110 */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + onenand->dma_addr = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(onenand->dma_addr)) + return PTR_ERR(onenand->dma_addr); + + s5pc110_dma_ops = s5pc110_dma_poll; + /* Interrupt support */ + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (r) { + init_completion(&onenand->complete); + s5pc110_dma_ops = s5pc110_dma_irq; + err = devm_request_irq(&pdev->dev, r->start, + s5pc110_onenand_irq, + IRQF_SHARED, "onenand", + &onenand); + if (err) { + dev_err(&pdev->dev, "failed to get irq\n"); + return err; + } + } + } + + err = onenand_scan(mtd, 1); + if (err) + return err; + + if (onenand->type != TYPE_S5PC110) { + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + } + + if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) + dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); + + err = mtd_device_parse_register(mtd, NULL, NULL, + pdata ? pdata->parts : NULL, + pdata ? pdata->nr_parts : 0); + if (err) { + dev_err(&pdev->dev, "failed to parse partitions and register the MTD device\n"); + onenand_release(mtd); + return err; + } + + platform_set_drvdata(pdev, mtd); + + return 0; +} + +static int s3c_onenand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + + onenand_release(mtd); + + return 0; +} + +static int s3c_pm_ops_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + this->wait(mtd, FL_PM_SUSPENDED); + return 0; +} + +static int s3c_pm_ops_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + this->unlock_all(mtd); + return 0; +} + +static const struct dev_pm_ops s3c_pm_ops = { + .suspend = s3c_pm_ops_suspend, + .resume = s3c_pm_ops_resume, +}; + +static const struct platform_device_id s3c_onenand_driver_ids[] = { + { + .name = "s3c6400-onenand", + .driver_data = TYPE_S3C6400, + }, { + .name = "s3c6410-onenand", + .driver_data = TYPE_S3C6410, + }, { + .name = "s5pc110-onenand", + .driver_data = TYPE_S5PC110, + }, { }, +}; +MODULE_DEVICE_TABLE(platform, s3c_onenand_driver_ids); + +static struct platform_driver s3c_onenand_driver = { + .driver = { + .name = "samsung-onenand", + .pm = &s3c_pm_ops, + }, + .id_table = s3c_onenand_driver_ids, + .probe = s3c_onenand_probe, + .remove = s3c_onenand_remove, +}; + +module_platform_driver(s3c_onenand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); +MODULE_DESCRIPTION("Samsung OneNAND controller support"); diff --git a/drivers/mtd/nand/onenand/samsung.h b/drivers/mtd/nand/onenand/samsung.h new file mode 100644 index 000000000000..9016dc0136a8 --- /dev/null +++ b/drivers/mtd/nand/onenand/samsung.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008-2010 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.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 __SAMSUNG_ONENAND_H__ +#define __SAMSUNG_ONENAND_H__ + +/* + * OneNAND Controller + */ +#define MEM_CFG_OFFSET 0x0000 +#define BURST_LEN_OFFSET 0x0010 +#define MEM_RESET_OFFSET 0x0020 +#define INT_ERR_STAT_OFFSET 0x0030 +#define INT_ERR_MASK_OFFSET 0x0040 +#define INT_ERR_ACK_OFFSET 0x0050 +#define ECC_ERR_STAT_OFFSET 0x0060 +#define MANUFACT_ID_OFFSET 0x0070 +#define DEVICE_ID_OFFSET 0x0080 +#define DATA_BUF_SIZE_OFFSET 0x0090 +#define BOOT_BUF_SIZE_OFFSET 0x00A0 +#define BUF_AMOUNT_OFFSET 0x00B0 +#define TECH_OFFSET 0x00C0 +#define FBA_WIDTH_OFFSET 0x00D0 +#define FPA_WIDTH_OFFSET 0x00E0 +#define FSA_WIDTH_OFFSET 0x00F0 +#define TRANS_SPARE_OFFSET 0x0140 +#define DBS_DFS_WIDTH_OFFSET 0x0160 +#define INT_PIN_ENABLE_OFFSET 0x01A0 +#define ACC_CLOCK_OFFSET 0x01C0 +#define FLASH_VER_ID_OFFSET 0x01F0 +#define FLASH_AUX_CNTRL_OFFSET 0x0300 /* s3c64xx only */ + +#define ONENAND_MEM_RESET_HOT 0x3 +#define ONENAND_MEM_RESET_COLD 0x2 +#define ONENAND_MEM_RESET_WARM 0x1 + +#define CACHE_OP_ERR (1 << 13) +#define RST_CMP (1 << 12) +#define RDY_ACT (1 << 11) +#define INT_ACT (1 << 10) +#define UNSUP_CMD (1 << 9) +#define LOCKED_BLK (1 << 8) +#define BLK_RW_CMP (1 << 7) +#define ERS_CMP (1 << 6) +#define PGM_CMP (1 << 5) +#define LOAD_CMP (1 << 4) +#define ERS_FAIL (1 << 3) +#define PGM_FAIL (1 << 2) +#define INT_TO (1 << 1) +#define LD_FAIL_ECC_ERR (1 << 0) + +#define TSRF (1 << 0) + +#endif diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig new file mode 100644 index 000000000000..19a2b283fbbe --- /dev/null +++ b/drivers/mtd/nand/raw/Kconfig @@ -0,0 +1,537 @@ +config MTD_NAND_ECC + 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. + + +menuconfig MTD_NAND + tristate "Raw/Parallel NAND Device Support" + depends on MTD + select MTD_NAND_ECC + help + This enables support for accessing all type of raw/parallel + 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_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_MARVELL + tristate "NAND controller support on Marvell boards" + depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ + COMPILE_TEST + depends on HAS_IOMEM && HAS_DMA + 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 diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile new file mode 100644 index 000000000000..165b7ef9e9a1 --- /dev/null +++ b/drivers/mtd/nand/raw/Makefile @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0 + +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 + +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_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_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 diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index d60ada45c549..37a3cc21c7bc 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -1,11 +1,12 @@ /* - * drivers/mtd/nand/ams-delta.c - * * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> * - * Derived from drivers/mtd/toto.c + * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28) + * Copyright (c) 2003 Texas Instruments + * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> + * * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> - * Partially stolen from drivers/mtd/nand/plat_nand.c + * Partially stolen from 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 @@ -185,7 +186,7 @@ static int ams_delta_init(struct platform_device *pdev) /* 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"); + pr_warn("Unable to allocate E3 NAND MTD device structure.\n"); err = -ENOMEM; goto out; } @@ -219,7 +220,7 @@ static int ams_delta_init(struct platform_device *pdev) this->dev_ready = ams_delta_nand_ready; } else { this->dev_ready = NULL; - printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n"); + pr_notice("Couldn't request gpio for Delta NAND ready.\n"); } /* 25 us command delay time */ this->chip_delay = 30; diff --git a/drivers/mtd/nand/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile index 288db4f38a8f..288db4f38a8f 100644 --- a/drivers/mtd/nand/atmel/Makefile +++ b/drivers/mtd/nand/raw/atmel/Makefile diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index b2f00b398490..12f6753d47ae 100644 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -9,10 +9,10 @@ * * Copyright 2003 Rick Bronson * - * Derived from drivers/mtd/nand/autcpu12.c + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) * - * Derived from drivers/mtd/spia.c + * Derived from drivers/mtd/spia.c (removed in v3.8) * Copyright 2000 Steven J. Hill (sjhill@cotw.com) * * diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c index ca0a70389ba9..555a74e15269 100644 --- a/drivers/mtd/nand/atmel/pmecc.c +++ b/drivers/mtd/nand/raw/atmel/pmecc.c @@ -9,10 +9,10 @@ * * Copyright 2003 Rick Bronson * - * Derived from drivers/mtd/nand/autcpu12.c + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) * - * Derived from drivers/mtd/spia.c + * Derived from drivers/mtd/spia.c (removed in v3.8) * Copyright 2000 Steven J. Hill (sjhill@cotw.com) * * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h index 817e0dd9fd15..808f1be0d6ad 100644 --- a/drivers/mtd/nand/atmel/pmecc.h +++ b/drivers/mtd/nand/raw/atmel/pmecc.h @@ -9,10 +9,10 @@ * * Copyright © 2003 Rick Bronson * - * Derived from drivers/mtd/nand/autcpu12.c + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de) * - * Derived from drivers/mtd/spia.c + * Derived from drivers/mtd/spia.c (removed in v3.8) * Copyright © 2000 Steven J. Hill (sjhill@cotw.com) * * diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 8ab827edf94e..df0ef1f1e2f5 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/au1550nd.c - * * Copyright (C) 2004 Embedded Edge, LLC * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mtd/nand/bcm47xxnflash/Makefile b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile index f05b119e134b..f05b119e134b 100644 --- a/drivers/mtd/nand/bcm47xxnflash/Makefile +++ b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h index 201b9baa52a0..201b9baa52a0 100644 --- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h +++ b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/raw/bcm47xxnflash/main.c index fb31429b70a9..fb31429b70a9 100644 --- a/drivers/mtd/nand/bcm47xxnflash/main.c +++ b/drivers/mtd/nand/raw/bcm47xxnflash/main.c diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c index 54bac5b73f0a..60874de430eb 100644 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c @@ -392,8 +392,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) 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; + b47n->nand_chip.set_features = nand_get_set_features_notsupp; + b47n->nand_chip.get_features = nand_get_set_features_notsupp; nand_chip->chip_delay = 50; b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; diff --git a/drivers/mtd/nand/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile index 195b845e48b8..195b845e48b8 100644 --- a/drivers/mtd/nand/brcmnand/Makefile +++ b/drivers/mtd/nand/raw/brcmnand/Makefile diff --git a/drivers/mtd/nand/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c index 59444b3a697d..59444b3a697d 100644 --- a/drivers/mtd/nand/brcmnand/bcm63138_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c diff --git a/drivers/mtd/nand/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c index 34c91b0e1e69..34c91b0e1e69 100644 --- a/drivers/mtd/nand/brcmnand/bcm6368_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index c28fd2bc1a84..1306aaa7a8bf 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -2297,7 +2297,11 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) if (ret) return ret; - return mtd_device_register(mtd, NULL, 0); + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + nand_cleanup(chip); + + return ret; } static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h index 5c44cd4aba87..5c44cd4aba87 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.h +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h diff --git a/drivers/mtd/nand/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c index 5c271077ac87..5c271077ac87 100644 --- a/drivers/mtd/nand/brcmnand/brcmstb_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c index 4c6ae113664d..4c6ae113664d 100644 --- a/drivers/mtd/nand/brcmnand/iproc_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index 567ff972d5fc..d8c8c9d1e640 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -645,8 +645,8 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->nand.read_buf = cafe_read_buf; cafe->nand.write_buf = cafe_write_buf; cafe->nand.select_chip = cafe_select_chip; - cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp; - cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp; + cafe->nand.set_features = nand_get_set_features_notsupp; + cafe->nand.get_features = nand_get_set_features_notsupp; cafe->nand.chip_delay = 0; @@ -751,8 +751,8 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->nand.bbt_td = &cafe_bbt_main_descr_512; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; } else { - printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", - mtd->writesize); + pr_warn("Unexpected NAND flash writesize %d. Aborting\n", + mtd->writesize); goto out_free_dma; } cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; @@ -774,10 +774,14 @@ static int cafe_nand_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mtd); mtd->name = "cafe_nand"; - mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + if (err) + goto out_cleanup_nand; goto out; + out_cleanup_nand: + nand_cleanup(&cafe->nand); out_free_dma: dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); out_irq: diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/raw/cmx270_nand.c index b01c9804590e..02d6751e9efe 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/raw/cmx270_nand.c @@ -1,10 +1,8 @@ /* - * linux/drivers/mtd/nand/cmx270-nand.c - * * Copyright (C) 2006 Compulab, Ltd. * Mike Rapoport <mike@compulab.co.il> * - * Derived from drivers/mtd/nand/h1910.c + * Derived from drivers/mtd/nand/h1910.c (removed in v3.10) * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index d48877540f14..82269fde9e66 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/cs553x_nand.c - * * (C) 2005, 2006 Red Hat Inc. * * Author: David Woodhouse <dwmw2@infradead.org> @@ -189,10 +187,11 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) struct nand_chip *this; struct mtd_info *new_mtd; - printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr); + pr_notice("Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", + cs, mmio ? "MM" : "P", adr); if (!mmio) { - printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n"); + pr_notice("PIO mode not yet implemented for CS553X NAND controller\n"); return -ENXIO; } @@ -211,7 +210,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) /* map physical address */ this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096); if (!this->IO_ADDR_R) { - printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr); + pr_warn("ioremap cs553x NAND @0x%08lx failed\n", adr); err = -EIO; goto out_mtd; } @@ -295,7 +294,7 @@ static int __init cs553x_init(void) /* If it doesn't have the NAND controller enabled, abort */ rdmsrl(MSR_DIVIL_BALL_OPTS, val); if (val & PIN_OPT_IDE) { - printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n"); + pr_info("CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n"); return -ENXIO; } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index ccc8c43abcff..0f09518d980f 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -826,7 +826,7 @@ static int nand_davinci_probe(struct platform_device *pdev) else ret = mtd_device_register(mtd, NULL, 0); if (ret < 0) - goto err; + goto err_cleanup_nand; val = davinci_nand_readl(info, NRCSR_OFFSET); dev_info(&pdev->dev, "controller rev. %d.%d\n", @@ -834,6 +834,9 @@ static int nand_davinci_probe(struct platform_device *pdev) return 0; +err_cleanup_nand: + nand_cleanup(&info->chip); + err: clk_disable_unprepare(info->clk); diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/raw/denali.c index 313c7f50621b..2a302a1d1430 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1384,10 +1384,12 @@ int denali_init(struct denali_nand_info *denali) ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(denali->dev, "Failed to register MTD: %d\n", ret); - goto free_buf; + goto cleanup_nand; } return 0; +cleanup_nand: + nand_cleanup(chip); free_buf: kfree(denali->buf); disable_irq: diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/raw/denali.h index 9ad33d237378..9ad33d237378 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/raw/denali.h diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index cfd33e6ca77f..cfd33e6ca77f 100644 --- a/drivers/mtd/nand/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c index 49cb3e1f8bd0..49cb3e1f8bd0 100644 --- a/drivers/mtd/nand/denali_pci.c +++ b/drivers/mtd/nand/raw/denali_pci.c diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 6bc93ea66f50..86a258de0b75 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/diskonchip.c - * * (C) 2003 Red Hat, Inc. * (C) 2004 Dan Brown <dan_brown@ieee.org> * (C) 2004 Kalev Lember <kalev@smartlink.ee> @@ -411,7 +409,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) ident.dword = readl(docptr + DoC_2k_CDSN_IO); if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { - printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); + pr_info("DiskOnChip 2000 responds to DWORD access\n"); this->read_buf = &doc2000_readbuf_dword; } } @@ -438,7 +436,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd) break; } doc->chips_per_floor = i; - printk(KERN_DEBUG "Detected %d chips per floor.\n", i); + pr_debug("Detected %d chips per floor.\n", i); } static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) @@ -934,14 +932,15 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, ret = doc_ecc_decode(rs_decoder, dat, calc_ecc); if (ret > 0) - printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); + pr_err("doc200x_correct_data corrected %d errors\n", + ret); } if (DoC_is_MillenniumPlus(doc)) WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); else WriteDOC(DOC_ECC_DIS, docptr, ECCConf); if (no_ecc_failures && mtd_is_eccerr(ret)) { - printk(KERN_ERR "suppressing ECC failure\n"); + pr_err("suppressing ECC failure\n"); ret = 0; } return ret; @@ -1014,11 +1013,11 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch if (retlen != mtd->writesize) continue; if (ret) { - printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); + pr_warn("ECC error scanning DOC at 0x%x\n", offs); } if (memcmp(buf, id, 6)) continue; - printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); + pr_info("Found DiskOnChip %s Media Header at 0x%x\n", id, offs); if (doc->mh0_page == -1) { doc->mh0_page = offs >> this->page_shift; if (!findmirror) @@ -1029,7 +1028,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch return 2; } if (doc->mh0_page == -1) { - printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); + pr_warn("DiskOnChip %s Media Header not found.\n", id); return 0; } /* Only one mediaheader was found. We want buf to contain a @@ -1038,7 +1037,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); if (retlen != mtd->writesize) { /* Insanity. Give up. */ - printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); + pr_err("Read DiskOnChip Media Header once, but can't reread it???\n"); return 0; } return 1; @@ -1068,11 +1067,11 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio le16_to_cpus(&mh->FirstPhysicalEUN); le32_to_cpus(&mh->FormattedSize); - printk(KERN_INFO " DataOrgID = %s\n" - " NumEraseUnits = %d\n" - " FirstPhysicalEUN = %d\n" - " FormattedSize = %d\n" - " UnitSizeFactor = %d\n", + pr_info(" DataOrgID = %s\n" + " NumEraseUnits = %d\n" + " FirstPhysicalEUN = %d\n" + " FormattedSize = %d\n" + " UnitSizeFactor = %d\n", mh->DataOrgID, mh->NumEraseUnits, mh->FirstPhysicalEUN, mh->FormattedSize, mh->UnitSizeFactor); @@ -1092,7 +1091,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio maxblocks = min(32768U, (maxblocks << 1) + psize); mh->UnitSizeFactor--; } - printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); + pr_warn("UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); } /* NOTE: The lines below modify internal variables of the NAND and MTD @@ -1103,13 +1102,13 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio if (mh->UnitSizeFactor != 0xff) { this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); mtd->erasesize <<= (0xff - mh->UnitSizeFactor); - printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); + pr_info("Setting virtual erase size to %d\n", mtd->erasesize); blocks = mtd->size >> this->bbt_erase_shift; maxblocks = min(32768U, mtd->erasesize - psize); } if (blocks > maxblocks) { - printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); + pr_err("UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); goto out; } @@ -1180,14 +1179,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti le32_to_cpus(&mh->FormatFlags); le32_to_cpus(&mh->PercentUsed); - printk(KERN_INFO " bootRecordID = %s\n" - " NoOfBootImageBlocks = %d\n" - " NoOfBinaryPartitions = %d\n" - " NoOfBDTLPartitions = %d\n" - " BlockMultiplerBits = %d\n" - " FormatFlgs = %d\n" - " OsakVersion = %d.%d.%d.%d\n" - " PercentUsed = %d\n", + pr_info(" bootRecordID = %s\n" + " NoOfBootImageBlocks = %d\n" + " NoOfBinaryPartitions = %d\n" + " NoOfBDTLPartitions = %d\n" + " BlockMultiplerBits = %d\n" + " FormatFlgs = %d\n" + " OsakVersion = %d.%d.%d.%d\n" + " PercentUsed = %d\n", mh->bootRecordID, mh->NoOfBootImageBlocks, mh->NoOfBinaryPartitions, mh->NoOfBDTLPartitions, @@ -1202,13 +1201,13 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti blocks = mtd->size >> vshift; if (blocks > 32768) { - printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); + pr_err("BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); goto out; } blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); if (inftl_bbt_write && (blocks > mtd->erasesize)) { - printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); + pr_err("Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); goto out; } @@ -1222,7 +1221,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti le32_to_cpus(&ip->spareUnits); le32_to_cpus(&ip->Reserved0); - printk(KERN_INFO " PARTITION[%d] ->\n" + pr_info(" PARTITION[%d] ->\n" " virtualUnits = %d\n" " firstUnit = %d\n" " lastUnit = %d\n" @@ -1308,7 +1307,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) struct mtd_partition parts[5]; if (this->numchips > doc->chips_per_floor) { - printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); + pr_err("Multi-floor INFTL devices not yet supported.\n"); return -EIO; } @@ -1436,7 +1435,8 @@ static int __init doc_probe(unsigned long physadr) return -EBUSY; virtadr = ioremap(physadr, DOC_IOREMAP_LEN); if (!virtadr) { - printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); + pr_err("Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", + DOC_IOREMAP_LEN, physadr); ret = -EIO; goto error_ioremap; } @@ -1495,7 +1495,7 @@ static int __init doc_probe(unsigned long physadr) reg = DoC_Mplus_Toggle; break; case DOC_ChipID_DocMilPlus32: - printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); + pr_err("DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); default: ret = -ENODEV; goto notfound; @@ -1511,7 +1511,7 @@ static int __init doc_probe(unsigned long physadr) tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; if ((tmp == tmpb) || (tmp != tmpc)) { - printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); + pr_warn("Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); ret = -ENODEV; goto notfound; } @@ -1545,12 +1545,13 @@ static int __init doc_probe(unsigned long physadr) } newval = ~newval; if (oldval == newval) { - printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); + pr_debug("Found alias of DOC at 0x%lx to 0x%lx\n", + doc->physadr, physadr); goto notfound; } } - printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); + pr_notice("DiskOnChip found at 0x%lx\n", physadr); len = sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); @@ -1665,12 +1666,13 @@ static int __init init_nanddoc(void) */ rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); if (!rs_decoder) { - printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); + pr_err("DiskOnChip: Could not create a RS decoder\n"); return -ENOMEM; } if (doc_config_location) { - printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + pr_info("Using configured DiskOnChip probe address 0x%lx\n", + doc_config_location); ret = doc_probe(doc_config_location); if (ret < 0) goto outerr; @@ -1682,7 +1684,7 @@ static int __init init_nanddoc(void) /* No banner message any more. Print a message if no DiskOnChip found, so the user knows we at least tried. */ if (!doclist) { - printk(KERN_INFO "No valid DiskOnChip devices found\n"); + pr_info("No valid DiskOnChip devices found\n"); ret = -ENODEV; goto outerr; } diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/raw/docg4.c index 72f1327c4430..1314aa99b9ab 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/raw/docg4.c @@ -1269,8 +1269,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->read_buf = docg4_read_buf; nand->write_buf = docg4_write_buf16; nand->erase = docg4_erase_block; - nand->onfi_set_features = nand_onfi_get_set_features_notsupp; - nand->onfi_get_features = nand_onfi_get_set_features_notsupp; + nand->set_features = nand_get_set_features_notsupp; + nand->get_features = nand_get_set_features_notsupp; nand->ecc.read_page = docg4_read_page; nand->ecc.write_page = docg4_write_page; nand->ecc.read_page_raw = docg4_read_page_raw; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index 8b6dcd739ecb..d28df991c73c 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -775,8 +775,8 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->select_chip = fsl_elbc_select_chip; chip->cmdfunc = fsl_elbc_cmdfunc; chip->waitfunc = fsl_elbc_wait; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; @@ -929,8 +929,8 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev) mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); - printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n", - (unsigned long long)res.start, priv->bank); + pr_info("eLBC NAND device at 0x%llx, bank %d\n", + (unsigned long long)res.start, priv->bank); return 0; err: diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 5a9c2f0020c2..61aae0224078 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -799,7 +799,7 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) msecs_to_jiffies(IFC_TIMEOUT_MSECS)); if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) - printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); + pr_err("fsl-ifc: Failed to Initialise SRAM\n"); /* Restore CSOR and CSOR_ext */ ifc_out32(csor, &ifc_global->csor_cs[cs].csor); @@ -832,8 +832,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->select_chip = fsl_ifc_select_chip; chip->cmdfunc = fsl_ifc_cmdfunc; chip->waitfunc = fsl_ifc_wait; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c index a88e2cf66e0f..a88e2cf66e0f 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/raw/fsl_upm.c diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index f49ed46fa770..28c48dcc514e 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/fsmc_nand.c - * * ST Microelectronics * Flexible Static Memory Controller (FSMC) * Driver for NAND portions @@ -9,7 +7,9 @@ * Vipin Kumar <vipin.kumar@st.com> * Ashish Priyadarshi * - * Based on drivers/mtd/nand/nomadik_nand.c + * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8) + * Copyright © 2007 STMicroelectronics Pvt. Ltd. + * Copyright © 2009 Alessandro Rubini * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -103,10 +103,6 @@ #define ECC3 0x1C #define FSMC_NAND_BANK_SZ 0x20 -#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \ - (FSMC_NAND_BANK_SZ * (bank)) + \ - reg) - #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) struct fsmc_nand_timings { @@ -143,7 +139,7 @@ enum access_mode { * @data_va: NAND port for Data. * @cmd_va: NAND port for Command. * @addr_va: NAND port for Address. - * @regs_va: FSMC regs base address. + * @regs_va: Registers base address for a given bank. */ struct fsmc_nand_data { u32 pid; @@ -258,45 +254,6 @@ static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd) } /* - * fsmc_cmd_ctrl - For facilitaing Hardware access - * This routine allows hardware specific access to control-lines(ALE,CLE) - */ -static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - unsigned int bank = host->bank; - - if (ctrl & NAND_CTRL_CHANGE) { - u32 pc; - - if (ctrl & NAND_CLE) { - this->IO_ADDR_R = host->cmd_va; - this->IO_ADDR_W = host->cmd_va; - } else if (ctrl & NAND_ALE) { - this->IO_ADDR_R = host->addr_va; - this->IO_ADDR_W = host->addr_va; - } else { - this->IO_ADDR_R = host->data_va; - this->IO_ADDR_W = host->data_va; - } - - pc = readl(FSMC_NAND_REG(regs, bank, PC)); - if (ctrl & NAND_NCE) - pc |= FSMC_ENABLE; - else - pc &= ~FSMC_ENABLE; - writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC)); - } - - mb(); - - if (cmd != NAND_CMD_NONE) - writeb_relaxed(cmd, this->IO_ADDR_W); -} - -/* * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine * * This routine initializes timing parameters related to NAND memory access in @@ -307,8 +264,6 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host, { uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; uint32_t tclr, tar, thiz, thold, twait, tset; - unsigned int bank = host->bank; - void __iomem *regs = host->regs_va; tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; @@ -318,18 +273,14 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host, tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (host->nand.options & NAND_BUSWIDTH_16) - writel_relaxed(value | FSMC_DEVWID_16, - FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(value | FSMC_DEVWID_16, host->regs_va + PC); else - writel_relaxed(value | FSMC_DEVWID_8, - FSMC_NAND_REG(regs, bank, PC)); - - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(thiz | thold | twait | tset, - FSMC_NAND_REG(regs, bank, COMM)); - writel_relaxed(thiz | thold | twait | tset, - FSMC_NAND_REG(regs, bank, ATTRIB)); + writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + PC); + + writel_relaxed(readl(host->regs_va + PC) | tclr | tar, + host->regs_va + PC); + writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM); + writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB); } static int fsmc_calc_timings(struct fsmc_nand_data *host, @@ -419,15 +370,13 @@ static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline, static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) { struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; - - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, - FSMC_NAND_REG(regs, bank, PC)); + + writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCPLEN_256, + host->regs_va + PC); + writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCEN, + host->regs_va + PC); + writel_relaxed(readl(host->regs_va + PC) | FSMC_ECCEN, + host->regs_va + PC); } /* @@ -439,13 +388,11 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc) { struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; uint32_t ecc_tmp; unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; do { - if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) + if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY) break; else cond_resched(); @@ -456,25 +403,25 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, return -ETIMEDOUT; } - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); + ecc_tmp = readl_relaxed(host->regs_va + ECC1); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); ecc[3] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); + ecc_tmp = readl_relaxed(host->regs_va + ECC2); ecc[4] = (uint8_t) (ecc_tmp >> 0); ecc[5] = (uint8_t) (ecc_tmp >> 8); ecc[6] = (uint8_t) (ecc_tmp >> 16); ecc[7] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); + ecc_tmp = readl_relaxed(host->regs_va + ECC3); ecc[8] = (uint8_t) (ecc_tmp >> 0); ecc[9] = (uint8_t) (ecc_tmp >> 8); ecc[10] = (uint8_t) (ecc_tmp >> 16); ecc[11] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); + ecc_tmp = readl_relaxed(host->regs_va + STS); ecc[12] = (uint8_t) (ecc_tmp >> 16); return 0; @@ -489,11 +436,9 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc) { struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; uint32_t ecc_tmp; - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); + ecc_tmp = readl_relaxed(host->regs_va + ECC1); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); @@ -598,18 +543,18 @@ unmap_dma: */ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - struct nand_chip *chip = mtd_to_nand(mtd); if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && IS_ALIGNED(len, sizeof(uint32_t))) { uint32_t *p = (uint32_t *)buf; len = len >> 2; for (i = 0; i < len; i++) - writel_relaxed(p[i], chip->IO_ADDR_W); + writel_relaxed(p[i], host->data_va); } else { for (i = 0; i < len; i++) - writeb_relaxed(buf[i], chip->IO_ADDR_W); + writeb_relaxed(buf[i], host->data_va); } } @@ -621,18 +566,18 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) */ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - struct nand_chip *chip = mtd_to_nand(mtd); if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && IS_ALIGNED(len, sizeof(uint32_t))) { uint32_t *p = (uint32_t *)buf; len = len >> 2; for (i = 0; i < len; i++) - p[i] = readl_relaxed(chip->IO_ADDR_R); + p[i] = readl_relaxed(host->data_va); } else { for (i = 0; i < len; i++) - buf[i] = readb_relaxed(chip->IO_ADDR_R); + buf[i] = readb_relaxed(host->data_va); } } @@ -663,6 +608,102 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); } +/* fsmc_select_chip - assert or deassert nCE */ +static void fsmc_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + u32 pc; + + /* Support only one CS */ + if (chipnr > 0) + return; + + pc = readl(host->regs_va + PC); + if (chipnr < 0) + writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + PC); + else + writel_relaxed(pc | FSMC_ENABLE, host->regs_va + PC); + + /* nCE line must be asserted before starting any operation */ + mb(); +} + +/* + * fsmc_exec_op - hook called by the core to execute NAND operations + * + * This controller is simple enough and thus does not need to use the parser + * provided by the core, instead, handle every situation here. + */ +static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, + bool check_only) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + const struct nand_op_instr *instr = NULL; + int ret = 0; + unsigned int op_id; + int i; + + pr_debug("Executing operation [%d instructions]:\n", op->ninstrs); + for (op_id = 0; op_id < op->ninstrs; op_id++) { + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + pr_debug(" ->CMD [0x%02x]\n", + instr->ctx.cmd.opcode); + + writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va); + break; + + case NAND_OP_ADDR_INSTR: + pr_debug(" ->ADDR [%d cyc]", + instr->ctx.addr.naddrs); + + for (i = 0; i < instr->ctx.addr.naddrs; i++) + writeb_relaxed(instr->ctx.addr.addrs[i], + host->addr_va); + break; + + case NAND_OP_DATA_IN_INSTR: + pr_debug(" ->DATA_IN [%d B%s]\n", instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + + if (host->mode == USE_DMA_ACCESS) + fsmc_read_buf_dma(mtd, instr->ctx.data.buf.in, + instr->ctx.data.len); + else + fsmc_read_buf(mtd, instr->ctx.data.buf.in, + instr->ctx.data.len); + break; + + case NAND_OP_DATA_OUT_INSTR: + pr_debug(" ->DATA_OUT [%d B%s]\n", instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + + if (host->mode == USE_DMA_ACCESS) + fsmc_write_buf_dma(mtd, instr->ctx.data.buf.out, + instr->ctx.data.len); + else + fsmc_write_buf(mtd, instr->ctx.data.buf.out, + instr->ctx.data.len); + break; + + case NAND_OP_WAITRDY_INSTR: + pr_debug(" ->WAITRDY [max %d ms]\n", + instr->ctx.waitrdy.timeout_ms); + + ret = nand_soft_waitrdy(chip, + instr->ctx.waitrdy.timeout_ms); + break; + } + } + + return ret; +} + /* * fsmc_read_page_hwecc * @mtd: mtd info structure @@ -754,13 +795,11 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, { struct nand_chip *chip = mtd_to_nand(mtd); struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - unsigned int bank = host->bank; uint32_t err_idx[8]; uint32_t num_err, i; uint32_t ecc1, ecc2, ecc3, ecc4; - num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; + num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF; /* no bit flipping */ if (likely(num_err == 0)) @@ -803,10 +842,10 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, * uint64_t array and error offset indexes are populated in err_idx * array */ - ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); - ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); - ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); - ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); + ecc1 = readl_relaxed(host->regs_va + ECC1); + ecc2 = readl_relaxed(host->regs_va + ECC2); + ecc3 = readl_relaxed(host->regs_va + ECC3); + ecc4 = readl_relaxed(host->regs_va + STS); err_idx[0] = (ecc1 >> 0) & 0x1FFF; err_idx[1] = (ecc1 >> 13) & 0x1FFF; @@ -889,6 +928,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) struct mtd_info *mtd; struct nand_chip *nand; struct resource *res; + void __iomem *base; dma_cap_mask_t mask; int ret = 0; u32 pid; @@ -923,9 +963,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return PTR_ERR(host->cmd_va); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - host->regs_va = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->regs_va)) - return PTR_ERR(host->regs_va); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + host->regs_va = base + FSMC_NOR_REG_SIZE + + (host->bank * FSMC_NAND_BANK_SZ); host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { @@ -942,7 +985,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * AMBA PrimeCell bus. However it is not a PrimeCell. */ for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(host->regs_va + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); + pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); host->pid = pid; dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, " "revision %02x, config %02x\n", @@ -960,9 +1003,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand_set_flash_node(nand, pdev->dev.of_node); mtd->dev.parent = &pdev->dev; - nand->IO_ADDR_R = host->data_va; - nand->IO_ADDR_W = host->data_va; - nand->cmd_ctrl = fsmc_cmd_ctrl; + nand->exec_op = fsmc_exec_op; + nand->select_chip = fsmc_select_chip; nand->chip_delay = 30; /* @@ -974,8 +1016,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.size = 512; nand->badblockbits = 7; - switch (host->mode) { - case USE_DMA_ACCESS: + if (host->mode == USE_DMA_ACCESS) { dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); host->read_dma_chan = dma_request_channel(mask, filter, NULL); @@ -988,15 +1029,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get write dma channel\n"); goto err_req_write_chnl; } - nand->read_buf = fsmc_read_buf_dma; - nand->write_buf = fsmc_write_buf_dma; - break; - - default: - case USE_WORD_ACCESS: - nand->read_buf = fsmc_read_buf; - nand->write_buf = fsmc_write_buf; - break; } if (host->dev_timings) diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/raw/gpio.c index a8bde6665c24..2780af26d9ab 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/raw/gpio.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/gpio.c - * * Updated, and converted to generic GPIO based driver by Russell King. * * Written by Ben Dooks <ben@simtec.co.uk> diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/raw/gpmi-nand/Makefile index 3a462487c35e..3a462487c35e 100644 --- a/drivers/mtd/nand/gpmi-nand/Makefile +++ b/drivers/mtd/nand/raw/gpmi-nand/Makefile diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h index 05bb91f2f4c4..05bb91f2f4c4 100644 --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h +++ b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 97787246af41..e94556705dc7 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -26,15 +26,8 @@ #include "gpmi-regs.h" #include "bch-regs.h" -static struct timing_threshold timing_default_threshold = { - .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> - BP_GPMI_TIMING0_DATA_SETUP), - .internal_data_setup_in_ns = 0, - .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> - BP_GPMI_CTRL1_RDN_DELAY), - .max_dll_clock_period_in_ns = 32, - .max_dll_delay_in_ns = 16, -}; +/* Converts time to clock cycles */ +#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period) #define MXS_SET_ADDR 0x4 #define MXS_CLR_ADDR 0x8 @@ -151,8 +144,15 @@ err_clk: return ret; } -#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) -#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) +int gpmi_enable_clk(struct gpmi_nand_data *this) +{ + return __gpmi_enable_clk(this, true); +} + +int gpmi_disable_clk(struct gpmi_nand_data *this) +{ + return __gpmi_enable_clk(this, false); +} int gpmi_init(struct gpmi_nand_data *this) { @@ -174,7 +174,6 @@ int gpmi_init(struct gpmi_nand_data *this) if (ret) goto err_out; - /* Choose NAND mode. */ writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); @@ -313,467 +312,6 @@ err_out: return ret; } -/* Converts time in nanoseconds to cycles. */ -static unsigned int ns_to_cycles(unsigned int time, - unsigned int period, unsigned int min) -{ - unsigned int k; - - k = (time + period - 1) / period; - return max(k, min); -} - -#define DEF_MIN_PROP_DELAY 5 -#define DEF_MAX_PROP_DELAY 9 -/* Apply timing to current hardware conditions. */ -static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, - struct gpmi_nfc_hardware_timing *hw) -{ - struct timing_threshold *nfc = &timing_default_threshold; - struct resources *r = &this->resources; - struct nand_chip *nand = &this->nand; - struct nand_timing target = this->timing; - bool improved_timing_is_available; - unsigned long clock_frequency_in_hz; - unsigned int clock_period_in_ns; - bool dll_use_half_periods; - unsigned int dll_delay_shift; - unsigned int max_sample_delay_in_ns; - unsigned int address_setup_in_cycles; - unsigned int data_setup_in_ns; - unsigned int data_setup_in_cycles; - unsigned int data_hold_in_cycles; - int ideal_sample_delay_in_ns; - unsigned int sample_delay_factor; - int tEYE; - unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; - unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; - - /* - * If there are multiple chips, we need to relax the timings to allow - * for signal distortion due to higher capacitance. - */ - if (nand->numchips > 2) { - target.data_setup_in_ns += 10; - target.data_hold_in_ns += 10; - target.address_setup_in_ns += 10; - } else if (nand->numchips > 1) { - target.data_setup_in_ns += 5; - target.data_hold_in_ns += 5; - target.address_setup_in_ns += 5; - } - - /* Check if improved timing information is available. */ - improved_timing_is_available = - (target.tREA_in_ns >= 0) && - (target.tRLOH_in_ns >= 0) && - (target.tRHOH_in_ns >= 0); - - /* Inspect the clock. */ - nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]); - clock_frequency_in_hz = nfc->clock_frequency_in_hz; - clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz; - - /* - * The NFC quantizes setup and hold parameters in terms of clock cycles. - * Here, we quantize the setup and hold timing parameters to the - * next-highest clock period to make sure we apply at least the - * specified times. - * - * For data setup and data hold, the hardware interprets a value of zero - * as the largest possible delay. This is not what's intended by a zero - * in the input parameter, so we impose a minimum of one cycle. - */ - data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, - clock_period_in_ns, 1); - data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, - clock_period_in_ns, 1); - address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, - clock_period_in_ns, 0); - - /* - * The clock's period affects the sample delay in a number of ways: - * - * (1) The NFC HAL tells us the maximum clock period the sample delay - * DLL can tolerate. If the clock period is greater than half that - * maximum, we must configure the DLL to be driven by half periods. - * - * (2) We need to convert from an ideal sample delay, in ns, to a - * "sample delay factor," which the NFC uses. This factor depends on - * whether we're driving the DLL with full or half periods. - * Paraphrasing the reference manual: - * - * AD = SDF x 0.125 x RP - * - * where: - * - * AD is the applied delay, in ns. - * SDF is the sample delay factor, which is dimensionless. - * RP is the reference period, in ns, which is a full clock period - * if the DLL is being driven by full periods, or half that if - * the DLL is being driven by half periods. - * - * Let's re-arrange this in a way that's more useful to us: - * - * 8 - * SDF = AD x ---- - * RP - * - * The reference period is either the clock period or half that, so this - * is: - * - * 8 AD x DDF - * SDF = AD x ----- = -------- - * f x P P - * - * where: - * - * f is 1 or 1/2, depending on how we're driving the DLL. - * P is the clock period. - * DDF is the DLL Delay Factor, a dimensionless value that - * incorporates all the constants in the conversion. - * - * DDF will be either 8 or 16, both of which are powers of two. We can - * reduce the cost of this conversion by using bit shifts instead of - * multiplication or division. Thus: - * - * AD << DDS - * SDF = --------- - * P - * - * or - * - * AD = (SDF >> DDS) x P - * - * where: - * - * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. - */ - if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) { - dll_use_half_periods = true; - dll_delay_shift = 3 + 1; - } else { - dll_use_half_periods = false; - dll_delay_shift = 3; - } - - /* - * Compute the maximum sample delay the NFC allows, under current - * conditions. If the clock is running too slowly, no sample delay is - * possible. - */ - if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns) - max_sample_delay_in_ns = 0; - else { - /* - * Compute the delay implied by the largest sample delay factor - * the NFC allows. - */ - max_sample_delay_in_ns = - (nfc->max_sample_delay_factor * clock_period_in_ns) >> - dll_delay_shift; - - /* - * Check if the implied sample delay larger than the NFC - * actually allows. - */ - if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns) - max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; - } - - /* - * Check if improved timing information is available. If not, we have to - * use a less-sophisticated algorithm. - */ - if (!improved_timing_is_available) { - /* - * Fold the read setup time required by the NFC into the ideal - * sample delay. - */ - ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns + - nfc->internal_data_setup_in_ns; - - /* - * The ideal sample delay may be greater than the maximum - * allowed by the NFC. If so, we can trade off sample delay time - * for more data setup time. - * - * In each iteration of the following loop, we add a cycle to - * the data setup time and subtract a corresponding amount from - * the sample delay until we've satisified the constraints or - * can't do any better. - */ - while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - - data_setup_in_cycles++; - ideal_sample_delay_in_ns -= clock_period_in_ns; - - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - - } - - /* - * Compute the sample delay factor that corresponds most closely - * to the ideal sample delay. If the result is too large for the - * NFC, use the maximum value. - * - * Notice that we use the ns_to_cycles function to compute the - * sample delay factor. We do this because the form of the - * computation is the same as that for calculating cycles. - */ - sample_delay_factor = - ns_to_cycles( - ideal_sample_delay_in_ns << dll_delay_shift, - clock_period_in_ns, 0); - - if (sample_delay_factor > nfc->max_sample_delay_factor) - sample_delay_factor = nfc->max_sample_delay_factor; - - /* Skip to the part where we return our results. */ - goto return_results; - } - - /* - * If control arrives here, we have more detailed timing information, - * so we can use a better algorithm. - */ - - /* - * Fold the read setup time required by the NFC into the maximum - * propagation delay. - */ - max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; - - /* - * Earlier, we computed the number of clock cycles required to satisfy - * the data setup time. Now, we need to know the actual nanoseconds. - */ - data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; - - /* - * Compute tEYE, the width of the data eye when reading from the NAND - * Flash. The eye width is fundamentally determined by the data setup - * time, perturbed by propagation delays and some characteristics of the - * NAND Flash device. - * - * start of the eye = max_prop_delay + tREA - * end of the eye = min_prop_delay + tRHOH + data_setup - */ - tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + - (int)data_setup_in_ns; - - tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; - - /* - * The eye must be open. If it's not, we can try to open it by - * increasing its main forcer, the data setup time. - * - * In each iteration of the following loop, we increase the data setup - * time by a single clock cycle. We do this until either the eye is - * open or we run into NFC limits. - */ - while ((tEYE <= 0) && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - /* Give a cycle to data setup. */ - data_setup_in_cycles++; - /* Synchronize the data setup time with the cycles. */ - data_setup_in_ns += clock_period_in_ns; - /* Adjust tEYE accordingly. */ - tEYE += clock_period_in_ns; - } - - /* - * When control arrives here, the eye is open. The ideal time to sample - * the data is in the center of the eye: - * - * end of the eye + start of the eye - * --------------------------------- - data_setup - * 2 - * - * After some algebra, this simplifies to the code immediately below. - */ - ideal_sample_delay_in_ns = - ((int)max_prop_delay_in_ns + - (int)target.tREA_in_ns + - (int)min_prop_delay_in_ns + - (int)target.tRHOH_in_ns - - (int)data_setup_in_ns) >> 1; - - /* - * The following figure illustrates some aspects of a NAND Flash read: - * - * - * __ _____________________________________ - * RDN \_________________/ - * - * <---- tEYE -----> - * /-----------------\ - * Read Data ----------------------------< >--------- - * \-----------------/ - * ^ ^ ^ ^ - * | | | | - * |<--Data Setup -->|<--Delay Time -->| | - * | | | | - * | | | - * | |<-- Quantized Delay Time -->| - * | | | - * - * - * We have some issues we must now address: - * - * (1) The *ideal* sample delay time must not be negative. If it is, we - * jam it to zero. - * - * (2) The *ideal* sample delay time must not be greater than that - * allowed by the NFC. If it is, we can increase the data setup - * time, which will reduce the delay between the end of the data - * setup and the center of the eye. It will also make the eye - * larger, which might help with the next issue... - * - * (3) The *quantized* sample delay time must not fall either before the - * eye opens or after it closes (the latter is the problem - * illustrated in the above figure). - */ - - /* Jam a negative ideal sample delay to zero. */ - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - - /* - * Extend the data setup as needed to reduce the ideal sample delay - * below the maximum permitted by the NFC. - */ - while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - - /* Give a cycle to data setup. */ - data_setup_in_cycles++; - /* Synchronize the data setup time with the cycles. */ - data_setup_in_ns += clock_period_in_ns; - /* Adjust tEYE accordingly. */ - tEYE += clock_period_in_ns; - - /* - * Decrease the ideal sample delay by one half cycle, to keep it - * in the middle of the eye. - */ - ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); - - /* Jam a negative ideal sample delay to zero. */ - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - } - - /* - * Compute the sample delay factor that corresponds to the ideal sample - * delay. If the result is too large, then use the maximum allowed - * value. - * - * Notice that we use the ns_to_cycles function to compute the sample - * delay factor. We do this because the form of the computation is the - * same as that for calculating cycles. - */ - sample_delay_factor = - ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift, - clock_period_in_ns, 0); - - if (sample_delay_factor > nfc->max_sample_delay_factor) - sample_delay_factor = nfc->max_sample_delay_factor; - - /* - * These macros conveniently encapsulate a computation we'll use to - * continuously evaluate whether or not the data sample delay is inside - * the eye. - */ - #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) - - #define QUANTIZED_DELAY \ - ((int) ((sample_delay_factor * clock_period_in_ns) >> \ - dll_delay_shift)) - - #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) - - #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1)) - - /* - * While the quantized sample time falls outside the eye, reduce the - * sample delay or extend the data setup to move the sampling point back - * toward the eye. Do not allow the number of data setup cycles to - * exceed the maximum allowed by the NFC. - */ - while (SAMPLE_IS_NOT_WITHIN_THE_EYE && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - /* - * If control arrives here, the quantized sample delay falls - * outside the eye. Check if it's before the eye opens, or after - * the eye closes. - */ - if (QUANTIZED_DELAY > IDEAL_DELAY) { - /* - * If control arrives here, the quantized sample delay - * falls after the eye closes. Decrease the quantized - * delay time and then go back to re-evaluate. - */ - if (sample_delay_factor != 0) - sample_delay_factor--; - continue; - } - - /* - * If control arrives here, the quantized sample delay falls - * before the eye opens. Shift the sample point by increasing - * data setup time. This will also make the eye larger. - */ - - /* Give a cycle to data setup. */ - data_setup_in_cycles++; - /* Synchronize the data setup time with the cycles. */ - data_setup_in_ns += clock_period_in_ns; - /* Adjust tEYE accordingly. */ - tEYE += clock_period_in_ns; - - /* - * Decrease the ideal sample delay by one half cycle, to keep it - * in the middle of the eye. - */ - ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); - - /* ...and one less period for the delay time. */ - ideal_sample_delay_in_ns -= clock_period_in_ns; - - /* Jam a negative ideal sample delay to zero. */ - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - - /* - * We have a new ideal sample delay, so re-compute the quantized - * delay. - */ - sample_delay_factor = - ns_to_cycles( - ideal_sample_delay_in_ns << dll_delay_shift, - clock_period_in_ns, 0); - - if (sample_delay_factor > nfc->max_sample_delay_factor) - sample_delay_factor = nfc->max_sample_delay_factor; - } - - /* Control arrives here when we're ready to return our results. */ -return_results: - hw->data_setup_in_cycles = data_setup_in_cycles; - hw->data_hold_in_cycles = data_hold_in_cycles; - hw->address_setup_in_cycles = address_setup_in_cycles; - hw->use_half_periods = dll_use_half_periods; - hw->sample_delay_factor = sample_delay_factor; - hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT; - hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; - - /* Return success. */ - return 0; -} - /* * <1> Firstly, we should know what's the GPMI-clock means. * The GPMI-clock is the internal clock in the gpmi nand controller. @@ -824,13 +362,10 @@ return_results: * 4.1) From the aspect of the nand chip pins: * Delay = (tREA + C - tRP) {1} * - * tREA : the maximum read access time. From the ONFI nand standards, - * we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4. - * Please check it in : www.onfi.org - * C : a constant for adjust the delay. default is 4. - * tRP : the read pulse width. - * Specified by the HW_GPMI_TIMING0:DATA_SETUP: - * tRP = (GPMI-clock-period) * DATA_SETUP + * tREA : the maximum read access time. + * C : a constant to adjust the delay. default is 4000ps. + * tRP : the read pulse width, which is exactly: + * tRP = (GPMI-clock-period) * DATA_SETUP * * 4.2) From the aspect of the GPMI nand controller: * Delay = RDN_DELAY * 0.125 * RP {2} @@ -843,239 +378,137 @@ return_results: * * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD - * is 16ns, but in mx6q, we use 12ns. + * is 16000ps, but in mx6q, we use 12000ps. * * 4.3) since {1} equals {2}, we get: * - * (tREA + 4 - tRP) * 8 - * RDN_DELAY = --------------------- {3} + * (tREA + 4000 - tRP) * 8 + * RDN_DELAY = ----------------------- {3} * RP - * - * 4.4) We only support the fastest asynchronous mode of ONFI nand. - * For some ONFI nand, the mode 4 is the fastest mode; - * while for some ONFI nand, the mode 5 is the fastest mode. - * So we only support the mode 4 and mode 5. It is no need to - * support other modes. */ -static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, - struct gpmi_nfc_hardware_timing *hw) +static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this, + const struct nand_sdr_timings *sdr) { - struct resources *r = &this->resources; - unsigned long rate = clk_get_rate(r->clock[0]); - int mode = this->timing_mode; - int dll_threshold = this->devdata->max_chain_delay; - unsigned long delay; - unsigned long clk_period; - int t_rea; - int c = 4; - int t_rp; - int rp; + struct gpmi_nfc_hardware_timing *hw = &this->hw; + unsigned int dll_threshold_ps = this->devdata->max_chain_delay; + unsigned int period_ps, reference_period_ps; + unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles; + unsigned int tRP_ps; + bool use_half_period; + int sample_delay_ps, sample_delay_factor; + u16 busy_timeout_cycles; + u8 wrn_dly_sel; + + if (sdr->tRC_min >= 30000) { + /* ONFI non-EDO modes [0-3] */ + hw->clk_rate = 22000000; + wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; + } else if (sdr->tRC_min >= 25000) { + /* ONFI EDO mode 4 */ + hw->clk_rate = 80000000; + wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + } else { + /* ONFI EDO mode 5 */ + hw->clk_rate = 100000000; + wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + } - /* - * [1] for GPMI_HW_GPMI_TIMING0: - * The async mode requires 40MHz for mode 4, 50MHz for mode 5. - * The GPMI can support 100MHz at most. So if we want to - * get the 40MHz or 50MHz, we have to set DS=1, DH=1. - * Set the ADDRESS_SETUP to 0 in mode 4. - */ - hw->data_setup_in_cycles = 1; - hw->data_hold_in_cycles = 1; - hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0); + /* SDR core timings are given in picoseconds */ + period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate); - /* [2] for GPMI_HW_GPMI_TIMING1 */ - hw->device_busy_timeout = 0x9000; + addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps); + data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps); + data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps); + busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps); - /* [3] for GPMI_HW_GPMI_CTRL1 */ - hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) | + BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) | + BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles); + hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096); /* - * Enlarge 10 times for the numerator and denominator in {3}. - * This make us to get more accurate result. + * Derive NFC ideal delay from {3}: + * + * (tREA + 4000 - tRP) * 8 + * RDN_DELAY = ----------------------- + * RP */ - clk_period = NSEC_PER_SEC / (rate / 10); - dll_threshold *= 10; - t_rea = ((mode == 5) ? 16 : 20) * 10; - c *= 10; - - t_rp = clk_period * 1; /* DATA_SETUP is 1 */ - - if (clk_period > dll_threshold) { - hw->use_half_periods = 1; - rp = clk_period / 2; + if (period_ps > dll_threshold_ps) { + use_half_period = true; + reference_period_ps = period_ps / 2; } else { - hw->use_half_periods = 0; - rp = clk_period; + use_half_period = false; + reference_period_ps = period_ps; } - /* - * Multiply the numerator with 10, we could do a round off: - * 7.8 round up to 8; 7.4 round down to 7. - */ - delay = (((t_rea + c - t_rp) * 8) * 10) / rp; - delay = (delay + 5) / 10; - - hw->sample_delay_factor = delay; -} - -static int enable_edo_mode(struct gpmi_nand_data *this, int mode) -{ - struct resources *r = &this->resources; - struct nand_chip *nand = &this->nand; - struct mtd_info *mtd = nand_to_mtd(nand); - uint8_t *feature; - unsigned long rate; - int ret; - - feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL); - if (!feature) - return -ENOMEM; - - nand->select_chip(mtd, 0); - - /* [1] send SET FEATURE command to NAND */ - feature[0] = mode; - ret = nand->onfi_set_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); - if (ret) - goto err_out; - - /* [2] send GET FEATURE command to double-check the timing mode */ - memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN); - ret = nand->onfi_get_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); - if (ret || feature[0] != mode) - goto err_out; - - nand->select_chip(mtd, -1); - - /* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */ - rate = (mode == 5) ? 100000000 : 80000000; - clk_set_rate(r->clock[0], rate); - - /* Let the gpmi_begin() re-compute the timing again. */ - this->flags &= ~GPMI_TIMING_INIT_OK; - - this->flags |= GPMI_ASYNC_EDO_ENABLED; - this->timing_mode = mode; - kfree(feature); - dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode); - return 0; - -err_out: - nand->select_chip(mtd, -1); - kfree(feature); - dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode); - return -EINVAL; -} - -int gpmi_extra_init(struct gpmi_nand_data *this) -{ - struct nand_chip *chip = &this->nand; - - /* Enable the asynchronous EDO feature. */ - if (GPMI_IS_MX6(this) && chip->onfi_version) { - int mode = onfi_get_async_timing_mode(chip); - - /* We only support the timing mode 4 and mode 5. */ - if (mode & ONFI_TIMING_MODE_5) - mode = 5; - else if (mode & ONFI_TIMING_MODE_4) - mode = 4; - else - return 0; + tRP_ps = data_setup_cycles * period_ps; + sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8; + if (sample_delay_ps > 0) + sample_delay_factor = sample_delay_ps / reference_period_ps; + else + sample_delay_factor = 0; - return enable_edo_mode(this, mode); - } - return 0; + hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel); + if (sample_delay_factor) + hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) | + BM_GPMI_CTRL1_DLL_ENABLE | + (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0); } -/* Begin the I/O */ -void gpmi_begin(struct gpmi_nand_data *this) +void gpmi_nfc_apply_timings(struct gpmi_nand_data *this) { + struct gpmi_nfc_hardware_timing *hw = &this->hw; struct resources *r = &this->resources; void __iomem *gpmi_regs = r->gpmi_regs; - unsigned int clock_period_in_ns; - uint32_t reg; - unsigned int dll_wait_time_in_us; - struct gpmi_nfc_hardware_timing hw; - int ret; - - /* Enable the clock. */ - ret = gpmi_enable_clk(this); - if (ret) { - dev_err(this->dev, "We failed in enable the clk\n"); - goto err_out; - } - - /* Only initialize the timing once */ - if (this->flags & GPMI_TIMING_INIT_OK) - return; - this->flags |= GPMI_TIMING_INIT_OK; + unsigned int dll_wait_time_us; - if (this->flags & GPMI_ASYNC_EDO_ENABLED) - gpmi_compute_edo_timing(this, &hw); - else - gpmi_nfc_compute_hardware_timing(this, &hw); - - /* [1] Set HW_GPMI_TIMING0 */ - reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | - BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | - BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles); - - writel(reg, gpmi_regs + HW_GPMI_TIMING0); - - /* [2] Set HW_GPMI_TIMING1 */ - writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout), - gpmi_regs + HW_GPMI_TIMING1); + clk_set_rate(r->clock[0], hw->clk_rate); - /* [3] The following code is to set the HW_GPMI_CTRL1. */ + writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0); + writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1); - /* Set the WRN_DLY_SEL */ - writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR); - writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel), - gpmi_regs + HW_GPMI_CTRL1_SET); - - /* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */ - writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); - - /* Clear out the DLL control fields. */ - reg = BM_GPMI_CTRL1_RDN_DELAY | BM_GPMI_CTRL1_HALF_PERIOD; - writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR); + /* + * Clear several CTRL1 fields, DLL must be disabled when setting + * RDN_DELAY or HALF_PERIOD. + */ + writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR); + writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET); - /* If no sample delay is called for, return immediately. */ - if (!hw.sample_delay_factor) - return; + /* Wait 64 clock cycles before using the GPMI after enabling the DLL */ + dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64; + if (!dll_wait_time_us) + dll_wait_time_us = 1; - /* Set RDN_DELAY or HALF_PERIOD. */ - reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0) - | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor); + /* Wait for the DLL to settle. */ + udelay(dll_wait_time_us); +} - writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET); +int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + const struct nand_sdr_timings *sdr; - /* At last, we enable the DLL. */ - writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); + /* Retrieve required NAND timings */ + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); - /* - * After we enable the GPMI DLL, we have to wait 64 clock cycles before - * we can use the GPMI. Calculate the amount of time we need to wait, - * in microseconds. - */ - clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]); - dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; + /* Only MX6 GPMI controller can reach EDO timings */ + if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this)) + return -ENOTSUPP; - if (!dll_wait_time_in_us) - dll_wait_time_in_us = 1; + /* Stop here if this call was just a check */ + if (chipnr < 0) + return 0; - /* Wait for the DLL to settle. */ - udelay(dll_wait_time_in_us); + /* Do the actual derivation of the controller timings */ + gpmi_nfc_compute_timings(this, sdr); -err_out: - return; -} + this->hw.must_apply_timings = true; -void gpmi_end(struct gpmi_nand_data *this) -{ - gpmi_disable_clk(this); + return 0; } /* Clears a BCH interrupt. */ diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 61fdd733492f..c2597c8107a0 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -94,7 +94,7 @@ static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { static const struct gpmi_devdata gpmi_devdata_imx23 = { .type = IS_MX23, .bch_max_ecc_strength = 20, - .max_chain_delay = 16, + .max_chain_delay = 16000, .clks = gpmi_clks_for_mx2x, .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), }; @@ -102,7 +102,7 @@ static const struct gpmi_devdata gpmi_devdata_imx23 = { static const struct gpmi_devdata gpmi_devdata_imx28 = { .type = IS_MX28, .bch_max_ecc_strength = 20, - .max_chain_delay = 16, + .max_chain_delay = 16000, .clks = gpmi_clks_for_mx2x, .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), }; @@ -114,7 +114,7 @@ static const char * const gpmi_clks_for_mx6[] = { static const struct gpmi_devdata gpmi_devdata_imx6q = { .type = IS_MX6Q, .bch_max_ecc_strength = 40, - .max_chain_delay = 12, + .max_chain_delay = 12000, .clks = gpmi_clks_for_mx6, .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), }; @@ -122,7 +122,7 @@ static const struct gpmi_devdata gpmi_devdata_imx6q = { static const struct gpmi_devdata gpmi_devdata_imx6sx = { .type = IS_MX6SX, .bch_max_ecc_strength = 62, - .max_chain_delay = 12, + .max_chain_delay = 12000, .clks = gpmi_clks_for_mx6, .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), }; @@ -134,7 +134,7 @@ static const char * const gpmi_clks_for_mx7d[] = { static const struct gpmi_devdata gpmi_devdata_imx7d = { .type = IS_MX7D, .bch_max_ecc_strength = 62, - .max_chain_delay = 12, + .max_chain_delay = 12000, .clks = gpmi_clks_for_mx7d, .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), }; @@ -695,34 +695,6 @@ static void release_resources(struct gpmi_nand_data *this) release_dma_channels(this); } -static int init_hardware(struct gpmi_nand_data *this) -{ - int ret; - - /* - * This structure contains the "safe" GPMI timing that should succeed - * with any NAND Flash device - * (although, with less-than-optimal performance). - */ - struct nand_timing safe_timing = { - .data_setup_in_ns = 80, - .data_hold_in_ns = 60, - .address_setup_in_ns = 25, - .gpmi_sample_delay_in_ns = 6, - .tREA_in_ns = -1, - .tRLOH_in_ns = -1, - .tRHOH_in_ns = -1, - }; - - /* Initialize the hardwares. */ - ret = gpmi_init(this); - if (ret) - return ret; - - this->timing = safe_timing; - return 0; -} - static int read_page_prepare(struct gpmi_nand_data *this, void *destination, unsigned length, void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, @@ -938,11 +910,32 @@ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) { struct nand_chip *chip = mtd_to_nand(mtd); struct gpmi_nand_data *this = nand_get_controller_data(chip); + int ret; - if ((this->current_chip < 0) && (chipnr >= 0)) - gpmi_begin(this); - else if ((this->current_chip >= 0) && (chipnr < 0)) - gpmi_end(this); + /* + * For power consumption matters, disable/enable the clock each time a + * die is selected/unselected. + */ + if (this->current_chip < 0 && chipnr >= 0) { + ret = gpmi_enable_clk(this); + if (ret) + dev_err(this->dev, "Failed to enable the clock\n"); + } else if (this->current_chip >= 0 && chipnr < 0) { + ret = gpmi_disable_clk(this); + if (ret) + dev_err(this->dev, "Failed to disable the clock\n"); + } + + /* + * This driver currently supports only one NAND chip. Plus, dies share + * the same configuration. So once timings have been applied on the + * controller side, they will not change anymore. When the time will + * come, the check on must_apply_timings will have to be dropped. + */ + if (chipnr >= 0 && this->hw.must_apply_timings) { + this->hw.must_apply_timings = false; + gpmi_nfc_apply_timings(this); + } this->current_chip = chipnr; } @@ -1955,14 +1948,6 @@ static int gpmi_init_last(struct gpmi_nand_data *this) chip->options |= NAND_SUBPAGE_READ; } - /* - * Can we enable the extra features? such as EDO or Sync mode. - * - * We do not check the return value now. That's means if we fail in - * enable the extra features, we still can run in the normal way. - */ - gpmi_extra_init(this); - return 0; } @@ -1983,6 +1968,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) nand_set_controller_data(chip, this); nand_set_flash_node(chip, this->pdev->dev.of_node); chip->select_chip = gpmi_select_chip; + chip->setup_data_interface = gpmi_setup_data_interface; chip->cmd_ctrl = gpmi_cmd_ctrl; chip->dev_ready = gpmi_dev_ready; chip->read_byte = gpmi_read_byte; @@ -2093,7 +2079,7 @@ static int gpmi_nand_probe(struct platform_device *pdev) if (ret) goto exit_acquire_resources; - ret = init_hardware(this); + ret = gpmi_init(this); if (ret) goto exit_nfc_init; @@ -2141,7 +2127,6 @@ static int gpmi_pm_resume(struct device *dev) return ret; /* re-init the GPMI registers */ - this->flags &= ~GPMI_TIMING_INIT_OK; ret = gpmi_init(this); if (ret) { dev_err(this->dev, "Error setting GPMI : %d\n", ret); @@ -2155,9 +2140,6 @@ static int gpmi_pm_resume(struct device *dev) return ret; } - /* re-init others */ - gpmi_extra_init(this); - return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 06c1f993912c..62fde59b995f 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -86,39 +86,6 @@ enum dma_ops_type { DMA_FOR_WRITE_ECC_PAGE }; -/** - * struct nand_timing - Fundamental timing attributes for NAND. - * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the - * maximum of tDS and tWP. A negative value - * indicates this characteristic isn't known. - * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the - * maximum of tDH, tWH and tREH. A negative value - * indicates this characteristic isn't known. - * @address_setup_in_ns: The address setup time, in nanoseconds. Usually - * the maximum of tCLS, tCS and tALS. A negative - * value indicates this characteristic isn't known. - * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value - * indicates this characteristic isn't known. - * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A - * negative value indicates this characteristic isn't - * known. - * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A - * negative value indicates this characteristic isn't - * known. - * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A - * negative value indicates this characteristic isn't - * known. - */ -struct nand_timing { - int8_t data_setup_in_ns; - int8_t data_hold_in_ns; - int8_t address_setup_in_ns; - int8_t gpmi_sample_delay_in_ns; - int8_t tREA_in_ns; - int8_t tRLOH_in_ns; - int8_t tRHOH_in_ns; -}; - enum gpmi_type { IS_MX23, IS_MX28, @@ -135,11 +102,27 @@ struct gpmi_devdata { const int clks_count; }; +/** + * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. + * @must_apply_timings: Whether controller timings have already been + * applied or not (useful only while there is + * support for only one chip select) + * @clk_rate: The clock rate that must be used to derive the + * following parameters + * @timing0: HW_GPMI_TIMING0 register + * @timing1: HW_GPMI_TIMING1 register + * @ctrl1n: HW_GPMI_CTRL1n register + */ +struct gpmi_nfc_hardware_timing { + bool must_apply_timings; + unsigned long int clk_rate; + u32 timing0; + u32 timing1; + u32 ctrl1n; +}; + struct gpmi_nand_data { - /* flags */ -#define GPMI_ASYNC_EDO_ENABLED (1 << 0) -#define GPMI_TIMING_INIT_OK (1 << 1) - int flags; + /* Devdata */ const struct gpmi_devdata *devdata; /* System Interface */ @@ -150,8 +133,7 @@ struct gpmi_nand_data { struct resources resources; /* Flash Hardware */ - struct nand_timing timing; - int timing_mode; + struct gpmi_nfc_hardware_timing hw; /* BCH */ struct bch_geometry bch_geometry; @@ -204,69 +186,6 @@ struct gpmi_nand_data { void *private; }; -/** - * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. - * @data_setup_in_cycles: The data setup time, in cycles. - * @data_hold_in_cycles: The data hold time, in cycles. - * @address_setup_in_cycles: The address setup time, in cycles. - * @device_busy_timeout: The timeout waiting for NAND Ready/Busy, - * this value is the number of cycles multiplied - * by 4096. - * @use_half_periods: Indicates the clock is running slowly, so the - * NFC DLL should use half-periods. - * @sample_delay_factor: The sample delay factor. - * @wrn_dly_sel: The delay on the GPMI write strobe. - */ -struct gpmi_nfc_hardware_timing { - /* for HW_GPMI_TIMING0 */ - uint8_t data_setup_in_cycles; - uint8_t data_hold_in_cycles; - uint8_t address_setup_in_cycles; - - /* for HW_GPMI_TIMING1 */ - uint16_t device_busy_timeout; -#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/ - - /* for HW_GPMI_CTRL1 */ - bool use_half_periods; - uint8_t sample_delay_factor; - uint8_t wrn_dly_sel; -}; - -/** - * struct timing_threshold - Timing threshold - * @max_data_setup_cycles: The maximum number of data setup cycles that - * can be expressed in the hardware. - * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires - * for data read internal setup. In the Reference - * Manual, see the chapter "High-Speed NAND - * Timing" for more details. - * @max_sample_delay_factor: The maximum sample delay factor that can be - * expressed in the hardware. - * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the - * sample delay DLL hardware can possibly work - * with (the DLL is unusable with longer periods). - * If the full-cycle period is greater than HALF - * this value, the DLL must be configured to use - * half-periods. - * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the - * DLL can implement. - * @clock_frequency_in_hz: The clock frequency, in Hz, during the current - * I/O transaction. If no I/O transaction is in - * progress, this is the clock frequency during - * the most recent I/O transaction. - */ -struct timing_threshold { - const unsigned int max_chip_count; - const unsigned int max_data_setup_cycles; - const unsigned int internal_data_setup_in_ns; - const unsigned int max_sample_delay_factor; - const unsigned int max_dll_clock_period_in_ns; - const unsigned int max_dll_delay_in_ns; - unsigned long clock_frequency_in_hz; - -}; - /* Common Services */ int common_nfc_set_geometry(struct gpmi_nand_data *); struct dma_chan *get_dma_chan(struct gpmi_nand_data *); @@ -279,14 +198,16 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *, /* GPMI-NAND helper function library */ int gpmi_init(struct gpmi_nand_data *); -int gpmi_extra_init(struct gpmi_nand_data *); void gpmi_clear_bch(struct gpmi_nand_data *); void gpmi_dump_info(struct gpmi_nand_data *); int bch_set_geometry(struct gpmi_nand_data *); int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip); int gpmi_send_command(struct gpmi_nand_data *); -void gpmi_begin(struct gpmi_nand_data *); -void gpmi_end(struct gpmi_nand_data *); +int gpmi_enable_clk(struct gpmi_nand_data *this); +int gpmi_disable_clk(struct gpmi_nand_data *this); +int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf); +void gpmi_nfc_apply_timings(struct gpmi_nand_data *this); int gpmi_read_data(struct gpmi_nand_data *); int gpmi_send_data(struct gpmi_nand_data *); int gpmi_send_page(struct gpmi_nand_data *, diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h index 82114cdc8330..d92bf32221ca 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h @@ -147,6 +147,11 @@ #define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) +#define BM_GPMI_CTRL1_CLEAR_MASK (BM_GPMI_CTRL1_WRN_DLY_SEL | \ + BM_GPMI_CTRL1_DLL_ENABLE | \ + BM_GPMI_CTRL1_RDN_DELAY | \ + BM_GPMI_CTRL1_HALF_PERIOD) + #define HW_GPMI_TIMING0 0x00000070 #define BP_GPMI_TIMING0_ADDRESS_SETUP 16 diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index cb862793ab6d..27558a67fa41 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -762,8 +762,8 @@ static int hisi_nfc_probe(struct platform_device *pdev) chip->write_buf = hisi_nfc_write_buf; chip->read_buf = hisi_nfc_read_buf; chip->chip_delay = HINFC504_CHIP_DELAY; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; hisi_nfc_host_init(host); diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index 613b00a9604b..613b00a9604b 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c diff --git a/drivers/mtd/nand/jz4780_bch.c b/drivers/mtd/nand/raw/jz4780_bch.c index 731c6051d91e..731c6051d91e 100644 --- a/drivers/mtd/nand/jz4780_bch.c +++ b/drivers/mtd/nand/raw/jz4780_bch.c diff --git a/drivers/mtd/nand/jz4780_bch.h b/drivers/mtd/nand/raw/jz4780_bch.h index bf4718088a3a..bf4718088a3a 100644 --- a/drivers/mtd/nand/jz4780_bch.h +++ b/drivers/mtd/nand/raw/jz4780_bch.h diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c index e69f6ae4c539..e69f6ae4c539 100644 --- a/drivers/mtd/nand/jz4780_nand.c +++ b/drivers/mtd/nand/raw/jz4780_nand.c diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index e357948a7505..e357948a7505 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 5f7cc6da0a7f..5f7cc6da0a7f 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c diff --git a/drivers/mtd/nand/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 03805f9669da..10e953218948 100644 --- a/drivers/mtd/nand/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -307,7 +307,8 @@ struct marvell_nfc_caps { * @controller: Base controller structure * @dev: Parent device (used to print error messages) * @regs: NAND controller registers - * @ecc_clk: ECC block clock, two times the NAND controller clock + * @core_clk: Core clock + * @reg_clk: Regiters clock * @complete: Completion object to wait for NAND controller events * @assigned_cs: Bitmask describing already assigned CS lines * @chips: List containing all the NAND chips attached to @@ -320,7 +321,8 @@ struct marvell_nfc { struct nand_hw_control controller; struct device *dev; void __iomem *regs; - struct clk *ecc_clk; + struct clk *core_clk; + struct clk *reg_clk; struct completion complete; unsigned long assigned_cs; struct list_head chips; @@ -379,6 +381,8 @@ struct marvell_nfc_timings { * return the number of clock periods. */ #define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns)) +#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \ + period_ns)) /** * NAND driver structure filled during the parsing of the ->exec_op() subop @@ -2189,7 +2193,7 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, struct nand_chip *chip = mtd_to_nand(mtd); struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - unsigned int period_ns = 1000000000 / clk_get_rate(nfc->ecc_clk) * 2; + unsigned int period_ns = 1000000000 / clk_get_rate(nfc->core_clk) * 2; const struct nand_sdr_timings *sdr; struct marvell_nfc_timings nfc_tmg; int read_delay; @@ -2236,8 +2240,20 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min), period_ns); - /* Use WAIT_MODE (wait for RB line) instead of only relying on delays */ - nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); + /* + * NFCv2: Use WAIT_MODE (wait for RB line), do not rely only on delays. + * NFCv1: No WAIT_MODE, tR must be maximal. + */ + if (nfc->caps->is_nfcv2) { + nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); + } else { + nfc_tmg.tR = TO_CYCLES64(sdr->tWB_max + sdr->tR_max, + period_ns); + if (nfc_tmg.tR + 3 > nfc_tmg.tCH) + nfc_tmg.tR = nfc_tmg.tCH - 3; + else + nfc_tmg.tR = 0; + } if (chipnr < 0) return 0; @@ -2249,18 +2265,24 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, NDTR0_TWP(nfc_tmg.tWP) | NDTR0_TWH(nfc_tmg.tWH) | NDTR0_TCS(nfc_tmg.tCS) | - NDTR0_TCH(nfc_tmg.tCH) | - NDTR0_RD_CNT_DEL(read_delay) | - NDTR0_SELCNTR | - NDTR0_TADL(nfc_tmg.tADL); + NDTR0_TCH(nfc_tmg.tCH); marvell_nand->ndtr1 = NDTR1_TAR(nfc_tmg.tAR) | NDTR1_TWHR(nfc_tmg.tWHR) | - NDTR1_TRHW(nfc_tmg.tRHW) | - NDTR1_WAIT_MODE | NDTR1_TR(nfc_tmg.tR); + if (nfc->caps->is_nfcv2) { + marvell_nand->ndtr0 |= + NDTR0_RD_CNT_DEL(read_delay) | + NDTR0_SELCNTR | + NDTR0_TADL(nfc_tmg.tADL); + + marvell_nand->ndtr1 |= + NDTR1_TRHW(nfc_tmg.tRHW) | + NDTR1_WAIT_MODE; + } + return 0; } @@ -2395,8 +2417,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, chip->exec_op = marvell_nfc_exec_op; chip->select_chip = marvell_nfc_select_chip; - if (nfc->caps->is_nfcv2 && - !of_property_read_bool(np, "marvell,nand-keep-config")) + if (!of_property_read_bool(np, "marvell,nand-keep-config")) chip->setup_data_interface = marvell_nfc_setup_data_interface; mtd = nand_to_mtd(chip); @@ -2738,20 +2759,37 @@ static int marvell_nfc_probe(struct platform_device *pdev) return irq; } - nfc->ecc_clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nfc->ecc_clk)) - return PTR_ERR(nfc->ecc_clk); + nfc->core_clk = devm_clk_get(&pdev->dev, "core"); - ret = clk_prepare_enable(nfc->ecc_clk); + /* Managed the legacy case (when the first clock was not named) */ + if (nfc->core_clk == ERR_PTR(-ENOENT)) + nfc->core_clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(nfc->core_clk)) + return PTR_ERR(nfc->core_clk); + + ret = clk_prepare_enable(nfc->core_clk); if (ret) return ret; + nfc->reg_clk = devm_clk_get(&pdev->dev, "reg"); + if (PTR_ERR(nfc->reg_clk) != -ENOENT) { + if (!IS_ERR(nfc->reg_clk)) { + ret = clk_prepare_enable(nfc->reg_clk); + if (ret) + goto unprepare_core_clk; + } else { + ret = PTR_ERR(nfc->reg_clk); + goto unprepare_core_clk; + } + } + marvell_nfc_disable_int(nfc, NDCR_ALL_INT); marvell_nfc_clear_int(nfc, NDCR_ALL_INT); ret = devm_request_irq(dev, irq, marvell_nfc_isr, 0, "marvell-nfc", nfc); if (ret) - goto unprepare_clk; + goto unprepare_reg_clk; /* Get NAND controller capabilities */ if (pdev->id_entry) @@ -2762,24 +2800,26 @@ static int marvell_nfc_probe(struct platform_device *pdev) if (!nfc->caps) { dev_err(dev, "Could not retrieve NFC caps\n"); ret = -EINVAL; - goto unprepare_clk; + goto unprepare_reg_clk; } /* Init the controller and then probe the chips */ ret = marvell_nfc_init(nfc); if (ret) - goto unprepare_clk; + goto unprepare_reg_clk; platform_set_drvdata(pdev, nfc); ret = marvell_nand_chips_init(dev, nfc); if (ret) - goto unprepare_clk; + goto unprepare_reg_clk; return 0; -unprepare_clk: - clk_disable_unprepare(nfc->ecc_clk); +unprepare_reg_clk: + clk_disable_unprepare(nfc->reg_clk); +unprepare_core_clk: + clk_disable_unprepare(nfc->core_clk); return ret; } @@ -2795,7 +2835,8 @@ static int marvell_nfc_remove(struct platform_device *pdev) dma_release_channel(nfc->dma_chan); } - clk_disable_unprepare(nfc->ecc_clk); + clk_disable_unprepare(nfc->reg_clk); + clk_disable_unprepare(nfc->core_clk); return 0; } diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index b6b97cc9fba6..6d1740d54e0d 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -6,9 +6,8 @@ * by OSADL membership fees in 2009; for details see www.osadl.org. * * Based on original driver from Freescale Semiconductor - * written by John Rigby <jrigby@freescale.com> on basis - * of drivers/mtd/nand/mxc_nand.c. Reworked and extended - * Piotr Ziecik <kosmo@semihalf.com>. + * written by John Rigby <jrigby@freescale.com> on basis of mxc_nand.c. + * Reworked and extended by Piotr Ziecik <kosmo@semihalf.com>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -708,8 +707,8 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip->read_buf = mpc5121_nfc_read_buf; chip->write_buf = mpc5121_nfc_write_buf; chip->select_chip = mpc5121_nfc_select_chip; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.algo = NAND_ECC_HAMMING; diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c index 40d86a861a70..40d86a861a70 100644 --- a/drivers/mtd/nand/mtk_ecc.c +++ b/drivers/mtd/nand/raw/mtk_ecc.c diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h index a455df080952..a455df080952 100644 --- a/drivers/mtd/nand/mtk_ecc.h +++ b/drivers/mtd/nand/raw/mtk_ecc.h diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 6977da3a26aa..6977da3a26aa 100644 --- a/drivers/mtd/nand/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index f3be0b2a8869..45786e707b7b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -140,6 +140,8 @@ struct mxc_nand_host; struct mxc_nand_devtype_data { void (*preset)(struct mtd_info *); + int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc, + int page); void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); void (*send_addr)(struct mxc_nand_host *, uint16_t, int); void (*send_page)(struct mtd_info *, unsigned int); @@ -150,10 +152,9 @@ struct mxc_nand_devtype_data { u32 (*get_ecc_status)(struct mxc_nand_host *); const struct mtd_ooblayout_ops *ooblayout; void (*select_chip)(struct mtd_info *mtd, int chip); - int (*correct_data)(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc); int (*setup_data_interface)(struct mtd_info *mtd, int csline, const struct nand_data_interface *conf); + void (*enable_hwecc)(struct nand_chip *chip, bool enable); /* * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked @@ -252,6 +253,109 @@ static void memcpy16_toio(void __iomem *trg, const void *src, int size) __raw_writew(*s++, t++); } +/* + * The controller splits a page into data chunks of 512 bytes + partial oob. + * There are writesize / 512 such chunks, the size of the partial oob parts is + * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then + * contains additionally the byte lost by rounding (if any). + * This function handles the needed shuffling between host->data_buf (which + * holds a page in natural order, i.e. writesize bytes data + oobsize bytes + * spare) and the NFC buffer. + */ +static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(this); + u16 i, oob_chunk_size; + u16 num_chunks = mtd->writesize / 512; + + u8 *d = buf; + u8 __iomem *s = host->spare0; + u16 sparebuf_size = host->devtype_data->spare_len; + + /* size of oob chunk for all but possibly the last one */ + oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; + + if (bfrom) { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + oob_chunk_size); + + /* the last chunk */ + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + host->used_oobsize - i * oob_chunk_size); + } else { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + oob_chunk_size); + + /* the last chunk */ + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + host->used_oobsize - i * oob_chunk_size); + } +} + +/* + * MXC NANDFC can only perform full page+spare or spare-only read/write. When + * the upper layers perform a read/write buf operation, the saved column address + * is used to index into the full page. So usually this function is called with + * column == 0 (unless no column cycle is needed indicated by column == -1) + */ +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + /* Write out column address, if necessary */ + if (column != -1) { + host->devtype_data->send_addr(host, column & 0xff, + page_addr == -1); + if (mtd->writesize > 512) + /* another col addr cycle for 2k page */ + host->devtype_data->send_addr(host, + (column >> 8) & 0xff, + false); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + host->devtype_data->send_addr(host, (page_addr & 0xff), false); + + if (mtd->writesize > 512) { + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); + } else + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); + } else { + if (nand_chip->options & NAND_ROW_ADDR_3) { + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); + } else + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); + } + } +} + static int check_int_v3(struct mxc_nand_host *host) { uint32_t tmp; @@ -575,6 +679,42 @@ static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) return ret; } +static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint16_t config1; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config1 = readw(NFC_V1_V2_CONFIG1); + + if (enable) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + else + config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; + + writew(config1, NFC_V1_V2_CONFIG1); +} + +static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint32_t config2; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config2 = readl(NFC_V3_CONFIG2); + + if (enable) + config2 |= NFC_V3_CONFIG2_ECC_EN; + else + config2 &= ~NFC_V3_CONFIG2_ECC_EN; + + writel(config2, NFC_V3_CONFIG2); +} + /* This functions is used by upper layer to checks if device is ready */ static int mxc_nand_dev_ready(struct mtd_info *mtd) { @@ -585,45 +725,90 @@ static int mxc_nand_dev_ready(struct mtd_info *mtd) return 1; } -static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, + bool ecc, int page) { - /* - * If HW ECC is enabled, we turn it on during init. There is - * no need to enable again here. - */ -} + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int bitflips_corrected = 0; + int no_subpages; + int i; -static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + host->devtype_data->enable_hwecc(chip, ecc); - /* - * 1-Bit errors are automatically corrected in HW. No need for - * additional correction. 2-Bit errors cannot be corrected by - * HW ECC, so we need to return failure - */ - uint16_t ecc_status = get_ecc_status_v1(host); + host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); + mxc_do_addr_cycle(mtd, 0, page); + + if (mtd->writesize > 512) + host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true); + + no_subpages = mtd->writesize >> 9; + + for (i = 0; i < no_subpages; i++) { + uint16_t ecc_stats; + + /* NANDFC buffer 0 is used for page read/write */ + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); + + writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + + ecc_stats = get_ecc_status_v1(host); + + ecc_stats >>= 2; - if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { - dev_dbg(host->dev, "HWECC uncorrectable 2-bit ECC error\n"); - return -EBADMSG; + if (buf && ecc) { + switch (ecc_stats & 0x3) { + case 0: + default: + break; + case 1: + mtd->ecc_stats.corrected++; + bitflips_corrected = 1; + break; + case 2: + mtd->ecc_stats.failed++; + break; + } + } } - return 0; + if (buf) + memcpy32_fromio(buf, host->main_area0, mtd->writesize); + if (oob) + copy_spare(mtd, true, oob); + + return bitflips_corrected; } -static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) +static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, + void *oob, bool ecc, int page) { - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int max_bitflips = 0; u32 ecc_stat, err; - int no_subpages = 1; - int ret = 0; + int no_subpages; u8 ecc_bit_mask, err_limit; + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); + mxc_do_addr_cycle(mtd, 0, page); + + if (mtd->writesize > 512) + host->devtype_data->send_cmd(host, + NAND_CMD_READSTART, true); + + host->devtype_data->send_page(mtd, NFC_OUTPUT); + + if (buf) + memcpy32_fromio(buf, host->main_area0, mtd->writesize); + if (oob) + copy_spare(mtd, true, oob); + ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; err_limit = (host->eccsize == 4) ? 0x4 : 0x8; @@ -634,25 +819,99 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, do { err = ecc_stat & ecc_bit_mask; if (err > err_limit) { - dev_dbg(host->dev, "UnCorrectable RS-ECC Error\n"); - return -EBADMSG; + mtd->ecc_stats.failed++; } else { - ret += err; + mtd->ecc_stats.corrected += err; + max_bitflips = max_t(unsigned int, max_bitflips, err); } + ecc_stat >>= 4; } while (--no_subpages); - dev_dbg(host->dev, "%d Symbol Correctable RS-ECC Error\n", ret); + return max_bitflips; +} - return ret; +static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + void *oob_buf; + + if (oob_required) + oob_buf = chip->oob_poi; + else + oob_buf = NULL; + + return host->devtype_data->read_page(chip, buf, oob_buf, 1, page); +} + +static int mxc_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + void *oob_buf; + + if (oob_required) + oob_buf = chip->oob_poi; + else + oob_buf = NULL; + + return host->devtype_data->read_page(chip, buf, oob_buf, 0, page); +} + +static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0, + page); } -static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) +static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf, + bool ecc, int page) { + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false); + mxc_do_addr_cycle(mtd, 0, page); + + memcpy32_toio(host->main_area0, buf, mtd->writesize); + copy_spare(mtd, false, chip->oob_poi); + + host->devtype_data->send_page(mtd, NFC_INPUT); + host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true); + mxc_do_addr_cycle(mtd, 0, page); + return 0; } +static int mxc_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + return mxc_nand_write_page(chip, buf, true, page); +} + +static int mxc_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + return mxc_nand_write_page(chip, buf, false, page); +} + +static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + memset(host->data_buf, 0xff, mtd->writesize); + + return mxc_nand_write_page(chip, host->data_buf, false, page); +} + static u_char mxc_nand_read_byte(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd_to_nand(mtd); @@ -772,109 +1031,6 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); } -/* - * The controller splits a page into data chunks of 512 bytes + partial oob. - * There are writesize / 512 such chunks, the size of the partial oob parts is - * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then - * contains additionally the byte lost by rounding (if any). - * This function handles the needed shuffling between host->data_buf (which - * holds a page in natural order, i.e. writesize bytes data + oobsize bytes - * spare) and the NFC buffer. - */ -static void copy_spare(struct mtd_info *mtd, bool bfrom) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(this); - u16 i, oob_chunk_size; - u16 num_chunks = mtd->writesize / 512; - - u8 *d = host->data_buf + mtd->writesize; - u8 __iomem *s = host->spare0; - u16 sparebuf_size = host->devtype_data->spare_len; - - /* size of oob chunk for all but possibly the last one */ - oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; - - if (bfrom) { - for (i = 0; i < num_chunks - 1; i++) - memcpy16_fromio(d + i * oob_chunk_size, - s + i * sparebuf_size, - oob_chunk_size); - - /* the last chunk */ - memcpy16_fromio(d + i * oob_chunk_size, - s + i * sparebuf_size, - host->used_oobsize - i * oob_chunk_size); - } else { - for (i = 0; i < num_chunks - 1; i++) - memcpy16_toio(&s[i * sparebuf_size], - &d[i * oob_chunk_size], - oob_chunk_size); - - /* the last chunk */ - memcpy16_toio(&s[i * sparebuf_size], - &d[i * oob_chunk_size], - host->used_oobsize - i * oob_chunk_size); - } -} - -/* - * MXC NANDFC can only perform full page+spare or spare-only read/write. When - * the upper layers perform a read/write buf operation, the saved column address - * is used to index into the full page. So usually this function is called with - * column == 0 (unless no column cycle is needed indicated by column == -1) - */ -static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - /* Write out column address, if necessary */ - if (column != -1) { - host->devtype_data->send_addr(host, column & 0xff, - page_addr == -1); - if (mtd->writesize > 512) - /* another col addr cycle for 2k page */ - host->devtype_data->send_addr(host, - (column >> 8) & 0xff, - false); - } - - /* Write out page address, if necessary */ - if (page_addr != -1) { - /* paddr_0 - p_addr_7 */ - host->devtype_data->send_addr(host, (page_addr & 0xff), false); - - if (mtd->writesize > 512) { - if (mtd->size >= 0x10000000) { - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, - false); - host->devtype_data->send_addr(host, - (page_addr >> 16) & 0xff, - true); - } else - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, true); - } else { - if (nand_chip->options & NAND_ROW_ADDR_3) { - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, - false); - host->devtype_data->send_addr(host, - (page_addr >> 16) & 0xff, - true); - } else - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, true); - } - } -} - #define MXC_V1_ECCBYTES 5 static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, @@ -1235,57 +1391,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, mxc_do_addr_cycle(mtd, column, page_addr); break; - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - if (command == NAND_CMD_READ0) - host->buf_start = column; - else - host->buf_start = column + mtd->writesize; - - command = NAND_CMD_READ0; /* only READ0 is valid */ - - host->devtype_data->send_cmd(host, command, false); - WARN_ONCE(column < 0, - "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", - command, column, page_addr); - mxc_do_addr_cycle(mtd, 0, page_addr); - - if (mtd->writesize > 512) - host->devtype_data->send_cmd(host, - NAND_CMD_READSTART, true); - - host->devtype_data->send_page(mtd, NFC_OUTPUT); - - memcpy32_fromio(host->data_buf, host->main_area0, - mtd->writesize); - copy_spare(mtd, true); - break; - - case NAND_CMD_SEQIN: - if (column >= mtd->writesize) - /* call ourself to read a page */ - mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); - - host->buf_start = column; - - host->devtype_data->send_cmd(host, command, false); - WARN_ONCE(column < -1, - "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", - command, column, page_addr); - mxc_do_addr_cycle(mtd, 0, page_addr); - break; - - case NAND_CMD_PAGEPROG: - memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); - copy_spare(mtd, false); - host->devtype_data->send_page(mtd, NFC_INPUT); - host->devtype_data->send_cmd(host, command, true); - WARN_ONCE(column != -1 || page_addr != -1, - "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", - command, column, page_addr); - mxc_do_addr_cycle(mtd, column, page_addr); - break; - case NAND_CMD_READID: host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); @@ -1316,19 +1421,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } -static int mxc_nand_onfi_set_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) +static int mxc_nand_set_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, u8 *subfeature_param) { struct nand_chip *nand_chip = mtd_to_nand(mtd); struct mxc_nand_host *host = nand_get_controller_data(nand_chip); int i; - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - host->buf_start = 0; for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) @@ -1342,19 +1441,13 @@ static int mxc_nand_onfi_set_features(struct mtd_info *mtd, return 0; } -static int mxc_nand_onfi_get_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) +static int mxc_nand_get_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, u8 *subfeature_param) { struct nand_chip *nand_chip = mtd_to_nand(mtd); struct mxc_nand_host *host = nand_get_controller_data(nand_chip); int i; - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false); mxc_do_addr_cycle(mtd, addr, -1); host->devtype_data->send_page(mtd, NFC_OUTPUT); @@ -1397,6 +1490,7 @@ static struct nand_bbt_descr bbt_mirror_descr = { /* v1 + irqpending_quirk: i.MX21 */ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, .send_page = send_page_v1, @@ -1407,7 +1501,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .get_ecc_status = get_ecc_status_v1, .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v1, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 1, .needs_ip = 0, .regs_offset = 0xe00, @@ -1420,6 +1514,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { /* v1 + !irqpending_quirk: i.MX27, i.MX31 */ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, .send_page = send_page_v1, @@ -1430,7 +1525,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { .get_ecc_status = get_ecc_status_v1, .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v1, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 0, .needs_ip = 0, .regs_offset = 0xe00, @@ -1444,6 +1539,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { /* v21: i.MX25, i.MX35 */ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .preset = preset_v2, + .read_page = mxc_nand_read_page_v2_v3, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, .send_page = send_page_v2, @@ -1454,8 +1550,8 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .get_ecc_status = get_ecc_status_v2, .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v2, - .correct_data = mxc_nand_correct_data_v2_v3, .setup_data_interface = mxc_nand_v2_setup_data_interface, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 0, .needs_ip = 0, .regs_offset = 0x1e00, @@ -1469,6 +1565,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { /* v3.2a: i.MX51 */ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, .send_cmd = send_cmd_v3, .send_addr = send_addr_v3, .send_page = send_page_v3, @@ -1479,7 +1576,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .get_ecc_status = get_ecc_status_v3, .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v2_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, .irqpending_quirk = 0, .needs_ip = 1, .regs_offset = 0, @@ -1494,6 +1591,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { /* v3.2b: i.MX53 */ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, .send_cmd = send_cmd_v3, .send_addr = send_addr_v3, .send_page = send_page_v3, @@ -1504,7 +1602,7 @@ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { .get_ecc_status = get_ecc_status_v3, .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v2_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, .irqpending_quirk = 0, .needs_ip = 1, .regs_offset = 0, @@ -1642,8 +1740,8 @@ static int mxcnd_probe(struct platform_device *pdev) this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; this->read_buf = mxc_nand_read_buf; - this->onfi_set_features = mxc_nand_onfi_set_features; - this->onfi_get_features = mxc_nand_onfi_get_features; + this->set_features = mxc_nand_set_features; + this->get_features = mxc_nand_get_features; host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) @@ -1751,9 +1849,12 @@ static int mxcnd_probe(struct platform_device *pdev) switch (this->ecc.mode) { case NAND_ECC_HW: - this->ecc.calculate = mxc_nand_calculate_ecc; - this->ecc.hwctl = mxc_nand_enable_hwecc; - this->ecc.correct = host->devtype_data->correct_data; + this->ecc.read_page = mxc_nand_read_page; + this->ecc.read_page_raw = mxc_nand_read_page_raw; + this->ecc.read_oob = mxc_nand_read_oob; + this->ecc.write_page = mxc_nand_write_page_ecc; + this->ecc.write_page_raw = mxc_nand_write_page_raw; + this->ecc.write_oob = mxc_nand_write_oob; break; case NAND_ECC_SOFT: @@ -1810,15 +1911,18 @@ static int mxcnd_probe(struct platform_device *pdev) goto escan; /* Register the partitions */ - mtd_device_parse_register(mtd, part_probes, - NULL, - host->pdata.parts, - host->pdata.nr_parts); + err = mtd_device_parse_register(mtd, part_probes, NULL, + host->pdata.parts, + host->pdata.nr_parts); + if (err) + goto cleanup_nand; platform_set_drvdata(pdev, host); return 0; +cleanup_nand: + nand_cleanup(this); escan: if (host->clk_act) clk_disable_unprepare(host->clk); diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/raw/nand_amd.c index 22f060f38123..22f060f38123 100644 --- a/drivers/mtd/nand/nand_amd.c +++ b/drivers/mtd/nand/raw/nand_amd.c diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index e70ca16a5118..72f3a89da513 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -349,7 +349,7 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) * 8-bits of the data bus. During address transfers, the host shall * set the upper 8-bits of the data bus to 00h. * - * One user of the write_byte callback is nand_onfi_set_features. The + * One user of the write_byte callback is nand_set_features. The * four parameters are specified to be written to I/O[7:0], but this is * neither an address nor a command transfer. Let's assume a 0 on the * upper I/O lines is OK. @@ -527,7 +527,6 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) /* Attempt erase before marking OOB */ memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; einfo.addr = ofs; einfo.len = 1ULL << chip->phys_erase_shift; nand_erase_nand(mtd, &einfo, 0); @@ -1160,6 +1159,60 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) return status; } +static bool nand_supports_get_features(struct nand_chip *chip, int addr) +{ + return (chip->parameters.supports_set_get_features && + test_bit(addr, chip->parameters.get_feature_list)); +} + +static bool nand_supports_set_features(struct nand_chip *chip, int addr) +{ + return (chip->parameters.supports_set_get_features && + test_bit(addr, chip->parameters.set_feature_list)); +} + +/** + * nand_get_features - wrapper to perform a GET_FEATURE + * @chip: NAND chip info structure + * @addr: feature address + * @subfeature_param: the subfeature parameters, a four bytes array + * + * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the + * operation cannot be handled. + */ +int nand_get_features(struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!nand_supports_get_features(chip, addr)) + return -ENOTSUPP; + + return chip->get_features(mtd, chip, addr, subfeature_param); +} +EXPORT_SYMBOL_GPL(nand_get_features); + +/** + * nand_set_features - wrapper to perform a SET_FEATURE + * @chip: NAND chip info structure + * @addr: feature address + * @subfeature_param: the subfeature parameters, a four bytes array + * + * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the + * operation cannot be handled. + */ +int nand_set_features(struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!nand_supports_set_features(chip, addr)) + return -ENOTSUPP; + + return chip->set_features(mtd, chip, addr, subfeature_param); +} +EXPORT_SYMBOL_GPL(nand_set_features); + /** * nand_reset_data_interface - Reset data interface and timings * @chip: The NAND chip @@ -1215,31 +1268,59 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) { struct mtd_info *mtd = nand_to_mtd(chip); + u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { + chip->onfi_timing_mode_default, + }; int ret; if (!chip->setup_data_interface) return 0; - /* - * Ensure the timing mode has been changed on the chip side - * before changing timings on the controller side. - */ - if (chip->onfi_version && - (le16_to_cpu(chip->onfi_params.opt_cmd) & - ONFI_OPT_CMD_SET_GET_FEATURES)) { - u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { - chip->onfi_timing_mode_default, - }; - - ret = chip->onfi_set_features(mtd, chip, - ONFI_FEATURE_ADDR_TIMING_MODE, - tmode_param); + /* Change the mode on the chip side (if supported by the NAND chip) */ + if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) { + chip->select_chip(mtd, chipnr); + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + chip->select_chip(mtd, -1); if (ret) - goto err; + return ret; } + /* Change the mode on the controller side */ ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); -err: + if (ret) + return ret; + + /* Check the mode has been accepted by the chip, if supported */ + if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) + return 0; + + memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN); + chip->select_chip(mtd, chipnr); + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + chip->select_chip(mtd, -1); + if (ret) + goto err_reset_chip; + + if (tmode_param[0] != chip->onfi_timing_mode_default) { + pr_warn("timing mode %d not acknowledged by the NAND chip\n", + chip->onfi_timing_mode_default); + goto err_reset_chip; + } + + return 0; + +err_reset_chip: + /* + * Fallback to mode 0 if the chip explicitly did not ack the chosen + * timing mode. + */ + nand_reset_data_interface(chip, chipnr); + chip->select_chip(mtd, chipnr); + nand_reset_op(chip); + chip->select_chip(mtd, -1); + return ret; } @@ -2739,10 +2820,18 @@ int nand_reset(struct nand_chip *chip, int chipnr) if (ret) return ret; - chip->select_chip(mtd, chipnr); + /* + * A nand_reset_data_interface() put both the NAND chip and the NAND + * controller in timings mode 0. If the default mode for this chip is + * also 0, no need to proceed to the change again. Plus, at probe time, + * nand_setup_data_interface() uses ->set/get_features() which would + * fail anyway as the parameter page is not available yet. + */ + if (!chip->onfi_timing_mode_default) + return 0; + chip->data_interface = saved_data_intf; ret = nand_setup_data_interface(chip, chipnr); - chip->select_chip(mtd, -1); if (ret) return ret; @@ -4605,22 +4694,20 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (nand_check_wp(mtd)) { pr_debug("%s: device is write protected!\n", __func__); - instr->state = MTD_ERASE_FAILED; + ret = -EIO; goto erase_exit; } /* Loop through the pages */ len = instr->len; - instr->state = MTD_ERASING; - while (len) { /* Check if we have a bad block, we do not erase bad blocks! */ if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, allowbbt)) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", __func__, page); - instr->state = MTD_ERASE_FAILED; + ret = -EIO; goto erase_exit; } @@ -4638,7 +4725,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (status) { pr_debug("%s: failed erase, page 0x%08x\n", __func__, page); - instr->state = MTD_ERASE_FAILED; + ret = -EIO; instr->fail_addr = ((loff_t)page << chip->page_shift); goto erase_exit; @@ -4655,20 +4742,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, chip->select_chip(mtd, chipnr); } } - instr->state = MTD_ERASE_DONE; + ret = 0; erase_exit: - ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; - /* Deselect and wake up anyone waiting on the device */ chip->select_chip(mtd, -1); nand_release_device(mtd); - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); - /* Return more or less happy */ return ret; } @@ -4769,44 +4850,35 @@ static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) } /** - * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand + * nand_default_set_features- [REPLACEABLE] set NAND chip features * @mtd: MTD device structure * @chip: nand chip info structure * @addr: feature address. * @subfeature_param: the subfeature parameters, a four bytes array. */ -static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, - int addr, uint8_t *subfeature_param) +static int nand_default_set_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + uint8_t *subfeature_param) { - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - return nand_set_features_op(chip, addr, subfeature_param); } /** - * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand + * nand_default_get_features- [REPLACEABLE] get NAND chip features * @mtd: MTD device structure * @chip: nand chip info structure * @addr: feature address. * @subfeature_param: the subfeature parameters, a four bytes array. */ -static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, - int addr, uint8_t *subfeature_param) +static int nand_default_get_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + uint8_t *subfeature_param) { - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - return nand_get_features_op(chip, addr, subfeature_param); } /** - * nand_onfi_get_set_features_notsupp - set/get features stub returning - * -ENOTSUPP + * nand_get_set_features_notsupp - set/get features stub returning -ENOTSUPP * @mtd: MTD device structure * @chip: nand chip info structure * @addr: feature address. @@ -4815,13 +4887,12 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, * Should be used by NAND controller drivers that do not support the SET/GET * FEATURES operations. */ -int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) +int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip, + int addr, u8 *subfeature_param) { return -ENOTSUPP; } -EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp); +EXPORT_SYMBOL(nand_get_set_features_notsupp); /** * nand_suspend - [MTD Interface] Suspend the NAND flash @@ -4878,10 +4949,10 @@ static void nand_set_defaults(struct nand_chip *chip) chip->select_chip = nand_select_chip; /* set for ONFI nand */ - if (!chip->onfi_set_features) - chip->onfi_set_features = nand_onfi_set_features; - if (!chip->onfi_get_features) - chip->onfi_get_features = nand_onfi_get_features; + if (!chip->set_features) + chip->set_features = nand_default_set_features; + if (!chip->get_features) + chip->get_features = nand_default_get_features; /* If called twice, pointers that depend on busw may need to be reset */ if (!chip->read_byte || chip->read_byte == nand_read_byte) @@ -5021,7 +5092,7 @@ ext_out: static int nand_flash_detect_onfi(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_onfi_params *p = &chip->onfi_params; + struct nand_onfi_params *p; char id[4]; int i, ret, val; @@ -5030,14 +5101,23 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) if (ret || strncmp(id, "ONFI", 4)) return 0; + /* ONFI chip: allocate a buffer to hold its parameter page */ + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + ret = nand_read_param_page_op(chip, 0, NULL, 0); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_onfi_param_page; + } for (i = 0; i < 3; i++) { ret = nand_read_data_op(chip, p, sizeof(*p), true); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_onfi_param_page; + } if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { @@ -5047,31 +5127,33 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) if (i == 3) { pr_err("Could not find valid ONFI parameter page; aborting\n"); - return 0; + goto free_onfi_param_page; } /* Check version */ val = le16_to_cpu(p->revision); if (val & (1 << 5)) - chip->onfi_version = 23; + chip->parameters.onfi.version = 23; else if (val & (1 << 4)) - chip->onfi_version = 22; + chip->parameters.onfi.version = 22; else if (val & (1 << 3)) - chip->onfi_version = 21; + chip->parameters.onfi.version = 21; else if (val & (1 << 2)) - chip->onfi_version = 20; + chip->parameters.onfi.version = 20; else if (val & (1 << 1)) - chip->onfi_version = 10; + chip->parameters.onfi.version = 10; - if (!chip->onfi_version) { + if (!chip->parameters.onfi.version) { pr_info("unsupported ONFI version: %d\n", val); - return 0; + goto free_onfi_param_page; + } else { + ret = 1; } sanitize_string(p->manufacturer, sizeof(p->manufacturer)); sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; + strncpy(chip->parameters.model, p->model, + sizeof(chip->parameters.model) - 1); mtd->writesize = le32_to_cpu(p->byte_per_page); @@ -5093,14 +5175,14 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun); chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun); - if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) + if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS) chip->options |= NAND_BUSWIDTH_16; if (p->ecc_bits != 0xff) { chip->ecc_strength_ds = p->ecc_bits; chip->ecc_step_ds = 512; - } else if (chip->onfi_version >= 21 && - (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { + } else if (chip->parameters.onfi.version >= 21 && + (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) { /* * The nand_flash_detect_ext_param_page() uses the @@ -5118,7 +5200,28 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) pr_warn("Could not retrieve ONFI ECC requirements\n"); } - return 1; + /* Save some parameters from the parameter page for future use */ + if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) { + chip->parameters.supports_set_get_features = true; + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + } + chip->parameters.onfi.tPROG = le16_to_cpu(p->t_prog); + chip->parameters.onfi.tBERS = le16_to_cpu(p->t_bers); + chip->parameters.onfi.tR = le16_to_cpu(p->t_r); + chip->parameters.onfi.tCCS = le16_to_cpu(p->t_ccs); + chip->parameters.onfi.async_timing_mode = + le16_to_cpu(p->async_timing_mode); + chip->parameters.onfi.vendor_revision = + le16_to_cpu(p->vendor_revision); + memcpy(chip->parameters.onfi.vendor, p->vendor, + sizeof(p->vendor)); + +free_onfi_param_page: + kfree(p); + return ret; } /* @@ -5127,8 +5230,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) static int nand_flash_detect_jedec(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_jedec_params *p = &chip->jedec_params; + struct nand_jedec_params *p; struct jedec_ecc_info *ecc; + int jedec_version = 0; char id[5]; int i, val, ret; @@ -5137,14 +5241,23 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) if (ret || strncmp(id, "JEDEC", sizeof(id))) return 0; + /* JEDEC chip: allocate a buffer to hold its parameter page */ + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + ret = nand_read_param_page_op(chip, 0x40, NULL, 0); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_jedec_param_page; + } for (i = 0; i < 3; i++) { ret = nand_read_data_op(chip, p, sizeof(*p), true); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_jedec_param_page; + } if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == le16_to_cpu(p->crc)) @@ -5153,25 +5266,25 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) if (i == 3) { pr_err("Could not find valid JEDEC parameter page; aborting\n"); - return 0; + goto free_jedec_param_page; } /* Check version */ val = le16_to_cpu(p->revision); if (val & (1 << 2)) - chip->jedec_version = 10; + jedec_version = 10; else if (val & (1 << 1)) - chip->jedec_version = 1; /* vendor specific version */ + jedec_version = 1; /* vendor specific version */ - if (!chip->jedec_version) { + if (!jedec_version) { pr_info("unsupported JEDEC version: %d\n", val); - return 0; + goto free_jedec_param_page; } sanitize_string(p->manufacturer, sizeof(p->manufacturer)); sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; + strncpy(chip->parameters.model, p->model, + sizeof(chip->parameters.model) - 1); mtd->writesize = le32_to_cpu(p->byte_per_page); @@ -5186,7 +5299,7 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; chip->bits_per_cell = p->bits_per_cell; - if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) + if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS) chip->options |= NAND_BUSWIDTH_16; /* ECC info */ @@ -5199,7 +5312,9 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) pr_warn("Invalid codeword size\n"); } - return 1; +free_jedec_param_page: + kfree(p); + return ret; } /* @@ -5358,8 +5473,8 @@ static bool find_full_id_nand(struct nand_chip *chip, chip->onfi_timing_mode_default = type->onfi_timing_mode_default; - if (!mtd->name) - mtd->name = type->name; + strncpy(chip->parameters.model, type->name, + sizeof(chip->parameters.model) - 1); return true; } @@ -5498,22 +5613,28 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) } } - chip->onfi_version = 0; + chip->parameters.onfi.version = 0; if (!type->name || !type->pagesize) { /* Check if the chip is ONFI compliant */ - if (nand_flash_detect_onfi(chip)) + ret = nand_flash_detect_onfi(chip); + if (ret < 0) + return ret; + else if (ret) goto ident_done; /* Check if the chip is JEDEC compliant */ - if (nand_flash_detect_jedec(chip)) + ret = nand_flash_detect_jedec(chip); + if (ret < 0) + return ret; + else if (ret) goto ident_done; } if (!type->name) return -ENODEV; - if (!mtd->name) - mtd->name = type->name; + strncpy(chip->parameters.model, type->name, + sizeof(chip->parameters.model) - 1); chip->chipsize = (uint64_t)type->chipsize << 20; @@ -5526,6 +5647,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) chip->options |= type->options; ident_done: + if (!mtd->name) + mtd->name = chip->parameters.model; if (chip->options & NAND_BUSWIDTH_AUTO) { WARN_ON(busw & NAND_BUSWIDTH_16); @@ -5572,17 +5695,8 @@ ident_done: pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", maf_id, dev_id); - - if (chip->onfi_version) - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - chip->onfi_params.model); - else if (chip->jedec_version) - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - chip->jedec_params.model); - else - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - type->name); - + pr_info("%s %s\n", nand_manufacturer_name(manufacturer), + chip->parameters.model); pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); @@ -6474,10 +6588,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* Enter fastest possible mode on all dies. */ for (i = 0; i < chip->numchips; i++) { - chip->select_chip(mtd, i); ret = nand_setup_data_interface(chip, i); - chip->select_chip(mtd, -1); - if (ret) goto err_nand_manuf_cleanup; } diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index 36092850be2c..d9f4ceff2568 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -852,7 +852,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, } memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; einfo.addr = to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c index 505441c9373b..7f11b68f6db1 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/raw/nand_bch.c @@ -95,7 +95,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, errloc[i]); } } else if (count < 0) { - printk(KERN_ERR "ecc unrecoverable error\n"); + pr_err("ecc unrecoverable error\n"); count = -EBADMSG; } return count; @@ -134,7 +134,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) } if (!eccsize || !eccbytes) { - printk(KERN_WARNING "ecc parameters not supplied\n"); + pr_warn("ecc parameters not supplied\n"); goto fail; } @@ -151,8 +151,8 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) /* verify that eccbytes has the expected value */ if (nbc->bch->ecc_bytes != eccbytes) { - printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", - eccbytes, nbc->bch->ecc_bytes); + pr_warn("invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); goto fail; } @@ -166,7 +166,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) /* sanity checks */ if (8*(eccsize+eccbytes) >= (1 << m)) { - printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + pr_warn("eccsize %u is too large\n", eccsize); goto fail; } @@ -181,7 +181,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) nand->ecc.steps = eccsteps; nand->ecc.total = eccsteps * eccbytes; if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { - printk(KERN_WARNING "invalid ecc layout\n"); + pr_warn("invalid ecc layout\n"); goto fail; } diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c index 7613a0388044..8e132edbc5ce 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/raw/nand_ecc.c @@ -2,8 +2,6 @@ * This file contains an ECC algorithm that detects and corrects 1 bit * errors in a 256 byte block of data. * - * drivers/mtd/nand/nand_ecc.c - * * Copyright © 2008 Koninklijke Philips Electronics NV. * Author: Frans Meulenbroeks * @@ -30,15 +28,6 @@ * */ -/* - * The STANDALONE macro is useful when running the code outside the kernel - * e.g. when running the code in a testbed or a benchmark program. - * When STANDALONE is used, the module related macros are commented out - * as well as the linux include files. - * Instead a private definition of mtd_info is given to satisfy the compiler - * (the code does not use mtd_info, so the code does not care) - */ -#ifndef STANDALONE #include <linux/types.h> #include <linux/kernel.h> #include <linux/module.h> @@ -46,17 +35,6 @@ #include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <asm/byteorder.h> -#else -#include <stdint.h> -struct mtd_info; -#define EXPORT_SYMBOL(x) /* x */ - -#define MODULE_LICENSE(x) /* x */ -#define MODULE_AUTHOR(x) /* x */ -#define MODULE_DESCRIPTION(x) /* x */ - -#define pr_err printf -#endif /* * invparity is a 256 byte table that contains the odd parity diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index d542908a0ebb..d542908a0ebb 100644 --- a/drivers/mtd/nand/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index 5423c3bb388e..5423c3bb388e 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index d290ff2a6d2f..7ed1f87e742a 100644 --- a/drivers/mtd/nand/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -22,6 +22,19 @@ static int macronix_nand_init(struct nand_chip *chip) if (nand_is_slc(chip)) chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; + /* + * MX30LF2G18AC chip does not support using SET/GET_FEATURES to change + * the timings unlike what is declared in the parameter page. Unflag + * this feature to avoid unnecessary downturns. + */ + if (chip->parameters.supports_set_get_features && + !strcmp("MX30LF2G18AC", chip->parameters.model)) { + bitmap_clear(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + bitmap_clear(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + } + return 0; } diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 02e109ae73f1..0af45b134c0c 100644 --- a/drivers/mtd/nand/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -48,8 +48,7 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) struct nand_chip *chip = mtd_to_nand(mtd); u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; - return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, - feature); + return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); } /* @@ -57,17 +56,18 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) */ static int micron_nand_onfi_init(struct nand_chip *chip) { - struct nand_onfi_params *p = &chip->onfi_params; - struct nand_onfi_vendor_micron *micron = (void *)p->vendor; + struct nand_parameters *p = &chip->parameters; + struct nand_onfi_vendor_micron *micron = (void *)p->onfi.vendor; - if (!chip->onfi_version) - return 0; - - if (le16_to_cpu(p->vendor_revision) < 1) - return 0; + if (chip->parameters.onfi.version && p->onfi.vendor_revision) { + chip->read_retries = micron->read_retry_options; + chip->setup_read_retry = micron_nand_setup_read_retry; + } - chip->read_retries = micron->read_retry_options; - chip->setup_read_retry = micron_nand_setup_read_retry; + if (p->supports_set_get_features) { + set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list); + set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list); + } return 0; } @@ -108,8 +108,7 @@ static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) if (enable) feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; - return chip->onfi_set_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); } static int @@ -209,7 +208,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; int ret; - if (chip->onfi_version == 0) + if (!chip->parameters.onfi.version) return MICRON_ON_DIE_UNSUPPORTED; if (chip->bits_per_cell != 1) @@ -219,8 +218,10 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (ret) return MICRON_ON_DIE_UNSUPPORTED; - chip->onfi_get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); + if (ret < 0) + return ret; + if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0) return MICRON_ON_DIE_UNSUPPORTED; @@ -228,8 +229,10 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (ret) return MICRON_ON_DIE_UNSUPPORTED; - chip->onfi_get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); + if (ret < 0) + return ret; + if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) return MICRON_ON_DIE_MANDATORY; @@ -237,7 +240,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) * Some Micron NANDs have an on-die ECC of 4/512, some other * 8/512. We only support the former. */ - if (chip->onfi_params.ecc_bits != 4) + if (chip->ecc_strength_ds != 4) return MICRON_ON_DIE_UNSUPPORTED; return MICRON_ON_DIE_SUPPORTED; diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c index ef022f62f74c..ef022f62f74c 100644 --- a/drivers/mtd/nand/nand_samsung.c +++ b/drivers/mtd/nand/raw/nand_samsung.c diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c index 9400d039ddbd..7c4e4a371bbc 100644 --- a/drivers/mtd/nand/nand_timings.c +++ b/drivers/mtd/nand/raw/nand_timings.c @@ -306,17 +306,17 @@ int onfi_fill_data_interface(struct nand_chip *chip, * tR, tPROG, tCCS, ... * These information are part of the ONFI parameter page. */ - if (chip->onfi_version) { - struct nand_onfi_params *params = &chip->onfi_params; + if (chip->parameters.onfi.version) { + struct nand_parameters *params = &chip->parameters; struct nand_sdr_timings *timings = &iface->timings.sdr; /* microseconds -> picoseconds */ - timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); - timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); - timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); + timings->tPROG_max = 1000000ULL * params->onfi.tPROG; + timings->tBERS_max = 1000000ULL * params->onfi.tBERS; + timings->tR_max = 1000000ULL * params->onfi.tR; /* nanoseconds -> picoseconds */ - timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); + timings->tCCS_min = 1000UL * params->onfi.tCCS; } return 0; diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c index 57df857074e6..ab43f027cd23 100644 --- a/drivers/mtd/nand/nand_toshiba.c +++ b/drivers/mtd/nand/raw/nand_toshiba.c @@ -35,6 +35,32 @@ static void toshiba_nand_decode_id(struct nand_chip *chip) (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ && !(chip->id.data[4] & 0x80) /* !BENAND */) mtd->oobsize = 32 * mtd->writesize >> 9; + + /* + * Extract ECC requirements from 6th id byte. + * For Toshiba SLC, ecc requrements are as follows: + * - 43nm: 1 bit ECC for each 512Byte is required. + * - 32nm: 4 bit ECC for each 512Byte is required. + * - 24nm: 8 bit ECC for each 512Byte is required. + */ + if (chip->id.len >= 6 && nand_is_slc(chip)) { + chip->ecc_step_ds = 512; + switch (chip->id.data[5] & 0x7) { + case 0x4: + chip->ecc_strength_ds = 1; + break; + case 0x5: + chip->ecc_strength_ds = 4; + break; + case 0x6: + chip->ecc_strength_ds = 8; + break; + default: + WARN(1, "Could not get ECC info"); + chip->ecc_step_ds = 0; + break; + } + } } static int toshiba_nand_init(struct nand_chip *chip) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index 44322a363ba5..e027c6f9d327 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -23,6 +23,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */ +#define pr_fmt(fmt) "[nandsim]" fmt + #include <linux/init.h> #include <linux/types.h> #include <linux/module.h> @@ -179,20 +181,17 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " /* The largest possible page size */ #define NS_LARGEST_PAGE_SIZE 4096 -/* The prefix for simulator output */ -#define NS_OUTPUT_PREFIX "[nandsim]" - /* Simulator's output macros (logging, debugging, warning, error) */ #define NS_LOG(args...) \ - do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) + do { if (log) pr_debug(" log: " args); } while(0) #define NS_DBG(args...) \ - do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) + do { if (dbg) pr_debug(" debug: " args); } while(0) #define NS_WARN(args...) \ - do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) + do { pr_warn(" warning: " args); } while(0) #define NS_ERR(args...) \ - do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) + do { pr_err(" error: " args); } while(0) #define NS_INFO(args...) \ - do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) + do { pr_info(" " args); } while(0) /* Busy-wait delay macros (microseconds, milliseconds) */ #define NS_UDELAY(us) \ diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index d8a806894937..d8a806894937 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/raw/nuc900_nand.c index af5b32c9a791..af5b32c9a791 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/raw/nuc900_nand.c diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/raw/omap2.c index 8cdf7d3d8fa7..e50c64adc3c8 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -2263,12 +2263,15 @@ scan_tail: err = mtd_device_register(mtd, NULL, 0); if (err) - goto return_error; + goto cleanup_nand; platform_set_drvdata(pdev, mtd); return 0; +cleanup_nand: + nand_cleanup(nand_chip); + return_error: if (!IS_ERR_OR_NULL(info->dma)) dma_release_channel(info->dma); diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c index a3f32f939cc1..a3f32f939cc1 100644 --- a/drivers/mtd/nand/omap_elm.c +++ b/drivers/mtd/nand/raw/omap_elm.c diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c index 5a5aa1f07d07..7825fd3ce66b 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/orion_nand.c - * * NAND support for Marvell Orion SoC platforms * * Tzachi Perelstein <tzachi@marvell.com> diff --git a/drivers/mtd/nand/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c index d649d5944826..d649d5944826 100644 --- a/drivers/mtd/nand/oxnas_nand.c +++ b/drivers/mtd/nand/raw/oxnas_nand.c diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c index a47a7e4bd25a..a47a7e4bd25a 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/raw/pasemi_nand.c diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c index 925a1323604d..925a1323604d 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/raw/plat_nand.c diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 563b759ffca6..b554fb6e609c 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2651,8 +2651,8 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc, chip->read_byte = qcom_nandc_read_byte; chip->read_buf = qcom_nandc_read_buf; chip->write_buf = qcom_nandc_write_buf; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; /* * the bad block marker is readable only when we read the last codeword diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/raw/r852.c index 595635b9e9de..dcdeb0660e5e 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -7,6 +7,9 @@ * published by the Free Software Foundation. */ +#define DRV_NAME "r852" +#define pr_fmt(fmt) DRV_NAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/jiffies.h> @@ -932,7 +935,7 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) &dev->card_detect_work, 0); - printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n"); + pr_notice("driver loaded successfully\n"); return 0; error10: diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/raw/r852.h index 8713c57f6207..1eed2fc2fa42 100644 --- a/drivers/mtd/nand/r852.h +++ b/drivers/mtd/nand/raw/r852.h @@ -144,17 +144,14 @@ struct r852_device { uint8_t ctlreg; /* cached contents of control reg */ }; -#define DRV_NAME "r852" - - #define dbg(format, ...) \ if (debug) \ - printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + pr_debug(format "\n", ## __VA_ARGS__) #define dbg_verbose(format, ...) \ if (debug > 1) \ - printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + pr_debug(format "\n", ## __VA_ARGS__) #define message(format, ...) \ - printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) + pr_info(format "\n", ## __VA_ARGS__) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index 4c383eeec6f6..1bc0458063d8 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -1,5 +1,4 @@ -/* linux/drivers/mtd/nand/s3c2410.c - * +/* * Copyright © 2004-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks <ben@simtec.co.uk> @@ -125,13 +124,11 @@ struct s3c2410_nand_info; * @chip: The NAND chip information. * @set: The platform information supplied for this set of NAND chips. * @info: Link back to the hardware information. - * @scan_res: The result from calling nand_scan_ident(). */ struct s3c2410_nand_mtd { struct nand_chip chip; struct s3c2410_nand_set *set; struct s3c2410_nand_info *info; - int scan_res; }; enum s3c_cpu_type { @@ -1164,17 +1161,19 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; s3c2410_nand_init_chip(info, nmtd, sets); - nmtd->scan_res = nand_scan_ident(mtd, - (sets) ? sets->nr_chips : 1, - NULL); + err = nand_scan_ident(mtd, (sets) ? sets->nr_chips : 1, NULL); + if (err) + goto exit_error; - if (nmtd->scan_res == 0) { - err = s3c2410_nand_update_chip(info, nmtd); - if (err < 0) - goto exit_error; - nand_scan_tail(mtd); - s3c2410_nand_add_partition(info, nmtd, sets); - } + err = s3c2410_nand_update_chip(info, nmtd); + if (err < 0) + goto exit_error; + + err = nand_scan_tail(mtd); + if (err) + goto exit_error; + + s3c2410_nand_add_partition(info, nmtd, sets); if (sets != NULL) sets++; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index c4e7755448e6..c7abceffcc40 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -877,7 +877,7 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, else if (!flctl->seqin_column) execmd_write_page_sector(mtd); else - printk(KERN_ERR "Invalid address !?\n"); + pr_err("Invalid address !?\n"); break; } set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); @@ -1180,8 +1180,8 @@ static int flctl_probe(struct platform_device *pdev) nand->read_buf = flctl_read_buf; nand->select_chip = flctl_select_chip; nand->cmdfunc = flctl_cmdfunc; - nand->onfi_set_features = nand_onfi_get_set_features_notsupp; - nand->onfi_get_features = nand_onfi_get_set_features_notsupp; + nand->set_features = nand_get_set_features_notsupp; + nand->get_features = nand_get_set_features_notsupp; if (pdata->flcmncr_val & SEL_16BIT) nand->options |= NAND_BUSWIDTH_16; @@ -1214,9 +1214,13 @@ static int flctl_probe(struct platform_device *pdev) goto err_chip; ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); + if (ret) + goto cleanup_nand; return 0; +cleanup_nand: + nand_cleanup(nand); err_chip: flctl_release_dma(flctl); pm_runtime_disable(&pdev->dev); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c index f59c455d9f51..e93df02c825e 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/sharpsl.c - * * Copyright (C) 2004 Richard Purdie * Copyright (C) 2008 Dmitry Baryshkov * diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/raw/sm_common.c index c378705c6e2b..7f5044a79f01 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/raw/sm_common.c @@ -119,9 +119,8 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) ret = mtd_write_oob(mtd, ofs, &ops); if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { - printk(KERN_NOTICE - "sm_common: can't mark sector at %i as bad\n", - (int)ofs); + pr_notice("sm_common: can't mark sector at %i as bad\n", + (int)ofs); return -EIO; } diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/raw/sm_common.h index 1581671b05ae..1581671b05ae 100644 --- a/drivers/mtd/nand/sm_common.h +++ b/drivers/mtd/nand/raw/sm_common.h diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c index 575997d0ef8a..9824a9923583 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/raw/socrates_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/socrates_nand.c - * * Copyright © 2008 Ilya Yanok, Emcraft Systems * * diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index f5a55c63935c..aad42812a353 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1475,92 +1475,18 @@ pio_fallback: return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page); } -static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; - int ret, i, cur_off = 0; - bool raw_mode = false; - - nand_read_page_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); - int oob_off = data_off + ecc->size; - u8 *data = buf + (i * ecc->size); - u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); - - ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, - oob_off, &cur_off, - &max_bitflips, !i, - oob_required, - page); - if (ret < 0) - return ret; - else if (ret) - raw_mode = true; - } - - if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, - !raw_mode, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return max_bitflips; -} - -static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); - int oob_off = data_off + ecc->size; - const u8 *data = buf + (i * ecc->size); - const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); - - ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, - oob, oob_off, &cur_off, - false, page); - if (ret) - return ret; - } - - if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, - &cur_off, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return nand_prog_page_end_op(chip); -} - -static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, - struct nand_chip *chip, - int page) +static int sunxi_nfc_hw_ecc_read_oob(struct mtd_info *mtd, + struct nand_chip *chip, + int page) { chip->pagebuf = -1; return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page); } -static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, - struct nand_chip *chip, - int page) +static int sunxi_nfc_hw_ecc_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, + int page) { int ret; @@ -1801,9 +1727,14 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { .free = sunxi_nand_ooblayout_free, }; -static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc, - struct device_node *np) +static void sunxi_nand_hw_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) +{ + kfree(ecc->priv); +} + +static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) { static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; struct nand_chip *nand = mtd_to_nand(mtd); @@ -1889,37 +1820,11 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, goto err; } - ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob; - ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob; + ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; + ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); ecc->priv = data; - return 0; - -err: - kfree(data); - - return ret; -} - -static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) -{ - kfree(ecc->priv); -} - -static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc, - struct device_node *np) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - - ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); - if (ret) - return ret; - if (nfc->dmac) { ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; @@ -1937,33 +1842,18 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, ecc->write_oob_raw = nand_write_oob_std; return 0; -} - -static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc, - struct device_node *np) -{ - int ret; - - ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); - if (ret) - return ret; - ecc->prepad = 4; - ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; - ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; - ecc->read_oob_raw = nand_read_oob_syndrome; - ecc->write_oob_raw = nand_write_oob_syndrome; +err: + kfree(data); - return 0; + return ret; } static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) { switch (ecc->mode) { case NAND_ECC_HW: - case NAND_ECC_HW_SYNDROME: - sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); + sunxi_nand_hw_ecc_ctrl_cleanup(ecc); break; case NAND_ECC_NONE: default: @@ -1991,11 +1881,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, if (ret) return ret; break; - case NAND_ECC_HW_SYNDROME: - ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np); - if (ret) - return ret; - break; case NAND_ECC_NONE: case NAND_ECC_SOFT: break; diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index c5bee00b7f5e..f54518ffb36a 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -591,8 +591,10 @@ static int chip_init(struct device *dev, struct device_node *np) tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE); err = mtd_device_register(mtd, NULL, 0); - if (err) + if (err) { + nand_cleanup(chip); return err; + } nfc->chips[cs] = tchip; diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index dcaa924502de..dcaa924502de 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index b567d212fe7d..b567d212fe7d 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index f367144f3c6f..d5a22fc96878 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -36,6 +36,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/swab.h> #define DRV_NAME "vf610_nfc" @@ -59,20 +60,21 @@ #define OOB_64 0x0040 #define OOB_MAX 0x0100 -/* - * NFC_CMD2[CODE] values. See section: - * - 31.4.7 Flash Command Code Description, Vybrid manual - * - 23.8.6 Flash Command Sequencer, MPC5125 manual - * - * Briefly these are bitmasks of controller cycles. - */ -#define READ_PAGE_CMD_CODE 0x7EE0 -#define READ_ONFI_PARAM_CMD_CODE 0x4860 -#define PROGRAM_PAGE_CMD_CODE 0x7FC0 -#define ERASE_CMD_CODE 0x4EC0 -#define READ_ID_CMD_CODE 0x4804 -#define RESET_CMD_CODE 0x4040 -#define STATUS_READ_CMD_CODE 0x4068 +/* NFC_CMD2[CODE] controller cycle bit masks */ +#define COMMAND_CMD_BYTE1 BIT(14) +#define COMMAND_CAR_BYTE1 BIT(13) +#define COMMAND_CAR_BYTE2 BIT(12) +#define COMMAND_RAR_BYTE1 BIT(11) +#define COMMAND_RAR_BYTE2 BIT(10) +#define COMMAND_RAR_BYTE3 BIT(9) +#define COMMAND_NADDR_BYTES(x) GENMASK(13, 13 - (x) + 1) +#define COMMAND_WRITE_DATA BIT(8) +#define COMMAND_CMD_BYTE2 BIT(7) +#define COMMAND_RB_HANDSHAKE BIT(6) +#define COMMAND_READ_DATA BIT(5) +#define COMMAND_CMD_BYTE3 BIT(4) +#define COMMAND_READ_STATUS BIT(3) +#define COMMAND_READ_ID BIT(2) /* NFC ECC mode define */ #define ECC_BYPASS 0 @@ -97,10 +99,13 @@ /* NFC_COL_ADDR Field */ #define COL_ADDR_MASK 0x0000FFFF #define COL_ADDR_SHIFT 0 +#define COL_ADDR(pos, val) (((val) & 0xFF) << (8 * (pos))) /* NFC_ROW_ADDR Field */ #define ROW_ADDR_MASK 0x00FFFFFF #define ROW_ADDR_SHIFT 0 +#define ROW_ADDR(pos, val) (((val) & 0xFF) << (8 * (pos))) + #define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 #define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 #define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 @@ -142,13 +147,6 @@ #define ECC_STATUS_MASK 0x80 #define ECC_STATUS_ERR_COUNT 0x3F -enum vf610_nfc_alt_buf { - ALT_BUF_DATA = 0, - ALT_BUF_ID = 1, - ALT_BUF_STAT = 2, - ALT_BUF_ONFI = 3, -}; - enum vf610_nfc_variant { NFC_VFC610 = 1, }; @@ -158,13 +156,15 @@ struct vf610_nfc { struct device *dev; void __iomem *regs; struct completion cmd_done; - uint buf_offset; - int write_sz; /* Status and ID are in alternate locations. */ - enum vf610_nfc_alt_buf alt_buf; enum vf610_nfc_variant variant; struct clk *clk; - bool use_hw_ecc; + /* + * Indicate that user data is accessed (full page/oob). This is + * useful to indicate the driver whether to swap byte endianness. + * See comments in vf610_nfc_rd_from_sram/vf610_nfc_wr_to_sram. + */ + bool data_access; u32 ecc_mode; }; @@ -173,6 +173,11 @@ static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd) return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip); } +static inline struct vf610_nfc *chip_to_nfc(struct nand_chip *chip) +{ + return container_of(chip, struct vf610_nfc, chip); +} + static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg) { return readl(nfc->regs + reg); @@ -200,18 +205,84 @@ static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg, (vf610_nfc_read(nfc, reg) & (~mask)) | val << shift); } -static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src, - size_t n) +static inline bool vf610_nfc_kernel_is_little_endian(void) { - /* - * Use this accessor for the internal SRAM buffers. On the ARM - * Freescale Vybrid SoC it's known that the driver can treat - * the SRAM buffer as if it's memory. Other platform might need - * to treat the buffers differently. - * - * For the time being, use memcpy - */ - memcpy(dst, src, n); +#ifdef __LITTLE_ENDIAN + return true; +#else + return false; +#endif +} + +/** + * Read accessor for internal SRAM buffer + * @dst: destination address in regular memory + * @src: source address in SRAM buffer + * @len: bytes to copy + * @fix_endian: Fix endianness if required + * + * Use this accessor for the internal SRAM buffers. On the ARM + * Freescale Vybrid SoC it's known that the driver can treat + * the SRAM buffer as if it's memory. Other platform might need + * to treat the buffers differently. + * + * The controller stores bytes from the NAND chip internally in big + * endianness. On little endian platforms such as Vybrid this leads + * to reversed byte order. + * For performance reason (and earlier probably due to unawareness) + * the driver avoids correcting endianness where it has control over + * write and read side (e.g. page wise data access). + */ +static inline void vf610_nfc_rd_from_sram(void *dst, const void __iomem *src, + size_t len, bool fix_endian) +{ + if (vf610_nfc_kernel_is_little_endian() && fix_endian) { + unsigned int i; + + for (i = 0; i < len; i += 4) { + u32 val = swab32(__raw_readl(src + i)); + + memcpy(dst + i, &val, min(sizeof(val), len - i)); + } + } else { + memcpy_fromio(dst, src, len); + } +} + +/** + * Write accessor for internal SRAM buffer + * @dst: destination address in SRAM buffer + * @src: source address in regular memory + * @len: bytes to copy + * @fix_endian: Fix endianness if required + * + * Use this accessor for the internal SRAM buffers. On the ARM + * Freescale Vybrid SoC it's known that the driver can treat + * the SRAM buffer as if it's memory. Other platform might need + * to treat the buffers differently. + * + * The controller stores bytes from the NAND chip internally in big + * endianness. On little endian platforms such as Vybrid this leads + * to reversed byte order. + * For performance reason (and earlier probably due to unawareness) + * the driver avoids correcting endianness where it has control over + * write and read side (e.g. page wise data access). + */ +static inline void vf610_nfc_wr_to_sram(void __iomem *dst, const void *src, + size_t len, bool fix_endian) +{ + if (vf610_nfc_kernel_is_little_endian() && fix_endian) { + unsigned int i; + + for (i = 0; i < len; i += 4) { + u32 val; + + memcpy(&val, src + i, min(sizeof(val), len - i)); + __raw_writel(swab32(val), dst + i); + } + } else { + memcpy_toio(dst, src, len); + } } /* Clear flags for upcoming command */ @@ -243,250 +314,185 @@ static void vf610_nfc_done(struct vf610_nfc *nfc) vf610_nfc_clear_status(nfc); } -static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col) +static irqreturn_t vf610_nfc_irq(int irq, void *data) { - u32 flash_id; + struct mtd_info *mtd = data; + struct vf610_nfc *nfc = mtd_to_nfc(mtd); - if (col < 4) { - flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1); - flash_id >>= (3 - col) * 8; - } else { - flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2); - flash_id >>= 24; - } + vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); + complete(&nfc->cmd_done); - return flash_id & 0xff; + return IRQ_HANDLED; } -static u8 vf610_nfc_get_status(struct vf610_nfc *nfc) +static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode) { - return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; + vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, + CONFIG_ECC_MODE_MASK, + CONFIG_ECC_MODE_SHIFT, ecc_mode); } -static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1, - u32 cmd_code) +static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) { - u32 tmp; - - vf610_nfc_clear_status(nfc); - - tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2); - tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK); - tmp |= cmd_byte1 << CMD_BYTE1_SHIFT; - tmp |= cmd_code << CMD_CODE_SHIFT; - vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp); + vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); } -static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1, - u32 cmd_byte2, u32 cmd_code) +static inline void vf610_nfc_run(struct vf610_nfc *nfc, u32 col, u32 row, + u32 cmd1, u32 cmd2, u32 trfr_sz) { - u32 tmp; + vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK, + COL_ADDR_SHIFT, col); + + vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, row); + + vf610_nfc_write(nfc, NFC_SECTOR_SIZE, trfr_sz); + vf610_nfc_write(nfc, NFC_FLASH_CMD1, cmd1); + vf610_nfc_write(nfc, NFC_FLASH_CMD2, cmd2); - vf610_nfc_send_command(nfc, cmd_byte1, cmd_code); + dev_dbg(nfc->dev, + "col 0x%04x, row 0x%08x, cmd1 0x%08x, cmd2 0x%08x, len %d\n", + col, row, cmd1, cmd2, trfr_sz); - tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1); - tmp &= ~CMD_BYTE2_MASK; - tmp |= cmd_byte2 << CMD_BYTE2_SHIFT; - vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp); + vf610_nfc_done(nfc); } -static irqreturn_t vf610_nfc_irq(int irq, void *data) +static inline const struct nand_op_instr * +vf610_get_next_instr(const struct nand_subop *subop, int *op_id) { - struct mtd_info *mtd = data; - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + if (*op_id + 1 >= subop->ninstrs) + return NULL; - vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); - complete(&nfc->cmd_done); + (*op_id)++; - return IRQ_HANDLED; + return &subop->instrs[*op_id]; } -static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page) +static int vf610_nfc_cmd(struct nand_chip *chip, + const struct nand_subop *subop) { - if (column != -1) { - if (nfc->chip.options & NAND_BUSWIDTH_16) - column = column / 2; - vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK, - COL_ADDR_SHIFT, column); + const struct nand_op_instr *instr; + struct vf610_nfc *nfc = chip_to_nfc(chip); + int op_id = -1, trfr_sz = 0, offset; + u32 col = 0, row = 0, cmd1 = 0, cmd2 = 0, code = 0; + bool force8bit = false; + + /* + * Some ops are optional, but the hardware requires the operations + * to be in this exact order. + * The op parser enforces the order and makes sure that there isn't + * a read and write element in a single operation. + */ + instr = vf610_get_next_instr(subop, &op_id); + if (!instr) + return -EINVAL; + + if (instr && instr->type == NAND_OP_CMD_INSTR) { + cmd2 |= instr->ctx.cmd.opcode << CMD_BYTE1_SHIFT; + code |= COMMAND_CMD_BYTE1; + + instr = vf610_get_next_instr(subop, &op_id); } - if (page != -1) - vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK, - ROW_ADDR_SHIFT, page); -} -static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode) -{ - vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ecc_mode); -} + if (instr && instr->type == NAND_OP_ADDR_INSTR) { + int naddrs = nand_subop_get_num_addr_cyc(subop, op_id); + int i = nand_subop_get_addr_start_off(subop, op_id); -static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) -{ - vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); -} + for (; i < naddrs; i++) { + u8 val = instr->ctx.addr.addrs[i]; -static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, - int column, int page) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; + if (i < 2) + col |= COL_ADDR(i, val); + else + row |= ROW_ADDR(i - 2, val); + } + code |= COMMAND_NADDR_BYTES(naddrs); - nfc->buf_offset = max(column, 0); - nfc->alt_buf = ALT_BUF_DATA; + instr = vf610_get_next_instr(subop, &op_id); + } - switch (command) { - case NAND_CMD_SEQIN: - /* Use valid column/page from preread... */ - vf610_nfc_addr_cycle(nfc, column, page); - nfc->buf_offset = 0; + if (instr && instr->type == NAND_OP_DATA_OUT_INSTR) { + trfr_sz = nand_subop_get_data_len(subop, op_id); + offset = nand_subop_get_data_start_off(subop, op_id); + force8bit = instr->ctx.data.force_8bit; /* - * SEQIN => data => PAGEPROG sequence is done by the controller - * hence we do not need to issue the command here... + * Don't fix endianness on page access for historical reasons. + * See comment in vf610_nfc_wr_to_sram */ - return; - case NAND_CMD_PAGEPROG: - trfr_sz += nfc->write_sz; - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN, - command, PROGRAM_PAGE_CMD_CODE); - if (nfc->use_hw_ecc) - vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); - else - vf610_nfc_ecc_mode(nfc, ECC_BYPASS); - break; - - case NAND_CMD_RESET: - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_command(nfc, command, RESET_CMD_CODE); - break; - - case NAND_CMD_READOOB: - trfr_sz += mtd->oobsize; - column = mtd->writesize; - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_commands(nfc, NAND_CMD_READ0, - NAND_CMD_READSTART, READ_PAGE_CMD_CODE); - vf610_nfc_addr_cycle(nfc, column, page); - vf610_nfc_ecc_mode(nfc, ECC_BYPASS); - break; - - case NAND_CMD_READ0: - trfr_sz += mtd->writesize + mtd->oobsize; - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_commands(nfc, NAND_CMD_READ0, - NAND_CMD_READSTART, READ_PAGE_CMD_CODE); - vf610_nfc_addr_cycle(nfc, column, page); - vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); - break; - - case NAND_CMD_PARAM: - nfc->alt_buf = ALT_BUF_ONFI; - trfr_sz = 3 * sizeof(struct nand_onfi_params); - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE); - vf610_nfc_addr_cycle(nfc, -1, column); - vf610_nfc_ecc_mode(nfc, ECC_BYPASS); - break; - - case NAND_CMD_ERASE1: - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_commands(nfc, command, - NAND_CMD_ERASE2, ERASE_CMD_CODE); - vf610_nfc_addr_cycle(nfc, column, page); - break; - - case NAND_CMD_READID: - nfc->alt_buf = ALT_BUF_ID; - nfc->buf_offset = 0; - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE); - vf610_nfc_addr_cycle(nfc, -1, column); - break; - - case NAND_CMD_STATUS: - nfc->alt_buf = ALT_BUF_STAT; - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE); - break; - default: - return; + vf610_nfc_wr_to_sram(nfc->regs + NFC_MAIN_AREA(0) + offset, + instr->ctx.data.buf.out + offset, + trfr_sz, !nfc->data_access); + code |= COMMAND_WRITE_DATA; + + instr = vf610_get_next_instr(subop, &op_id); } - vf610_nfc_done(nfc); + if (instr && instr->type == NAND_OP_CMD_INSTR) { + cmd1 |= instr->ctx.cmd.opcode << CMD_BYTE2_SHIFT; + code |= COMMAND_CMD_BYTE2; - nfc->use_hw_ecc = false; - nfc->write_sz = 0; -} + instr = vf610_get_next_instr(subop, &op_id); + } -static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->buf_offset; + if (instr && instr->type == NAND_OP_WAITRDY_INSTR) { + code |= COMMAND_RB_HANDSHAKE; - /* Alternate buffers are only supported through read_byte */ - WARN_ON(nfc->alt_buf); + instr = vf610_get_next_instr(subop, &op_id); + } - vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); + if (instr && instr->type == NAND_OP_DATA_IN_INSTR) { + trfr_sz = nand_subop_get_data_len(subop, op_id); + offset = nand_subop_get_data_start_off(subop, op_id); + force8bit = instr->ctx.data.force_8bit; - nfc->buf_offset += len; -} + code |= COMMAND_READ_DATA; + } -static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->buf_offset; - uint l; + if (force8bit && (chip->options & NAND_BUSWIDTH_16)) + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); - l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); - vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); + cmd2 |= code << CMD_CODE_SHIFT; - nfc->write_sz += l; - nfc->buf_offset += l; -} + vf610_nfc_run(nfc, col, row, cmd1, cmd2, trfr_sz); -static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - u8 tmp; - uint c = nfc->buf_offset; - - switch (nfc->alt_buf) { - case ALT_BUF_ID: - tmp = vf610_nfc_get_id(nfc, c); - break; - case ALT_BUF_STAT: - tmp = vf610_nfc_get_status(nfc); - break; -#ifdef __LITTLE_ENDIAN - case ALT_BUF_ONFI: - /* Reverse byte since the controller uses big endianness */ - c = nfc->buf_offset ^ 0x3; - /* fall-through */ -#endif - default: - tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); - break; + if (instr && instr->type == NAND_OP_DATA_IN_INSTR) { + /* + * Don't fix endianness on page access for historical reasons. + * See comment in vf610_nfc_rd_from_sram + */ + vf610_nfc_rd_from_sram(instr->ctx.data.buf.in + offset, + nfc->regs + NFC_MAIN_AREA(0) + offset, + trfr_sz, !nfc->data_access); } - nfc->buf_offset++; - return tmp; -} -static u16 vf610_nfc_read_word(struct mtd_info *mtd) -{ - u16 tmp; + if (force8bit && (chip->options & NAND_BUSWIDTH_16)) + vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); - vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); - return tmp; + return 0; } -/* If not provided, upper layers apply a fixed delay. */ -static int vf610_nfc_dev_ready(struct mtd_info *mtd) +static const struct nand_op_parser vf610_nfc_op_parser = NAND_OP_PARSER( + NAND_OP_PARSER_PATTERN(vf610_nfc_cmd, + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5), + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, PAGE_2K + OOB_MAX), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), + NAND_OP_PARSER_PATTERN(vf610_nfc_cmd, + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, PAGE_2K + OOB_MAX)), + ); + +static int vf610_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) { - /* NFC handles R/B internally; always ready. */ - return 1; + return nand_op_parser_exec_op(chip, &vf610_nfc_op_parser, op, + check_only); } /* @@ -511,21 +517,6 @@ static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); } -/* Count the number of 0's in buff up to max_bits */ -static inline int count_written_bits(uint8_t *buff, int size, int max_bits) -{ - uint32_t *buff32 = (uint32_t *)buff; - int k, written_bits = 0; - - for (k = 0; k < (size / 4); k++) { - written_bits += hweight32(~buff32[k]); - if (unlikely(written_bits > max_bits)) - break; - } - - return written_bits; -} - static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *oob, int page) { @@ -541,9 +532,9 @@ static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, if (!(ecc_status & ECC_STATUS_MASK)) return ecc_count; - /* Read OOB without ECC unit enabled */ - vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); - vf610_nfc_read_buf(mtd, oob, mtd->oobsize); + nfc->data_access = true; + nand_read_oob_op(&nfc->chip, page, 0, oob, mtd->oobsize); + nfc->data_access = false; /* * On an erased page, bit count (including OOB) should be zero or @@ -554,15 +545,51 @@ static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, flips_threshold); } +static void vf610_nfc_fill_row(struct nand_chip *chip, int page, u32 *code, + u32 *row) +{ + *row = ROW_ADDR(0, page & 0xff) | ROW_ADDR(1, page >> 8); + *code |= COMMAND_RAR_BYTE1 | COMMAND_RAR_BYTE2; + + if (chip->options & NAND_ROW_ADDR_3) { + *row |= ROW_ADDR(2, page >> 16); + *code |= COMMAND_RAR_BYTE3; + } +} + static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int eccsize = chip->ecc.size; + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int trfr_sz = mtd->writesize + mtd->oobsize; + u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; int stat; - nand_read_page_op(chip, page, 0, buf, eccsize); + cmd2 |= NAND_CMD_READ0 << CMD_BYTE1_SHIFT; + code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2; + + vf610_nfc_fill_row(chip, page, &code, &row); + + cmd1 |= NAND_CMD_READSTART << CMD_BYTE2_SHIFT; + code |= COMMAND_CMD_BYTE2 | COMMAND_RB_HANDSHAKE | COMMAND_READ_DATA; + + cmd2 |= code << CMD_CODE_SHIFT; + + vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); + vf610_nfc_run(nfc, 0, row, cmd1, cmd2, trfr_sz); + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); + + /* + * Don't fix endianness on page access for historical reasons. + * See comment in vf610_nfc_rd_from_sram + */ + vf610_nfc_rd_from_sram(buf, nfc->regs + NFC_MAIN_AREA(0), + mtd->writesize, false); if (oob_required) - vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + vf610_nfc_rd_from_sram(chip->oob_poi, + nfc->regs + NFC_MAIN_AREA(0) + + mtd->writesize, + mtd->oobsize, false); stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); @@ -579,14 +606,103 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int trfr_sz = mtd->writesize + mtd->oobsize; + u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; + u8 status; + int ret; - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - if (oob_required) - vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + cmd2 |= NAND_CMD_SEQIN << CMD_BYTE1_SHIFT; + code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2; + + vf610_nfc_fill_row(chip, page, &code, &row); + + cmd1 |= NAND_CMD_PAGEPROG << CMD_BYTE2_SHIFT; + code |= COMMAND_CMD_BYTE2 | COMMAND_WRITE_DATA; + + /* + * Don't fix endianness on page access for historical reasons. + * See comment in vf610_nfc_wr_to_sram + */ + vf610_nfc_wr_to_sram(nfc->regs + NFC_MAIN_AREA(0), buf, + mtd->writesize, false); + + code |= COMMAND_RB_HANDSHAKE; + cmd2 |= code << CMD_CODE_SHIFT; + + vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); + vf610_nfc_run(nfc, 0, row, cmd1, cmd2, trfr_sz); + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +static int vf610_nfc_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int ret; + + nfc->data_access = true; + ret = nand_read_page_raw(mtd, chip, buf, oob_required, page); + nfc->data_access = false; + + return ret; +} + +static int vf610_nfc_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, + int oob_required, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int ret; + + nfc->data_access = true; + ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + if (!ret && oob_required) + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + nfc->data_access = false; - /* Always write whole page including OOB due to HW ECC */ - nfc->use_hw_ecc = true; - nfc->write_sz = mtd->writesize + mtd->oobsize; + if (ret) + return ret; + + return nand_prog_page_end_op(chip); +} + +static int vf610_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int ret; + + nfc->data_access = true; + ret = nand_read_oob_std(mtd, chip, page); + nfc->data_access = false; + + return ret; +} + +static int vf610_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int ret; + + nfc->data_access = true; + ret = nand_prog_page_begin_op(chip, page, mtd->writesize, + chip->oob_poi, mtd->oobsize); + nfc->data_access = false; + + if (ret) + return ret; return nand_prog_page_end_op(chip); } @@ -605,6 +721,7 @@ static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc) vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); /* Disable virtual pages, only one elementary transfer unit */ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, @@ -682,7 +799,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) dev_err(nfc->dev, "Only one NAND chip supported!\n"); err = -EINVAL; - goto error; + goto err_disable_clk; } nand_set_flash_node(chip, child); @@ -692,18 +809,11 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (!nand_get_flash_node(chip)) { dev_err(nfc->dev, "NAND chip sub-node missing!\n"); err = -ENODEV; - goto err_clk; + goto err_disable_clk; } - chip->dev_ready = vf610_nfc_dev_ready; - chip->cmdfunc = vf610_nfc_command; - chip->read_byte = vf610_nfc_read_byte; - chip->read_word = vf610_nfc_read_word; - chip->read_buf = vf610_nfc_read_buf; - chip->write_buf = vf610_nfc_write_buf; + chip->exec_op = vf610_nfc_exec_op; chip->select_chip = vf610_nfc_select_chip; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; chip->options |= NAND_NO_SUBPAGE_WRITE; @@ -712,7 +822,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); if (err) { dev_err(nfc->dev, "Error requesting IRQ!\n"); - goto error; + goto err_disable_clk; } vf610_nfc_preinit_controller(nfc); @@ -720,7 +830,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) /* first scan to find the device and get the page size */ err = nand_scan_ident(mtd, 1, NULL); if (err) - goto error; + goto err_disable_clk; vf610_nfc_init_controller(nfc); @@ -732,20 +842,20 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { dev_err(nfc->dev, "Unsupported flash page size\n"); err = -ENXIO; - goto error; + goto err_disable_clk; } if (chip->ecc.mode == NAND_ECC_HW) { if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { dev_err(nfc->dev, "Unsupported flash with hwecc\n"); err = -ENXIO; - goto error; + goto err_disable_clk; } if (chip->ecc.size != mtd->writesize) { dev_err(nfc->dev, "Step size needs to be page size\n"); err = -ENXIO; - goto error; + goto err_disable_clk; } /* Only 64 byte ECC layouts known */ @@ -763,11 +873,15 @@ static int vf610_nfc_probe(struct platform_device *pdev) } else { dev_err(nfc->dev, "Unsupported ECC strength\n"); err = -ENXIO; - goto error; + goto err_disable_clk; } chip->ecc.read_page = vf610_nfc_read_page; chip->ecc.write_page = vf610_nfc_write_page; + chip->ecc.read_page_raw = vf610_nfc_read_page_raw; + chip->ecc.write_page_raw = vf610_nfc_write_page_raw; + chip->ecc.read_oob = vf610_nfc_read_oob; + chip->ecc.write_oob = vf610_nfc_write_oob; chip->ecc.size = PAGE_2K; } @@ -775,16 +889,19 @@ static int vf610_nfc_probe(struct platform_device *pdev) /* second phase scan */ err = nand_scan_tail(mtd); if (err) - goto error; + goto err_disable_clk; platform_set_drvdata(pdev, mtd); /* Register device in MTD */ - return mtd_device_register(mtd, NULL, 0); + err = mtd_device_register(mtd, NULL, 0); + if (err) + goto err_cleanup_nand; + return 0; -error: - of_node_put(nand_get_flash_node(chip)); -err_clk: +err_cleanup_nand: + nand_cleanup(chip); +err_disable_clk: clk_disable_unprepare(nfc->clk); return err; } diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c index 9926b4e3d69d..9926b4e3d69d 100644 --- a/drivers/mtd/nand/xway_nand.c +++ b/drivers/mtd/nand/raw/xway_nand.c |