diff options
Diffstat (limited to 'drivers/mtd/nand')
72 files changed, 20694 insertions, 11232 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index c89592239bc..f1cf503517f 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -22,45 +22,51 @@ menuconfig MTD_NAND if MTD_NAND -config MTD_NAND_VERIFY_WRITE - bool "Verify NAND page writes" - help - This adds an extra check when data is written to the flash. The - NAND 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_SM_COMMON +config MTD_NAND_BCH tristate - default n + select BCH + depends on MTD_NAND_ECC_BCH + default MTD_NAND -config MTD_NAND_MUSEUM_IDS - bool "Enable chip ids for obsolete ancient NAND devices" +config MTD_NAND_ECC_BCH + bool "Support software BCH ECC" default n help - Enable this option only when your board has first generation - NAND chips (page size 256 byte, erase size 4-8KiB). The IDs - of these chips were reused by later, larger chips. + 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_NAND_AUTCPU12 - tristate "SmartMediaCard on autronix autcpu12 board" - depends on ARCH_AUTCPU12 - help - This enables the driver for the autronix autcpu12 board to - access the SmartMediaCard. +config MTD_SM_COMMON + tristate + default n config MTD_NAND_DENALI - depends on PCI + tristate "Support Denali NAND controller" + depends on HAS_DMA + help + Enable support for the Denali NAND controller. This should be + combined with either the PCI or platform drivers to provide device + registration. + +config MTD_NAND_DENALI_PCI tristate "Support Denali NAND controller on Intel Moorestown" + depends on PCI && MTD_NAND_DENALI 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" + depends on HAVE_CLK && MTD_NAND_DENALI + help + Enable the driver for NAND flash on platforms using a Denali NAND + controller as a DT device. + config MTD_NAND_DENALI_SCRATCH_REG_ADDR hex "Denali NAND size scratch register address" default "0xFF108018" - depends on MTD_NAND_DENALI + depends on MTD_NAND_DENALI_PCI help Some platforms place the NAND chip size in a scratch register because (some versions of) the driver aren't able to automatically @@ -68,31 +74,12 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR scratch register here to enable this feature. On Intel Moorestown boards, the scratch register is at 0xFF108018. -config MTD_NAND_EDB7312 - tristate "Support for Cirrus Logic EBD7312 evaluation board" - depends on ARCH_EDB7312 - help - This enables the driver for the Cirrus Logic EBD7312 evaluation - board to access the onboard NAND Flash. - -config MTD_NAND_H1900 - tristate "iPAQ H1900 flash" - depends on ARCH_PXA && MTD_PARTITIONS - help - This enables the driver for the iPAQ h1900 flash. - config MTD_NAND_GPIO tristate "GPIO NAND Flash driver" - depends on GENERIC_GPIO && ARM + depends on GPIOLIB help This enables a GPIO based NAND flash driver. -config MTD_NAND_SPIA - tristate "NAND Flash device on SPIA board" - depends on ARCH_P720T - help - If you had to ask, you don't have one. Say 'N'. - config MTD_NAND_AMS_DELTA tristate "NAND Flash device on Amstrad E3" depends on MACH_AMS_DELTA @@ -101,27 +88,23 @@ config MTD_NAND_AMS_DELTA Support for NAND flash on Amstrad E3 (Delta). config MTD_NAND_OMAP2 - tristate "NAND Flash device on OMAP2 and OMAP3" - depends on ARM && (ARCH_OMAP2 || ARCH_OMAP3) + tristate "NAND Flash device on OMAP2, OMAP3 and OMAP4" + depends on ARCH_OMAP2PLUS help - Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. + Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 + platforms. -config MTD_NAND_OMAP_PREFETCH - bool "GPMC prefetch support for NAND Flash device" +config MTD_NAND_OMAP_BCH depends on MTD_NAND_OMAP2 - default y - help - The NAND device can be accessed for Read/Write using GPMC PREFETCH engine - to improve the performance. - -config MTD_NAND_OMAP_PREFETCH_DMA - depends on MTD_NAND_OMAP_PREFETCH - bool "DMA mode" + tristate "Support hardware based BCH error correction" default n + select BCH help - The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode - or in DMA interrupt mode. - Say y for DMA mode or MPU mode will be used + 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 they should not enable this config symbol. config MTD_NAND_IDS tristate @@ -140,7 +123,7 @@ config MTD_NAND_RICOH config MTD_NAND_AU1550 tristate "Au1550/1200 NAND support" - depends on SOC_AU1200 || SOC_AU1550 + depends on MIPS_ALCHEMY help This enables the driver for the NAND flash controller on the AMD/Alchemy 1550 SOC. @@ -177,25 +160,9 @@ config MTD_NAND_BF5XX_BOOTROM_ECC If unsure, say N. -config MTD_NAND_RTC_FROM4 - tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)" - depends on SH_SOLUTION_ENGINE - select REED_SOLOMON - select REED_SOLOMON_DEC8 - select BITREVERSE - help - This enables the driver for the Renesas Technology AG-AND - flash interface board (FROM_BOARD4) - -config MTD_NAND_PPCHAMELEONEVB - tristate "NAND Flash device on PPChameleonEVB board" - depends on PPCHAMELEONEVB && BROKEN - help - This enables the NAND flash driver on the PPChameleon EVB Board. - config MTD_NAND_S3C2410 tristate "NAND Flash support for Samsung S3C SoCs" - depends on ARCH_S3C2410 || ARCH_S3C64XX + depends on ARCH_S3C24XX || ARCH_S3C64XX help This enables the NAND flash controller on the S3C24xx and S3C64xx SoCs @@ -235,25 +202,9 @@ config MTD_NAND_S3C2410_CLKSTOP when the is NAND chip selected or released, but will save approximately 5mA of power when there is nothing happening. -config MTD_NAND_BCM_UMI - tristate "NAND Flash support for BCM Reference Boards" - depends on ARCH_BCMRING - help - This enables the NAND flash controller on the BCM UMI block. - - No board specfic support is done by this driver, each board - must advertise a platform_device for the driver to attach. - -config MTD_NAND_BCM_UMI_HWCS - bool "BCM UMI NAND Hardware CS" - depends on MTD_NAND_BCM_UMI - help - Enable the use of the BCM UMI block's internal CS using NAND. - This should only be used if you know the external NAND CS can toggle. - config MTD_NAND_DISKONCHIP - tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)" + depends on HAS_IOMEM select REED_SOLOMON select REED_SOLOMON_DEC16 help @@ -321,6 +272,26 @@ config MTD_NAND_DISKONCHIP_BBTWRITE 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 @@ -353,53 +324,35 @@ config MTD_NAND_ATMEL help Enables support for NAND Flash / Smart Media Card interface on Atmel AT91 and AVR32 processors. -choice - prompt "ECC management for NAND Flash / SmartMedia on AT91 / AVR32" - depends on MTD_NAND_ATMEL -config MTD_NAND_ATMEL_ECC_HW - bool "Hardware ECC" - depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 || AVR32 +config MTD_NAND_PXA3xx + tristate "NAND support on PXA3xx and Armada 370/XP" + depends on PXA3xx || ARCH_MMP || PLAT_ORION help - Use hardware ECC instead of software ECC when the chip - supports it. - - The hardware ECC controller is capable of single bit error - correction and 2-bit random detection per page. - - NB : hardware and software ECC schemes are incompatible. - If you switch from one to another, you'll have to erase your - mtd partition. - - If unsure, say Y + This enables the driver for the NAND flash device found on + PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). -config MTD_NAND_ATMEL_ECC_SOFT - bool "Software ECC" +config MTD_NAND_SLC_LPC32XX + tristate "NXP LPC32xx SLC Controller" + depends on ARCH_LPC32XX help - Use software ECC. + 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. - NB : hardware and software ECC schemes are incompatible. - If you switch from one to another, you'll have to erase your - mtd partition. + Please check the actual NAND chip connected and its support + by the SLC NAND controller. -config MTD_NAND_ATMEL_ECC_NONE - bool "No ECC (testing only, DANGEROUS)" - depends on DEBUG_KERNEL +config MTD_NAND_MLC_LPC32XX + tristate "NXP LPC32xx MLC Controller" + depends on ARCH_LPC32XX help - No ECC will be used. - It's not a good idea and it should be reserved for testing - purpose only. + Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND + controller. This is the default for the WORK92105 controller + board. - If unsure, say N - -endchoice - -config MTD_NAND_PXA3xx - tristate "Support for NAND flash devices on PXA3xx" - depends on PXA3xx || ARCH_MMP - help - This enables the driver for the NAND flash device found on - PXA3xx processors + 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" @@ -421,25 +374,37 @@ config MTD_NAND_TMIO config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" - depends on MTD_PARTITIONS 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. The GPMI may conflicts with other + block, such as SD card. So pay attention to it when you enable + the GPMI. + +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_ALAUDA - tristate "MTD driver for Olympus MAUSB-10 and Fujifilm DPC-R1" - depends on USB - help - These two (and possibly other) Alauda-based cardreaders for - SmartMedia and xD allow raw flash access. - config MTD_NAND_ORION tristate "NAND Flash support for Marvell Orion SoC" depends on PLAT_ORION @@ -459,6 +424,17 @@ config MTD_NAND_FSL_ELBC 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 MTD_NAND && FSL_SOC + 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 @@ -476,30 +452,26 @@ config MTD_NAND_MPC5121_NFC config MTD_NAND_MXC tristate "MXC NAND support" - depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX51 + depends on ARCH_MXC help This enables the driver for the NAND flash controller on the MXC processors. -config MTD_NAND_NOMADIK - tristate "ST Nomadik 8815 NAND support" - depends on ARCH_NOMADIK - help - Driver for the NAND flash controller on the Nomadik, with ECC. - config MTD_NAND_SH_FLCTL tristate "Support for NAND on Renesas SuperH FLCTL" - depends on SUPERH || ARCH_SHMOBILE + depends on SUPERH || ARCH_SHMOBILE || 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 SoC" - depends on ARCH_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 processors. + DaVinci/Keystone processors. config MTD_NAND_TXX9NDFMC tristate "NAND Flash support for TXx9 SoC" @@ -515,7 +487,7 @@ config MTD_NAND_SOCRATES config MTD_NAND_NUC900 tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." - depends on ARCH_W90X900 && MTD_PARTITIONS + depends on ARCH_W90X900 help This enables the driver for the NAND Flash on evaluation board based on w90p910 / NUC9xx. @@ -528,9 +500,17 @@ config MTD_NAND_JZ4740 config MTD_NAND_FSMC tristate "Support for NAND on ST Micros FSMC" - depends on PLAT_SPEAR || PLAT_NOMADIK || MACH_U300 + 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 + tristate "Support for NAND on Lantiq XWAY SoC" + depends on LANTIQ && SOC_TYPE_XWAY + select MTD_NAND_PLATFORM + help + Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached + to the External Bus Unit (EBU). + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 8ad6faec72c..542b5689eb6 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -4,24 +4,22 @@ 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_NAND_IDS) += nand_ids.o obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o -obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o -obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o obj-$(CONFIG_MTD_NAND_DENALI) += denali.o -obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.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_PPCHAMELEONEVB) += ppchameleonevb.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.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_H1900) += h1910.o -obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o @@ -33,20 +31,23 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o -obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_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_NOMADIK) += nomadik_nand.o -obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o +obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c deleted file mode 100644 index 8691e0482ed..00000000000 --- a/drivers/mtd/nand/alauda.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - * MTD driver for Alauda chips - * - * Copyright (C) 2007 Joern Engel <joern@logfs.org> - * - * Based on drivers/usb/usb-skeleton.c which is: - * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) - * and on drivers/usb/storage/alauda.c, which is: - * (c) 2005 Daniel Drake <dsd@gentoo.org> - * - * Idea and initial work by Arnd Bergmann <arnd@arndb.de> - */ -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/kref.h> -#include <linux/usb.h> -#include <linux/mutex.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand_ecc.h> - -/* Control commands */ -#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 -#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a -#define ALAUDA_GET_XD_MEDIA_SIG 0x86 - -/* Common prefix */ -#define ALAUDA_BULK_CMD 0x40 - -/* The two ports */ -#define ALAUDA_PORT_XD 0x00 -#define ALAUDA_PORT_SM 0x01 - -/* Bulk commands */ -#define ALAUDA_BULK_READ_PAGE 0x84 -#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */ -#define ALAUDA_BULK_READ_BLOCK 0x94 -#define ALAUDA_BULK_ERASE_BLOCK 0xa3 -#define ALAUDA_BULK_WRITE_PAGE 0xa4 -#define ALAUDA_BULK_WRITE_BLOCK 0xb4 -#define ALAUDA_BULK_RESET_MEDIA 0xe0 - -/* Address shifting */ -#define PBA_LO(pba) ((pba & 0xF) << 5) -#define PBA_HI(pba) (pba >> 3) -#define PBA_ZONE(pba) (pba >> 11) - -#define TIMEOUT HZ - -static const struct usb_device_id alauda_table[] = { - { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ - { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ - { } -}; -MODULE_DEVICE_TABLE(usb, alauda_table); - -struct alauda_card { - u8 id; /* id byte */ - u8 chipshift; /* 1<<chipshift total size */ - u8 pageshift; /* 1<<pageshift page size */ - u8 blockshift; /* 1<<blockshift block size */ -}; - -struct alauda { - struct usb_device *dev; - struct usb_interface *interface; - struct mtd_info *mtd; - struct alauda_card *card; - struct mutex card_mutex; - u32 pagemask; - u32 bytemask; - u32 blockmask; - unsigned int write_out; - unsigned int bulk_in; - unsigned int bulk_out; - u8 port; - struct kref kref; -}; - -static struct alauda_card alauda_card_ids[] = { - /* NAND flash */ - { 0x6e, 20, 8, 12}, /* 1 MB */ - { 0xe8, 20, 8, 12}, /* 1 MB */ - { 0xec, 20, 8, 12}, /* 1 MB */ - { 0x64, 21, 8, 12}, /* 2 MB */ - { 0xea, 21, 8, 12}, /* 2 MB */ - { 0x6b, 22, 9, 13}, /* 4 MB */ - { 0xe3, 22, 9, 13}, /* 4 MB */ - { 0xe5, 22, 9, 13}, /* 4 MB */ - { 0xe6, 23, 9, 13}, /* 8 MB */ - { 0x73, 24, 9, 14}, /* 16 MB */ - { 0x75, 25, 9, 14}, /* 32 MB */ - { 0x76, 26, 9, 14}, /* 64 MB */ - { 0x79, 27, 9, 14}, /* 128 MB */ - { 0x71, 28, 9, 14}, /* 256 MB */ - - /* MASK ROM */ - { 0x5d, 21, 9, 13}, /* 2 MB */ - { 0xd5, 22, 9, 13}, /* 4 MB */ - { 0xd6, 23, 9, 13}, /* 8 MB */ - { 0x57, 24, 9, 13}, /* 16 MB */ - { 0x58, 25, 9, 13}, /* 32 MB */ - { } -}; - -static struct alauda_card *get_card(u8 id) -{ - struct alauda_card *card; - - for (card = alauda_card_ids; card->id; card++) - if (card->id == id) - return card; - return NULL; -} - -static void alauda_delete(struct kref *kref) -{ - struct alauda *al = container_of(kref, struct alauda, kref); - - if (al->mtd) { - del_mtd_device(al->mtd); - kfree(al->mtd); - } - usb_put_dev(al->dev); - kfree(al); -} - -static int alauda_get_media_status(struct alauda *al, void *buf) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), - ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static int alauda_ack_media(struct alauda *al) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0), - ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static int alauda_get_media_signatures(struct alauda *al, void *buf) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), - ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static void alauda_reset(struct alauda *al) -{ - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0, - 0, 0, 0, 0, al->port - }; - mutex_lock(&al->card_mutex); - usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ); - mutex_unlock(&al->card_mutex); -} - -static void correct_data(void *buf, void *read_ecc, - int *corrected, int *uncorrected) -{ - u8 calc_ecc[3]; - int err; - - nand_calculate_ecc(NULL, buf, calc_ecc); - err = nand_correct_data(NULL, buf, read_ecc, calc_ecc); - if (err) { - if (err > 0) - (*corrected)++; - else - (*uncorrected)++; - } -} - -struct alauda_sg_request { - struct urb *urb[3]; - struct completion comp; -}; - -static void alauda_complete(struct urb *urb) -{ - struct completion *comp = urb->context; - - if (comp) - complete(comp); -} - -static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, - void *oob) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = from >> al->card->blockshift; - u32 page = (from >> al->card->pageshift) & al->pagemask; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port - }; - int i, err; - - for (i=0; i<3; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<3; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<3; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<3; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - usb_free_urb(sg.urb[2]); - return err; -} - -static int alauda_read_page(struct mtd_info *mtd, loff_t from, - void *buf, u8 *oob, int *corrected, int *uncorrected) -{ - int err; - - err = __alauda_read_page(mtd, from, buf, oob); - if (err) - return err; - correct_data(buf, oob+13, corrected, uncorrected); - correct_data(buf+256, oob+8, corrected, uncorrected); - return 0; -} - -static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf, - void *oob) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = to >> al->card->blockshift; - u32 page = (to >> al->card->pageshift) & al->pagemask; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port - }; - int i, err; - - for (i=0; i<3; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<3; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<3; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<3; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - usb_free_urb(sg.urb[2]); - return err; -} - -static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = ofs >> al->card->blockshift; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port - }; - u8 buf[2]; - int i, err; - - for (i=0; i<2; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<2; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<2; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<2; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - return err; -} - -static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) -{ - static u8 ignore_buf[512]; /* write only */ - - return __alauda_read_page(mtd, from, ignore_buf, oob); -} - -static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) -{ - u8 oob[16]; - int err; - - err = alauda_read_oob(mtd, ofs, oob); - if (err) - return err; - - /* A block is marked bad if two or more bits are zero */ - return hweight8(oob[5]) >= 7 ? 0 : 1; -} - -static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct alauda *al = mtd->priv; - void *bounce_buf; - int err, corrected=0, uncorrected=0; - - bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); - if (!bounce_buf) - return -ENOMEM; - - *retlen = len; - while (len) { - u8 oob[16]; - size_t byte = from & al->bytemask; - size_t cplen = min(len, mtd->writesize - byte); - - err = alauda_read_page(mtd, from, bounce_buf, oob, - &corrected, &uncorrected); - if (err) - goto out; - - memcpy(buf, bounce_buf + byte, cplen); - buf += cplen; - from += cplen; - len -= cplen; - } - err = 0; - if (corrected) - err = -EUCLEAN; - if (uncorrected) - err = -EBADMSG; -out: - kfree(bounce_buf); - return err; -} - -static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct alauda *al = mtd->priv; - int err, corrected=0, uncorrected=0; - - if ((from & al->bytemask) || (len & al->bytemask)) - return alauda_bounce_read(mtd, from, len, retlen, buf); - - *retlen = len; - while (len) { - u8 oob[16]; - - err = alauda_read_page(mtd, from, buf, oob, - &corrected, &uncorrected); - if (err) - return err; - - buf += mtd->writesize; - from += mtd->writesize; - len -= mtd->writesize; - } - err = 0; - if (corrected) - err = -EUCLEAN; - if (uncorrected) - err = -EBADMSG; - return err; -} - -static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct alauda *al = mtd->priv; - int err; - - if ((to & al->bytemask) || (len & al->bytemask)) - return -EINVAL; - - *retlen = len; - while (len) { - u32 page = (to >> al->card->pageshift) & al->pagemask; - u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - - /* don't write to bad blocks */ - if (page == 0) { - err = alauda_isbad(mtd, to); - if (err) { - return -EIO; - } - } - nand_calculate_ecc(mtd, buf, &oob[13]); - nand_calculate_ecc(mtd, buf+256, &oob[8]); - - err = alauda_write_page(mtd, to, (void*)buf, oob); - if (err) - return err; - - buf += mtd->writesize; - to += mtd->writesize; - len -= mtd->writesize; - } - return 0; -} - -static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct alauda *al = mtd->priv; - u32 ofs = instr->addr; - u32 len = instr->len; - int err; - - if ((ofs & al->blockmask) || (len & al->blockmask)) - return -EINVAL; - - while (len) { - /* don't erase bad blocks */ - err = alauda_isbad(mtd, ofs); - if (err > 0) - err = -EIO; - if (err < 0) - return err; - - err = alauda_erase_block(mtd, ofs); - if (err < 0) - return err; - - ofs += mtd->erasesize; - len -= mtd->erasesize; - } - return 0; -} - -static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - int err; - - err = __alauda_erase(mtd, instr); - instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE; - mtd_erase_callback(instr); - return err; -} - -static int alauda_init_media(struct alauda *al) -{ - u8 buf[4], *b0=buf, *b1=buf+1; - struct alauda_card *card; - struct mtd_info *mtd; - int err; - - mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) - return -ENOMEM; - - for (;;) { - err = alauda_get_media_status(al, buf); - if (err < 0) - goto error; - if (*b0 & 0x10) - break; - msleep(20); - } - - err = alauda_ack_media(al); - if (err) - goto error; - - msleep(10); - - err = alauda_get_media_status(al, buf); - if (err < 0) - goto error; - - if (*b0 != 0x14) { - /* media not ready */ - err = -EIO; - goto error; - } - err = alauda_get_media_signatures(al, buf); - if (err < 0) - goto error; - - card = get_card(*b1); - if (!card) { - printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1); - err = -EIO; - goto error; - } - printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n", - 1<<card->pageshift, 1<<card->blockshift, - 1<<(card->chipshift-20)); - al->card = card; - al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1; - al->bytemask = (1 << card->pageshift) - 1; - al->blockmask = (1 << card->blockshift) - 1; - - mtd->name = "alauda"; - mtd->size = 1<<card->chipshift; - mtd->erasesize = 1<<card->blockshift; - mtd->writesize = 1<<card->pageshift; - mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH; - mtd->read = alauda_read; - mtd->write = alauda_write; - mtd->erase = alauda_erase; - mtd->block_isbad = alauda_isbad; - mtd->priv = al; - mtd->owner = THIS_MODULE; - - err = add_mtd_device(mtd); - if (err) { - err = -ENFILE; - goto error; - } - - al->mtd = mtd; - alauda_reset(al); /* no clue whether this is necessary */ - return 0; -error: - kfree(mtd); - return err; -} - -static int alauda_check_media(struct alauda *al) -{ - u8 buf[2], *b0 = buf, *b1 = buf+1; - int err; - - err = alauda_get_media_status(al, buf); - if (err < 0) - return err; - - if ((*b1 & 0x01) == 0) { - /* door open */ - return -EIO; - } - if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) { - /* no media ? */ - return -EIO; - } - if (*b0 & 0x08) { - /* media change ? */ - return alauda_init_media(al); - } - return 0; -} - -static int alauda_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct alauda *al; - struct usb_host_interface *iface; - struct usb_endpoint_descriptor *ep, - *ep_in=NULL, *ep_out=NULL, *ep_wr=NULL; - int i, err = -ENOMEM; - - al = kzalloc(2*sizeof(*al), GFP_KERNEL); - if (!al) - goto error; - - kref_init(&al->kref); - usb_set_intfdata(interface, al); - - al->dev = usb_get_dev(interface_to_usbdev(interface)); - al->interface = interface; - - iface = interface->cur_altsetting; - for (i = 0; i < iface->desc.bNumEndpoints; ++i) { - ep = &iface->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep)) { - ep_in = ep; - } else if (usb_endpoint_is_bulk_out(ep)) { - if (i==0) - ep_wr = ep; - else - ep_out = ep; - } - } - err = -EIO; - if (!ep_wr || !ep_in || !ep_out) - goto error; - - al->write_out = usb_sndbulkpipe(al->dev, - usb_endpoint_num(ep_wr)); - al->bulk_in = usb_rcvbulkpipe(al->dev, - usb_endpoint_num(ep_in)); - al->bulk_out = usb_sndbulkpipe(al->dev, - usb_endpoint_num(ep_out)); - - /* second device is identical up to now */ - memcpy(al+1, al, sizeof(*al)); - - mutex_init(&al[0].card_mutex); - mutex_init(&al[1].card_mutex); - - al[0].port = ALAUDA_PORT_XD; - al[1].port = ALAUDA_PORT_SM; - - dev_info(&interface->dev, "alauda probed\n"); - alauda_check_media(al); - alauda_check_media(al+1); - - return 0; - -error: - if (al) - kref_put(&al->kref, alauda_delete); - return err; -} - -static void alauda_disconnect(struct usb_interface *interface) -{ - struct alauda *al; - - al = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - /* FIXME: prevent more I/O from starting */ - - /* decrement our usage count */ - if (al) - kref_put(&al->kref, alauda_delete); - - dev_info(&interface->dev, "alauda gone"); -} - -static struct usb_driver alauda_driver = { - .name = "alauda", - .probe = alauda_probe, - .disconnect = alauda_disconnect, - .id_table = alauda_table, -}; - -static int __init alauda_init(void) -{ - return usb_register(&alauda_driver); -} - -static void __exit alauda_exit(void) -{ - usb_deregister(&alauda_driver); -} - -module_init(alauda_init); -module_exit(alauda_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index a067d090cb3..4936e9e0002 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -17,25 +17,26 @@ */ #include <linux/slab.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/gpio.h> +#include <linux/platform_data/gpio-omap.h> + #include <asm/io.h> -#include <mach/hardware.h> #include <asm/sizes.h> -#include <mach/gpio.h> -#include <plat/board-ams-delta.h> + +#include <mach/board-ams-delta.h> + +#include <mach/hardware.h> /* * MTD structure for E3 (Delta) */ static struct mtd_info *ams_delta_mtd = NULL; -#define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP) - /* * Define partitions for flash devices */ @@ -68,10 +69,9 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) writew(0, io_base + OMAP_MPUIO_IO_CNTL); writew(byte, this->IO_ADDR_W); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0); ndelay(40); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, - AMS_DELTA_LATCH2_NAND_NWE); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1); } static u_char ams_delta_read_byte(struct mtd_info *mtd) @@ -80,12 +80,11 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; void __iomem *io_base = this->priv; - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0); ndelay(40); writew(~0, io_base + OMAP_MPUIO_IO_CNTL); res = readw(this->IO_ADDR_R); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, - AMS_DELTA_LATCH2_NAND_NRE); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1); return res; } @@ -107,18 +106,6 @@ static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len) buf[i] = ams_delta_read_byte(mtd); } -static int ams_delta_verify_buf(struct mtd_info *mtd, const u_char *buf, - int len) -{ - int i; - - for (i=0; i<len; i++) - if (buf[i] != ams_delta_read_byte(mtd)) - return -EFAULT; - - return 0; -} - /* * Command control function * @@ -132,15 +119,12 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, { if (ctrl & NAND_CTRL_CHANGE) { - unsigned long bits; - - bits = (~ctrl & NAND_NCE) ? AMS_DELTA_LATCH2_NAND_NCE : 0; - bits |= (ctrl & NAND_CLE) ? AMS_DELTA_LATCH2_NAND_CLE : 0; - bits |= (ctrl & NAND_ALE) ? AMS_DELTA_LATCH2_NAND_ALE : 0; - - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE | - AMS_DELTA_LATCH2_NAND_ALE | - AMS_DELTA_LATCH2_NAND_NCE, bits); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE, + (ctrl & NAND_NCE) == 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE, + (ctrl & NAND_CLE) != 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE, + (ctrl & NAND_ALE) != 0); } if (cmd != NAND_CMD_NONE) @@ -152,10 +136,43 @@ static int ams_delta_nand_ready(struct mtd_info *mtd) return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB); } +static const struct gpio _mandatory_gpio[] = { + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nce", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nre", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwp", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwe", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_ale", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_cle", + }, +}; + /* * Main initialization routine */ -static int __devinit ams_delta_init(struct platform_device *pdev) +static int ams_delta_init(struct platform_device *pdev) { struct nand_chip *this; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -186,18 +203,17 @@ static int __devinit ams_delta_init(struct platform_device *pdev) /* Link the private data with the MTD structure */ ams_delta_mtd->priv = this; - if (!request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev))) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - err = -EBUSY; - goto out_free; - } + /* + * Don't try to request the memory region from here, + * it should have been already requested from the + * gpio-omap driver and requesting it again would fail. + */ io_base = ioremap(res->start, resource_size(res)); if (io_base == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); err = -EIO; - goto out_release_io; + goto out_free; } this->priv = io_base; @@ -208,7 +224,6 @@ static int __devinit ams_delta_init(struct platform_device *pdev) this->read_byte = ams_delta_read_byte; this->write_buf = ams_delta_write_buf; this->read_buf = ams_delta_read_buf; - this->verify_buf = ams_delta_verify_buf; this->cmd_ctrl = ams_delta_hwcontrol; if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) { this->dev_ready = ams_delta_nand_ready; @@ -223,28 +238,27 @@ static int __devinit ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, io_base); /* Set chip enabled, but */ - ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE | - AMS_DELTA_LATCH2_NAND_NWE | - AMS_DELTA_LATCH2_NAND_NCE | - AMS_DELTA_LATCH2_NAND_NWP); + err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + if (err) + goto out_gpio; - /* Scan to find existance of the device */ + /* Scan to find existence of the device */ if (nand_scan(ams_delta_mtd, 1)) { err = -ENXIO; goto out_mtd; } /* Register the partitions */ - add_mtd_partitions(ams_delta_mtd, partition_info, - ARRAY_SIZE(partition_info)); + mtd_device_register(ams_delta_mtd, partition_info, + ARRAY_SIZE(partition_info)); goto out; out_mtd: - platform_set_drvdata(pdev, NULL); + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); +out_gpio: + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); -out_release_io: - release_mem_region(res->start, resource_size(res)); out_free: kfree(ams_delta_mtd); out: @@ -254,16 +268,16 @@ out_free: /* * Clean up routine */ -static int __devexit ams_delta_cleanup(struct platform_device *pdev) +static int ams_delta_cleanup(struct platform_device *pdev) { void __iomem *io_base = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* Release resources, unregister device */ nand_release(ams_delta_mtd); + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); - release_mem_region(res->start, resource_size(res)); /* Free the MTD device structure */ kfree(ams_delta_mtd); @@ -273,24 +287,14 @@ static int __devexit ams_delta_cleanup(struct platform_device *pdev) static struct platform_driver ams_delta_nand_driver = { .probe = ams_delta_init, - .remove = __devexit_p(ams_delta_cleanup), + .remove = ams_delta_cleanup, .driver = { .name = "ams-delta-nand", .owner = THIS_MODULE, }, }; -static int __init ams_delta_nand_init(void) -{ - return platform_driver_register(&ams_delta_nand_driver); -} -module_init(ams_delta_nand_init); - -static void __exit ams_delta_nand_exit(void) -{ - platform_driver_unregister(&ams_delta_nand_driver); -} -module_exit(ams_delta_nand_exit); +module_platform_driver(ams_delta_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>"); diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index ccce0f03b5d..4ce181a35bc 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1,20 +1,25 @@ /* - * Copyright (C) 2003 Rick Bronson + * Copyright © 2003 Rick Bronson * * Derived from drivers/mtd/nand/autcpu12.c - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de) * * Derived from drivers/mtd/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * Copyright © 2000 Steven J. Hill (sjhill@cotw.com) * * * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 - * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007 * * Derived from Das U-Boot source code * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) - * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas * + * Add Programmable Multibit ECC support for various AT91 SoC + * © Copyright 2012 ATMEL, Hong Xu + * + * Add Nand Flash Controller support for SAMA5 SoC + * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,31 +27,28 @@ * */ +#include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_mtd.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> #include <linux/gpio.h> +#include <linux/interrupt.h> #include <linux/io.h> +#include <linux/platform_data/atmel.h> -#include <mach/board.h> -#include <mach/cpu.h> - -#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW -#define hard_ecc 1 -#else -#define hard_ecc 0 -#endif - -#ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE -#define no_ecc 1 -#else -#define no_ecc 0 -#endif +static int use_dma = 1; +module_param(use_dma, int, 0); static int on_flash_bbt = 0; module_param(on_flash_bbt, int, 0); @@ -58,6 +60,7 @@ module_param(on_flash_bbt, int, 0); __raw_writel((value), add + ATMEL_ECC_##reg) #include "atmel_nand_ecc.h" /* Hardware ECC registers */ +#include "atmel_nand_nfc.h" /* Nand Flash Controller definition */ /* oob layout for large page size * bad block info is on bytes 0 and 1 @@ -85,22 +88,75 @@ static struct nand_ecclayout atmel_oobinfo_small = { }, }; +struct atmel_nfc { + void __iomem *base_cmd_regs; + void __iomem *hsmc_regs; + void __iomem *sram_bank0; + dma_addr_t sram_bank0_phys; + bool use_nfc_sram; + bool write_by_sram; + + bool is_initialized; + struct completion comp_nfc; + + /* Point to the sram bank which include readed data via NFC */ + void __iomem *data_in_sram; + bool will_write_sram; +}; +static struct atmel_nfc nand_nfc; + struct atmel_nand_host { struct nand_chip nand_chip; struct mtd_info mtd; void __iomem *io_base; - struct atmel_nand_data *board; + dma_addr_t io_phys; + struct atmel_nand_data board; struct device *dev; void __iomem *ecc; + + struct completion comp; + struct dma_chan *dma_chan; + + struct atmel_nfc *nfc; + + bool has_pmecc; + u8 pmecc_corr_cap; + u16 pmecc_sector_size; + u32 pmecc_lookup_table_offset; + u32 pmecc_lookup_table_offset_512; + u32 pmecc_lookup_table_offset_1024; + + int pmecc_bytes_per_sector; + int pmecc_sector_number; + int pmecc_degree; /* Degree of remainders */ + int pmecc_cw_len; /* Length of codeword */ + + void __iomem *pmerrloc_base; + void __iomem *pmecc_rom_base; + + /* lookup table for alpha_to and index_of */ + void __iomem *pmecc_alpha_to; + void __iomem *pmecc_index_of; + + /* data for pmecc computation */ + int16_t *pmecc_partial_syn; + int16_t *pmecc_si; + int16_t *pmecc_smu; /* Sigma table */ + int16_t *pmecc_lmu; /* polynomal order */ + int *pmecc_mu; + int *pmecc_dmu; + int *pmecc_delta; }; +static struct nand_ecclayout atmel_pmecc_oobinfo; + /* * Enable NAND. */ static void atmel_nand_enable(struct atmel_nand_host *host) { - if (host->board->enable_pin) - gpio_set_value(host->board->enable_pin, 0); + if (gpio_is_valid(host->board.enable_pin)) + gpio_set_value(host->board.enable_pin, 0); } /* @@ -108,8 +164,8 @@ static void atmel_nand_enable(struct atmel_nand_host *host) */ static void atmel_nand_disable(struct atmel_nand_host *host) { - if (host->board->enable_pin) - gpio_set_value(host->board->enable_pin, 1); + if (gpio_is_valid(host->board.enable_pin)) + gpio_set_value(host->board.enable_pin, 1); } /* @@ -130,9 +186,9 @@ static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl return; if (ctrl & NAND_CLE) - writeb(cmd, host->io_base + (1 << host->board->cle)); + writeb(cmd, host->io_base + (1 << host->board.cle)); else - writeb(cmd, host->io_base + (1 << host->board->ale)); + writeb(cmd, host->io_base + (1 << host->board.ale)); } /* @@ -143,28 +199,110 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) struct nand_chip *nand_chip = mtd->priv; struct atmel_nand_host *host = nand_chip->priv; - return gpio_get_value(host->board->rdy_pin) ^ - !!host->board->rdy_pin_active_low; + return gpio_get_value(host->board.rdy_pin) ^ + !!host->board.rdy_pin_active_low; +} + +/* Set up for hardware ready pin and enable pin. */ +static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + int res = 0; + + if (gpio_is_valid(host->board.rdy_pin)) { + res = devm_gpio_request(host->dev, + host->board.rdy_pin, "nand_rdy"); + if (res < 0) { + dev_err(host->dev, + "can't request rdy gpio %d\n", + host->board.rdy_pin); + return res; + } + + res = gpio_direction_input(host->board.rdy_pin); + if (res < 0) { + dev_err(host->dev, + "can't request input direction rdy gpio %d\n", + host->board.rdy_pin); + return res; + } + + chip->dev_ready = atmel_nand_device_ready; + } + + if (gpio_is_valid(host->board.enable_pin)) { + res = devm_gpio_request(host->dev, + host->board.enable_pin, "nand_enable"); + if (res < 0) { + dev_err(host->dev, + "can't request enable gpio %d\n", + host->board.enable_pin); + return res; + } + + res = gpio_direction_output(host->board.enable_pin, 1); + if (res < 0) { + dev_err(host->dev, + "can't request output direction enable gpio %d\n", + host->board.enable_pin); + return res; + } + } + + return res; +} + +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u32 *t = trg; + const __iomem u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + *t++ = readl_relaxed(s++); +} + +static void memcpy32_toio(void __iomem *trg, const void *src, int size) +{ + int i; + u32 __iomem *t = trg; + const u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + writel_relaxed(*s++, t++); } /* * Minimal-overhead PIO for data access. */ -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; - __raw_readsb(nand_chip->IO_ADDR_R, buf, len); + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) { + memcpy32_fromio(buf, host->nfc->data_in_sram, len); + host->nfc->data_in_sram += len; + } else { + __raw_readsb(nand_chip->IO_ADDR_R, buf, len); + } } static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; - __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) { + memcpy32_fromio(buf, host->nfc->data_in_sram, len); + host->nfc->data_in_sram += len; + } else { + __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); + } } -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; @@ -178,6 +316,922 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len) __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2); } +static void dma_complete_func(void *completion) +{ + complete(completion); +} + +static int nfc_set_sram_bank(struct atmel_nand_host *host, unsigned int bank) +{ + /* NFC only has two banks. Must be 0 or 1 */ + if (bank > 1) + return -EINVAL; + + if (bank) { + /* Only for a 2k-page or lower flash, NFC can handle 2 banks */ + if (host->mtd.writesize > 2048) + return -EINVAL; + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK1); + } else { + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK0); + } + + return 0; +} + +static uint nfc_get_sram_off(struct atmel_nand_host *host) +{ + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1) + return NFC_SRAM_BANK1_OFFSET; + else + return 0; +} + +static dma_addr_t nfc_sram_phys(struct atmel_nand_host *host) +{ + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1) + return host->nfc->sram_bank0_phys + NFC_SRAM_BANK1_OFFSET; + else + return host->nfc->sram_bank0_phys; +} + +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, + int is_read) +{ + struct dma_device *dma_dev; + enum dma_ctrl_flags flags; + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr; + struct dma_async_tx_descriptor *tx = NULL; + dma_cookie_t cookie; + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + void *p = buf; + int err = -EIO; + enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + struct atmel_nfc *nfc = host->nfc; + + if (buf >= high_memory) + goto err_buf; + + dma_dev = host->dma_chan->device; + + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + + phys_addr = dma_map_single(dma_dev->dev, p, len, dir); + if (dma_mapping_error(dma_dev->dev, phys_addr)) { + dev_err(host->dev, "Failed to dma_map_single\n"); + goto err_buf; + } + + if (is_read) { + if (nfc && nfc->data_in_sram) + dma_src_addr = nfc_sram_phys(host) + (nfc->data_in_sram + - (nfc->sram_bank0 + nfc_get_sram_off(host))); + else + dma_src_addr = host->io_phys; + + dma_dst_addr = phys_addr; + } else { + dma_src_addr = phys_addr; + + if (nfc && nfc->write_by_sram) + dma_dst_addr = nfc_sram_phys(host); + else + dma_dst_addr = host->io_phys; + } + + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr, + dma_src_addr, len, flags); + if (!tx) { + dev_err(host->dev, "Failed to prepare DMA memcpy\n"); + goto err_dma; + } + + init_completion(&host->comp); + tx->callback = dma_complete_func; + tx->callback_param = &host->comp; + + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { + dev_err(host->dev, "Failed to do DMA tx_submit\n"); + goto err_dma; + } + + dma_async_issue_pending(host->dma_chan); + wait_for_completion(&host->comp); + + if (is_read && nfc && nfc->data_in_sram) + /* After read data from SRAM, need to increase the position */ + nfc->data_in_sram += len; + + err = 0; + +err_dma: + dma_unmap_single(dma_dev->dev, phys_addr, len, dir); +err_buf: + if (err != 0) + dev_dbg(host->dev, "Fall back to CPU I/O\n"); + return err; +} + +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + + if (use_dma && len > mtd->oobsize) + /* only use DMA for bigger than oob size: better performances */ + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) + return; + + if (host->board.bus_width_16) + atmel_read_buf16(mtd, buf, len); + else + atmel_read_buf8(mtd, buf, len); +} + +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + + if (use_dma && len > mtd->oobsize) + /* only use DMA for bigger than oob size: better performances */ + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) + return; + + if (host->board.bus_width_16) + atmel_write_buf16(mtd, buf, len); + else + atmel_write_buf8(mtd, buf, len); +} + +/* + * Return number of ecc bytes per sector according to sector size and + * correction capability + * + * Following table shows what at91 PMECC supported: + * Correction Capability Sector_512_bytes Sector_1024_bytes + * ===================== ================ ================= + * 2-bits 4-bytes 4-bytes + * 4-bits 7-bytes 7-bytes + * 8-bits 13-bytes 14-bytes + * 12-bits 20-bytes 21-bytes + * 24-bits 39-bytes 42-bytes + */ +static int pmecc_get_ecc_bytes(int cap, int sector_size) +{ + int m = 12 + sector_size / 512; + return (m * cap + 7) / 8; +} + +static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, + int oobsize, int ecc_len) +{ + int i; + + layout->eccbytes = ecc_len; + + /* ECC will occupy the last ecc_len bytes continuously */ + for (i = 0; i < ecc_len; i++) + layout->eccpos[i] = oobsize - ecc_len + i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = + oobsize - ecc_len - layout->oobfree[0].offset; +} + +static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) +{ + int table_size; + + table_size = host->pmecc_sector_size == 512 ? + PMECC_LOOKUP_TABLE_SIZE_512 : PMECC_LOOKUP_TABLE_SIZE_1024; + + return host->pmecc_rom_base + host->pmecc_lookup_table_offset + + table_size * sizeof(int16_t); +} + +static int pmecc_data_alloc(struct atmel_nand_host *host) +{ + const int cap = host->pmecc_corr_cap; + int size; + + size = (2 * cap + 1) * sizeof(int16_t); + host->pmecc_partial_syn = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_si = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_lmu = devm_kzalloc(host->dev, + (cap + 1) * sizeof(int16_t), GFP_KERNEL); + host->pmecc_smu = devm_kzalloc(host->dev, + (cap + 2) * size, GFP_KERNEL); + + size = (cap + 1) * sizeof(int); + host->pmecc_mu = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_dmu = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_delta = devm_kzalloc(host->dev, size, GFP_KERNEL); + + if (!host->pmecc_partial_syn || + !host->pmecc_si || + !host->pmecc_lmu || + !host->pmecc_smu || + !host->pmecc_mu || + !host->pmecc_dmu || + !host->pmecc_delta) + return -ENOMEM; + + return 0; +} + +static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int i; + uint32_t value; + + /* Fill odd syndromes */ + for (i = 0; i < host->pmecc_corr_cap; i++) { + value = pmecc_readl_rem_relaxed(host->ecc, sector, i / 2); + if (i & 1) + value >>= 16; + value &= 0xffff; + host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value; + } +} + +static void pmecc_substitute(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int16_t __iomem *alpha_to = host->pmecc_alpha_to; + int16_t __iomem *index_of = host->pmecc_index_of; + int16_t *partial_syn = host->pmecc_partial_syn; + const int cap = host->pmecc_corr_cap; + int16_t *si; + int i, j; + + /* si[] is a table that holds the current syndrome value, + * an element of that table belongs to the field + */ + si = host->pmecc_si; + + memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1)); + + /* Computation 2t syndromes based on S(x) */ + /* Odd syndromes */ + for (i = 1; i < 2 * cap; i += 2) { + for (j = 0; j < host->pmecc_degree; j++) { + if (partial_syn[i] & ((unsigned short)0x1 << j)) + si[i] = readw_relaxed(alpha_to + i * j) ^ si[i]; + } + } + /* Even syndrome = (Odd syndrome) ** 2 */ + for (i = 2, j = 1; j <= cap; i = ++j << 1) { + if (si[j] == 0) { + si[i] = 0; + } else { + int16_t tmp; + + tmp = readw_relaxed(index_of + si[j]); + tmp = (tmp * 2) % host->pmecc_cw_len; + si[i] = readw_relaxed(alpha_to + tmp); + } + } + + return; +} + +static void pmecc_get_sigma(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + int16_t *lmu = host->pmecc_lmu; + int16_t *si = host->pmecc_si; + int *mu = host->pmecc_mu; + int *dmu = host->pmecc_dmu; /* Discrepancy */ + int *delta = host->pmecc_delta; /* Delta order */ + int cw_len = host->pmecc_cw_len; + const int16_t cap = host->pmecc_corr_cap; + const int num = 2 * cap + 1; + int16_t __iomem *index_of = host->pmecc_index_of; + int16_t __iomem *alpha_to = host->pmecc_alpha_to; + int i, j, k; + uint32_t dmu_0_count, tmp; + int16_t *smu = host->pmecc_smu; + + /* index of largest delta */ + int ro; + int largest; + int diff; + + dmu_0_count = 0; + + /* First Row */ + + /* Mu */ + mu[0] = -1; + + memset(smu, 0, sizeof(int16_t) * num); + smu[0] = 1; + + /* discrepancy set to 1 */ + dmu[0] = 1; + /* polynom order set to 0 */ + lmu[0] = 0; + delta[0] = (mu[0] * 2 - lmu[0]) >> 1; + + /* Second Row */ + + /* Mu */ + mu[1] = 0; + /* Sigma(x) set to 1 */ + memset(&smu[num], 0, sizeof(int16_t) * num); + smu[num] = 1; + + /* discrepancy set to S1 */ + dmu[1] = si[1]; + + /* polynom order set to 0 */ + lmu[1] = 0; + + delta[1] = (mu[1] * 2 - lmu[1]) >> 1; + + /* Init the Sigma(x) last row */ + memset(&smu[(cap + 1) * num], 0, sizeof(int16_t) * num); + + for (i = 1; i <= cap; i++) { + mu[i + 1] = i << 1; + /* Begin Computing Sigma (Mu+1) and L(mu) */ + /* check if discrepancy is set to 0 */ + if (dmu[i] == 0) { + dmu_0_count++; + + tmp = ((cap - (lmu[i] >> 1) - 1) / 2); + if ((cap - (lmu[i] >> 1) - 1) & 0x1) + tmp += 2; + else + tmp += 1; + + if (dmu_0_count == tmp) { + for (j = 0; j <= (lmu[i] >> 1) + 1; j++) + smu[(cap + 1) * num + j] = + smu[i * num + j]; + + lmu[cap + 1] = lmu[i]; + return; + } + + /* copy polynom */ + for (j = 0; j <= lmu[i] >> 1; j++) + smu[(i + 1) * num + j] = smu[i * num + j]; + + /* copy previous polynom order to the next */ + lmu[i + 1] = lmu[i]; + } else { + ro = 0; + largest = -1; + /* find largest delta with dmu != 0 */ + for (j = 0; j < i; j++) { + if ((dmu[j]) && (delta[j] > largest)) { + largest = delta[j]; + ro = j; + } + } + + /* compute difference */ + diff = (mu[i] - mu[ro]); + + /* Compute degree of the new smu polynomial */ + if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) + lmu[i + 1] = lmu[i]; + else + lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; + + /* Init smu[i+1] with 0 */ + for (k = 0; k < num; k++) + smu[(i + 1) * num + k] = 0; + + /* Compute smu[i+1] */ + for (k = 0; k <= lmu[ro] >> 1; k++) { + int16_t a, b, c; + + if (!(smu[ro * num + k] && dmu[i])) + continue; + a = readw_relaxed(index_of + dmu[i]); + b = readw_relaxed(index_of + dmu[ro]); + c = readw_relaxed(index_of + smu[ro * num + k]); + tmp = a + (cw_len - b) + c; + a = readw_relaxed(alpha_to + tmp % cw_len); + smu[(i + 1) * num + (k + diff)] = a; + } + + for (k = 0; k <= lmu[i] >> 1; k++) + smu[(i + 1) * num + k] ^= smu[i * num + k]; + } + + /* End Computing Sigma (Mu+1) and L(mu) */ + /* In either case compute delta */ + delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; + + /* Do not compute discrepancy for the last iteration */ + if (i >= cap) + continue; + + for (k = 0; k <= (lmu[i + 1] >> 1); k++) { + tmp = 2 * (i - 1); + if (k == 0) { + dmu[i + 1] = si[tmp + 3]; + } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { + int16_t a, b, c; + a = readw_relaxed(index_of + + smu[(i + 1) * num + k]); + b = si[2 * (i - 1) + 3 - k]; + c = readw_relaxed(index_of + b); + tmp = a + c; + tmp %= cw_len; + dmu[i + 1] = readw_relaxed(alpha_to + tmp) ^ + dmu[i + 1]; + } + } + } + + return; +} + +static int pmecc_err_location(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + unsigned long end_time; + const int cap = host->pmecc_corr_cap; + const int num = 2 * cap + 1; + int sector_size = host->pmecc_sector_size; + int err_nbr = 0; /* number of error */ + int roots_nbr; /* number of roots */ + int i; + uint32_t val; + int16_t *smu = host->pmecc_smu; + + pmerrloc_writel(host->pmerrloc_base, ELDIS, PMERRLOC_DISABLE); + + for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) { + pmerrloc_writel_sigma_relaxed(host->pmerrloc_base, i, + smu[(cap + 1) * num + i]); + err_nbr++; + } + + val = (err_nbr - 1) << 16; + if (sector_size == 1024) + val |= 1; + + pmerrloc_writel(host->pmerrloc_base, ELCFG, val); + pmerrloc_writel(host->pmerrloc_base, ELEN, + sector_size * 8 + host->pmecc_degree * cap); + + end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS); + while (!(pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR) + & PMERRLOC_CALC_DONE)) { + if (unlikely(time_after(jiffies, end_time))) { + dev_err(host->dev, "PMECC: Timeout to calculate error location.\n"); + return -1; + } + cpu_relax(); + } + + roots_nbr = (pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR) + & PMERRLOC_ERR_NUM_MASK) >> 8; + /* Number of roots == degree of smu hence <= cap */ + if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1) + return err_nbr - 1; + + /* Number of roots does not match the degree of smu + * unable to correct error */ + return -1; +} + +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, + int sector_num, int extra_bytes, int err_nbr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int i = 0; + int byte_pos, bit_pos, sector_size, pos; + uint32_t tmp; + uint8_t err_byte; + + sector_size = host->pmecc_sector_size; + + while (err_nbr) { + tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1; + byte_pos = tmp / 8; + bit_pos = tmp % 8; + + if (byte_pos >= (sector_size + extra_bytes)) + BUG(); /* should never happen */ + + if (byte_pos < sector_size) { + err_byte = *(buf + byte_pos); + *(buf + byte_pos) ^= (1 << bit_pos); + + pos = sector_num * host->pmecc_sector_size + byte_pos; + dev_info(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + pos, bit_pos, err_byte, *(buf + byte_pos)); + } else { + /* Bit flip in OOB area */ + tmp = sector_num * host->pmecc_bytes_per_sector + + (byte_pos - sector_size); + err_byte = ecc[tmp]; + ecc[tmp] ^= (1 << bit_pos); + + pos = tmp + nand_chip->ecc.layout->eccpos[0]; + dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + pos, bit_pos, err_byte, ecc[tmp]); + } + + i++; + err_nbr--; + } + + return; +} + +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, + u8 *ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int i, err_nbr, eccbytes; + uint8_t *buf_pos; + int total_err = 0; + + eccbytes = nand_chip->ecc.bytes; + for (i = 0; i < eccbytes; i++) + if (ecc[i] != 0xff) + goto normal_check; + /* Erased page, return OK */ + return 0; + +normal_check: + for (i = 0; i < host->pmecc_sector_number; i++) { + err_nbr = 0; + if (pmecc_stat & 0x1) { + buf_pos = buf + i * host->pmecc_sector_size; + + pmecc_gen_syndrome(mtd, i); + pmecc_substitute(mtd); + pmecc_get_sigma(mtd); + + err_nbr = pmecc_err_location(mtd); + if (err_nbr == -1) { + dev_err(host->dev, "PMECC: Too many errors\n"); + mtd->ecc_stats.failed++; + return -EIO; + } else { + pmecc_correct_data(mtd, buf_pos, ecc, i, + host->pmecc_bytes_per_sector, err_nbr); + mtd->ecc_stats.corrected += err_nbr; + total_err += err_nbr; + } + } + pmecc_stat >>= 1; + } + + return total_err; +} + +static void pmecc_enable(struct atmel_nand_host *host, int ecc_op) +{ + u32 val; + + if (ecc_op != NAND_ECC_READ && ecc_op != NAND_ECC_WRITE) { + dev_err(host->dev, "atmel_nand: wrong pmecc operation type!"); + return; + } + + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); + val = pmecc_readl_relaxed(host->ecc, CFG); + + if (ecc_op == NAND_ECC_READ) + pmecc_writel(host->ecc, CFG, (val & ~PMECC_CFG_WRITE_OP) + | PMECC_CFG_AUTO_ENABLE); + else + pmecc_writel(host->ecc, CFG, (val | PMECC_CFG_WRITE_OP) + & ~PMECC_CFG_AUTO_ENABLE); + + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA); +} + +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct atmel_nand_host *host = chip->priv; + int eccsize = chip->ecc.size; + uint8_t *oob = chip->oob_poi; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t stat; + unsigned long end_time; + int bitflips = 0; + + if (!host->nfc || !host->nfc->use_nfc_sram) + pmecc_enable(host, NAND_ECC_READ); + + chip->read_buf(mtd, buf, eccsize); + chip->read_buf(mtd, oob, mtd->oobsize); + + end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS); + while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) { + if (unlikely(time_after(jiffies, end_time))) { + dev_err(host->dev, "PMECC: Timeout to get error status.\n"); + return -EIO; + } + cpu_relax(); + } + + stat = pmecc_readl_relaxed(host->ecc, ISR); + if (stat != 0) { + bitflips = pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]); + if (bitflips < 0) + /* uncorrectable errors */ + return 0; + } + + return bitflips; +} + +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) +{ + struct atmel_nand_host *host = chip->priv; + uint32_t *eccpos = chip->ecc.layout->eccpos; + int i, j; + unsigned long end_time; + + if (!host->nfc || !host->nfc->write_by_sram) { + pmecc_enable(host, NAND_ECC_WRITE); + chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + } + + end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS); + while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) { + if (unlikely(time_after(jiffies, end_time))) { + dev_err(host->dev, "PMECC: Timeout to get ECC value.\n"); + return -EIO; + } + cpu_relax(); + } + + for (i = 0; i < host->pmecc_sector_number; i++) { + for (j = 0; j < host->pmecc_bytes_per_sector; j++) { + int pos; + + pos = i * host->pmecc_bytes_per_sector + j; + chip->oob_poi[eccpos[pos]] = + pmecc_readb_ecc_relaxed(host->ecc, i, j); + } + } + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static void atmel_pmecc_core_init(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + uint32_t val = 0; + struct nand_ecclayout *ecc_layout; + + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); + + switch (host->pmecc_corr_cap) { + case 2: + val = PMECC_CFG_BCH_ERR2; + break; + case 4: + val = PMECC_CFG_BCH_ERR4; + break; + case 8: + val = PMECC_CFG_BCH_ERR8; + break; + case 12: + val = PMECC_CFG_BCH_ERR12; + break; + case 24: + val = PMECC_CFG_BCH_ERR24; + break; + } + + if (host->pmecc_sector_size == 512) + val |= PMECC_CFG_SECTOR512; + else if (host->pmecc_sector_size == 1024) + val |= PMECC_CFG_SECTOR1024; + + switch (host->pmecc_sector_number) { + case 1: + val |= PMECC_CFG_PAGE_1SECTOR; + break; + case 2: + val |= PMECC_CFG_PAGE_2SECTORS; + break; + case 4: + val |= PMECC_CFG_PAGE_4SECTORS; + break; + case 8: + val |= PMECC_CFG_PAGE_8SECTORS; + break; + } + + val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE + | PMECC_CFG_AUTO_DISABLE); + pmecc_writel(host->ecc, CFG, val); + + ecc_layout = nand_chip->ecc.layout; + pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1); + pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]); + pmecc_writel(host->ecc, EADDR, + ecc_layout->eccpos[ecc_layout->eccbytes - 1]); + /* See datasheet about PMECC Clock Control Register */ + pmecc_writel(host->ecc, CLK, 2); + pmecc_writel(host->ecc, IDR, 0xff); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); +} + +/* + * Get minimum ecc requirements from NAND. + * If pmecc-cap, pmecc-sector-size in DTS are not specified, this function + * will set them according to minimum ecc requirement. Otherwise, use the + * value in DTS file. + * return 0 if success. otherwise return error code. + */ +static int pmecc_choose_ecc(struct atmel_nand_host *host, + int *cap, int *sector_size) +{ + /* Get minimum ECC requirements */ + if (host->nand_chip.ecc_strength_ds) { + *cap = host->nand_chip.ecc_strength_ds; + *sector_size = host->nand_chip.ecc_step_ds; + dev_info(host->dev, "minimum ECC: %d bits in %d bytes\n", + *cap, *sector_size); + } else { + *cap = 2; + *sector_size = 512; + dev_info(host->dev, "can't detect min. ECC, assume 2 bits in 512 bytes\n"); + } + + /* If device tree doesn't specify, use NAND's minimum ECC parameters */ + if (host->pmecc_corr_cap == 0) { + /* use the most fitable ecc bits (the near bigger one ) */ + if (*cap <= 2) + host->pmecc_corr_cap = 2; + else if (*cap <= 4) + host->pmecc_corr_cap = 4; + else if (*cap <= 8) + host->pmecc_corr_cap = 8; + else if (*cap <= 12) + host->pmecc_corr_cap = 12; + else if (*cap <= 24) + host->pmecc_corr_cap = 24; + else + return -EINVAL; + } + if (host->pmecc_sector_size == 0) { + /* use the most fitable sector size (the near smaller one ) */ + if (*sector_size >= 1024) + host->pmecc_sector_size = 1024; + else if (*sector_size >= 512) + host->pmecc_sector_size = 512; + else + return -EINVAL; + } + return 0; +} + +static int atmel_pmecc_nand_init_params(struct platform_device *pdev, + struct atmel_nand_host *host) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *nand_chip = &host->nand_chip; + struct resource *regs, *regs_pmerr, *regs_rom; + int cap, sector_size, err_no; + + err_no = pmecc_choose_ecc(host, &cap, §or_size); + if (err_no) { + dev_err(host->dev, "The NAND flash's ECC requirement are not support!"); + return err_no; + } + + if (cap > host->pmecc_corr_cap || + sector_size != host->pmecc_sector_size) + dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n"); + + cap = host->pmecc_corr_cap; + sector_size = host->pmecc_sector_size; + host->pmecc_lookup_table_offset = (sector_size == 512) ? + host->pmecc_lookup_table_offset_512 : + host->pmecc_lookup_table_offset_1024; + + dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", + cap, sector_size); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!regs) { + dev_warn(host->dev, + "Can't get I/O resource regs for PMECC controller, rolling back on software ECC\n"); + nand_chip->ecc.mode = NAND_ECC_SOFT; + return 0; + } + + host->ecc = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(host->ecc)) { + dev_err(host->dev, "ioremap failed\n"); + err_no = PTR_ERR(host->ecc); + goto err; + } + + regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2); + host->pmerrloc_base = devm_ioremap_resource(&pdev->dev, regs_pmerr); + if (IS_ERR(host->pmerrloc_base)) { + dev_err(host->dev, + "Can not get I/O resource for PMECC ERRLOC controller!\n"); + err_no = PTR_ERR(host->pmerrloc_base); + goto err; + } + + regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); + host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom); + if (IS_ERR(host->pmecc_rom_base)) { + dev_err(host->dev, "Can not get I/O resource for ROM!\n"); + err_no = PTR_ERR(host->pmecc_rom_base); + goto err; + } + + /* ECC is calculated for the whole page (1 step) */ + nand_chip->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 2048: + host->pmecc_degree = (sector_size == 512) ? + PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14; + host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; + host->pmecc_sector_number = mtd->writesize / sector_size; + host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( + cap, sector_size); + host->pmecc_alpha_to = pmecc_get_alpha_to(host); + host->pmecc_index_of = host->pmecc_rom_base + + host->pmecc_lookup_table_offset; + + nand_chip->ecc.steps = 1; + nand_chip->ecc.strength = cap; + nand_chip->ecc.bytes = host->pmecc_bytes_per_sector * + host->pmecc_sector_number; + if (nand_chip->ecc.bytes > mtd->oobsize - 2) { + dev_err(host->dev, "No room for ECC bytes\n"); + err_no = -EINVAL; + goto err; + } + pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, + mtd->oobsize, + nand_chip->ecc.bytes); + nand_chip->ecc.layout = &atmel_pmecc_oobinfo; + break; + case 512: + case 1024: + case 4096: + /* TODO */ + dev_warn(host->dev, + "Unsupported page size for PMECC, use Software ECC\n"); + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand_chip->ecc.mode = NAND_ECC_SOFT; + return 0; + } + + /* Allocate data for PMECC computation */ + err_no = pmecc_data_alloc(host); + if (err_no) { + dev_err(host->dev, + "Cannot allocate memory for PMECC computation!\n"); + goto err; + } + + nand_chip->options |= NAND_NO_SUBPAGE_WRITE; + nand_chip->ecc.read_page = atmel_nand_pmecc_read_page; + nand_chip->ecc.write_page = atmel_nand_pmecc_write_page; + + atmel_pmecc_core_init(mtd); + + return 0; + +err: + return err_no; +} + /* * Calculate HW ECC * @@ -215,9 +1269,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd, * mtd: mtd info structure * chip: nand chip info structure * buf: buffer to store read data + * oob_required: caller expects OOB data read to chip->oob_poi */ -static int atmel_nand_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -226,6 +1281,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; uint8_t *ecc_pos; int stat; + unsigned int max_bitflips = 0; /* * Errata: ALE is incorrectly wired up to the ECC controller @@ -235,10 +1291,9 @@ static int atmel_nand_read_page(struct mtd_info *mtd, * Workaround: Reset the parity registers before reading the * actual data. */ - if (cpu_is_at32ap7000()) { - struct atmel_nand_host *host = chip->priv; + struct atmel_nand_host *host = chip->priv; + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); - } /* read the page */ chip->read_buf(mtd, p, eccsize); @@ -262,10 +1317,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* check if there's an error */ stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } /* get back to oob start (end of page) */ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); @@ -273,7 +1330,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* read the oob */ chip->read_buf(mtd, oob, mtd->oobsize); - return 0; + return max_bitflips; } /* @@ -357,58 +1414,583 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, */ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) { - if (cpu_is_at32ap7000()) { - struct nand_chip *nand_chip = mtd->priv; - struct atmel_nand_host *host = nand_chip->priv; + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); +} + +static int atmel_of_init_port(struct atmel_nand_host *host, + struct device_node *np) +{ + u32 val; + u32 offset[2]; + int ecc_mode; + struct atmel_nand_data *board = &host->board; + enum of_gpio_flags flags = 0; + + if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid addr-offset %u\n", val); + return -EINVAL; + } + board->ale = val; + } + + if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid cmd-offset %u\n", val); + return -EINVAL; + } + board->cle = val; + } + + ecc_mode = of_get_nand_ecc_mode(np); + + board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode; + + board->on_flash_bbt = of_get_nand_on_flash_bbt(np); + + board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma"); + + if (of_get_nand_bus_width(np) == 16) + board->bus_width_16 = 1; + + board->rdy_pin = of_get_gpio_flags(np, 0, &flags); + board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW); + + board->enable_pin = of_get_gpio(np, 1); + board->det_pin = of_get_gpio(np, 2); + + host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc"); + + /* load the nfc driver if there is */ + of_platform_populate(np, NULL, NULL, host->dev); + + if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc) + return 0; /* Not using PMECC */ + + /* use PMECC, get correction capability, sector size and lookup + * table offset. + * If correction bits and sector size are not specified, then find + * them from NAND ONFI parameters. + */ + if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) { + if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && + (val != 24)) { + dev_err(host->dev, + "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", + val); + return -EINVAL; + } + host->pmecc_corr_cap = (u8)val; } + + if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) { + if ((val != 512) && (val != 1024)) { + dev_err(host->dev, + "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", + val); + return -EINVAL; + } + host->pmecc_sector_size = (u16)val; + } + + if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", + offset, 2) != 0) { + dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); + return -EINVAL; + } + if (!offset[0] && !offset[1]) { + dev_err(host->dev, "Invalid PMECC lookup table offset\n"); + return -EINVAL; + } + host->pmecc_lookup_table_offset_512 = offset[0]; + host->pmecc_lookup_table_offset_1024 = offset[1]; + + return 0; +} + +static int atmel_hw_nand_init_params(struct platform_device *pdev, + struct atmel_nand_host *host) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *nand_chip = &host->nand_chip; + struct resource *regs; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!regs) { + dev_err(host->dev, + "Can't get I/O resource regs, use software ECC\n"); + nand_chip->ecc.mode = NAND_ECC_SOFT; + return 0; + } + + host->ecc = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(host->ecc)) { + dev_err(host->dev, "ioremap failed\n"); + return PTR_ERR(host->ecc); + } + + /* ECC is calculated for the whole page (1 step) */ + nand_chip->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 512: + nand_chip->ecc.layout = &atmel_oobinfo_small; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528); + break; + case 1024: + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056); + break; + case 2048: + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112); + break; + case 4096: + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224); + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand_chip->ecc.mode = NAND_ECC_SOFT; + return 0; + } + + /* set up for HW ECC */ + nand_chip->ecc.calculate = atmel_nand_calculate; + nand_chip->ecc.correct = atmel_nand_correct; + nand_chip->ecc.hwctl = atmel_nand_hwctl; + nand_chip->ecc.read_page = atmel_nand_read_page; + nand_chip->ecc.bytes = 4; + nand_chip->ecc.strength = 1; + + return 0; +} + +/* SMC interrupt service routine */ +static irqreturn_t hsmc_interrupt(int irq, void *dev_id) +{ + struct atmel_nand_host *host = dev_id; + u32 status, mask, pending; + irqreturn_t ret = IRQ_HANDLED; + + status = nfc_readl(host->nfc->hsmc_regs, SR); + mask = nfc_readl(host->nfc->hsmc_regs, IMR); + pending = status & mask; + + if (pending & NFC_SR_XFR_DONE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE); + } else if (pending & NFC_SR_RB_EDGE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE); + } else if (pending & NFC_SR_CMD_DONE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_CMD_DONE); + } else { + ret = IRQ_NONE; + } + + return ret; } -#ifdef CONFIG_MTD_CMDLINE_PARTS -static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif +/* NFC(Nand Flash Controller) related functions */ +static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag) +{ + unsigned long timeout; + init_completion(&host->nfc->comp_nfc); + + /* Enable interrupt that need to wait for */ + nfc_writel(host->nfc->hsmc_regs, IER, flag); + + timeout = wait_for_completion_timeout(&host->nfc->comp_nfc, + msecs_to_jiffies(NFC_TIME_OUT_MS)); + if (timeout) + return 0; + + /* Time out to wait for the interrupt */ + dev_err(host->dev, "Time out to wait for interrupt: 0x%08x\n", flag); + return -ETIMEDOUT; +} + +static int nfc_send_command(struct atmel_nand_host *host, + unsigned int cmd, unsigned int addr, unsigned char cycle0) +{ + unsigned long timeout; + dev_dbg(host->dev, + "nfc_cmd: 0x%08x, addr1234: 0x%08x, cycle0: 0x%02x\n", + cmd, addr, cycle0); + + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS); + while (nfc_cmd_readl(NFCADDR_CMD_NFCBUSY, host->nfc->base_cmd_regs) + & NFCADDR_CMD_NFCBUSY) { + if (time_after(jiffies, timeout)) { + dev_err(host->dev, + "Time out to wait CMD_NFCBUSY ready!\n"); + return -ETIMEDOUT; + } + } + nfc_writel(host->nfc->hsmc_regs, CYCLE0, cycle0); + nfc_cmd_addr1234_writel(cmd, addr, host->nfc->base_cmd_regs); + return nfc_wait_interrupt(host, NFC_SR_CMD_DONE); +} + +static int nfc_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + if (!nfc_wait_interrupt(host, NFC_SR_RB_EDGE)) + return 1; + return 0; +} + +static void nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + if (chip == -1) + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_DISABLE); + else + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE); +} + +static int nfc_make_addr(struct mtd_info *mtd, int command, int column, + int page_addr, unsigned int *addr1234, unsigned int *cycle0) +{ + struct nand_chip *chip = mtd->priv; + + int acycle = 0; + unsigned char addr_bytes[8]; + int index = 0, bit_shift; + + BUG_ON(addr1234 == NULL || cycle0 == NULL); + + *cycle0 = 0; + *addr1234 = 0; + + if (column != -1) { + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + addr_bytes[acycle++] = column & 0xff; + if (mtd->writesize > 512) + addr_bytes[acycle++] = (column >> 8) & 0xff; + } + + if (page_addr != -1) { + addr_bytes[acycle++] = page_addr & 0xff; + addr_bytes[acycle++] = (page_addr >> 8) & 0xff; + if (chip->chipsize > (128 << 20)) + addr_bytes[acycle++] = (page_addr >> 16) & 0xff; + } + + if (acycle > 4) + *cycle0 = addr_bytes[index++]; + + for (bit_shift = 0; index < acycle; bit_shift += 8) + *addr1234 += addr_bytes[index++] << bit_shift; + + /* return acycle in cmd register */ + return acycle << NFCADDR_CMD_ACYCLE_BIT_POS; +} + +static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + unsigned long timeout; + unsigned int nfc_addr_cmd = 0; + + unsigned int cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS; + + /* Set default settings: no cmd2, no addr cycle. read from nand */ + unsigned int cmd2 = 0; + unsigned int vcmd2 = 0; + int acycle = NFCADDR_CMD_ACYCLE_NONE; + int csid = NFCADDR_CMD_CSID_3; + int dataen = NFCADDR_CMD_DATADIS; + int nfcwr = NFCADDR_CMD_NFCRD; + unsigned int addr1234 = 0; + unsigned int cycle0 = 0; + bool do_addr = true; + host->nfc->data_in_sram = NULL; + + dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n", + __func__, command, column, page_addr); + + switch (command) { + case NAND_CMD_RESET: + nfc_addr_cmd = cmd1 | acycle | csid | dataen | nfcwr; + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); + udelay(chip->chip_delay); + + nfc_nand_command(mtd, NAND_CMD_STATUS, -1, -1); + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) { + if (time_after(jiffies, timeout)) { + dev_err(host->dev, + "Time out to wait status ready!\n"); + break; + } + } + return; + case NAND_CMD_STATUS: + do_addr = false; + break; + case NAND_CMD_PARAM: + case NAND_CMD_READID: + do_addr = false; + acycle = NFCADDR_CMD_ACYCLE_1; + if (column != -1) + addr1234 = column; + break; + case NAND_CMD_RNDOUT: + cmd2 = NAND_CMD_RNDOUTSTART << NFCADDR_CMD_CMD2_BIT_POS; + vcmd2 = NFCADDR_CMD_VCMD2; + break; + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; /* only READ0 is valid */ + cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS; + } + if (host->nfc->use_nfc_sram) { + /* Enable Data transfer to sram */ + dataen = NFCADDR_CMD_DATAEN; + + /* Need enable PMECC now, since NFC will transfer + * data in bus after sending nfc read command. + */ + if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) + pmecc_enable(host, NAND_ECC_READ); + } + + cmd2 = NAND_CMD_READSTART << NFCADDR_CMD_CMD2_BIT_POS; + vcmd2 = NFCADDR_CMD_VCMD2; + break; + /* For prgramming command, the cmd need set to write enable */ + case NAND_CMD_PAGEPROG: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + nfcwr = NFCADDR_CMD_NFCWR; + if (host->nfc->will_write_sram && command == NAND_CMD_SEQIN) + dataen = NFCADDR_CMD_DATAEN; + break; + default: + break; + } + + if (do_addr) + acycle = nfc_make_addr(mtd, command, column, page_addr, + &addr1234, &cycle0); + + nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr; + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); + + if (dataen == NFCADDR_CMD_DATAEN) + if (nfc_wait_interrupt(host, NFC_SR_XFR_DONE)) + dev_err(host->dev, "something wrong, No XFR_DONE interrupt comes.\n"); + + /* + * Program and erase have their own busy handlers status, sequential + * in, and deplete1 need no delay. + */ + switch (command) { + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_RNDOUT: + case NAND_CMD_SEQIN: + case NAND_CMD_READID: + return; + + case NAND_CMD_READ0: + if (dataen == NFCADDR_CMD_DATAEN) { + host->nfc->data_in_sram = host->nfc->sram_bank0 + + nfc_get_sram_off(host); + return; + } + /* fall through */ + default: + nfc_wait_interrupt(host, NFC_SR_RB_EDGE); + } +} + +static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw) +{ + int cfg, len; + int status = 0; + struct atmel_nand_host *host = chip->priv; + void __iomem *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host); + + /* Subpage write is not supported */ + if (offset || (data_len < mtd->writesize)) + return -EINVAL; + + cfg = nfc_readl(host->nfc->hsmc_regs, CFG); + len = mtd->writesize; + + if (unlikely(raw)) { + len += mtd->oobsize; + nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE); + } else + nfc_writel(host->nfc->hsmc_regs, CFG, cfg & ~NFC_CFG_WSPARE); + + /* Copy page data to sram that will write to nand via NFC */ + if (use_dma) { + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0) + /* Fall back to use cpu copy */ + memcpy32_toio(sram, buf, len); + } else { + memcpy32_toio(sram, buf, len); + } + + if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) + /* + * When use NFC sram, need set up PMECC before send + * NAND_CMD_SEQIN command. Since when the nand command + * is sent, nfc will do transfer from sram and nand. + */ + pmecc_enable(host, NAND_ECC_WRITE); + + host->nfc->will_write_sram = true; + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + host->nfc->will_write_sram = false; + + if (likely(!raw)) + /* Need to write ecc into oob */ + status = chip->ecc.write_page(mtd, chip, buf, oob_required); + + if (status < 0) + return status; + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +static int nfc_sram_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + int res = 0; + + /* Initialize the NFC CFG register */ + unsigned int cfg_nfc = 0; + + /* set page size and oob layout */ + switch (mtd->writesize) { + case 512: + cfg_nfc = NFC_CFG_PAGESIZE_512; + break; + case 1024: + cfg_nfc = NFC_CFG_PAGESIZE_1024; + break; + case 2048: + cfg_nfc = NFC_CFG_PAGESIZE_2048; + break; + case 4096: + cfg_nfc = NFC_CFG_PAGESIZE_4096; + break; + case 8192: + cfg_nfc = NFC_CFG_PAGESIZE_8192; + break; + default: + dev_err(host->dev, "Unsupported page size for NFC.\n"); + res = -ENXIO; + return res; + } + + /* oob bytes size = (NFCSPARESIZE + 1) * 4 + * Max support spare size is 512 bytes. */ + cfg_nfc |= (((mtd->oobsize / 4) - 1) << NFC_CFG_NFC_SPARESIZE_BIT_POS + & NFC_CFG_NFC_SPARESIZE); + /* default set a max timeout */ + cfg_nfc |= NFC_CFG_RSPARE | + NFC_CFG_NFC_DTOCYC | NFC_CFG_NFC_DTOMUL; + + nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc); + + host->nfc->will_write_sram = false; + nfc_set_sram_bank(host, 0); + + /* Use Write page with NFC SRAM only for PMECC or ECC NONE. */ + if (host->nfc->write_by_sram) { + if ((chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) || + chip->ecc.mode == NAND_ECC_NONE) + chip->write_page = nfc_sram_write_page; + else + host->nfc->write_by_sram = false; + } + + dev_info(host->dev, "Using NFC Sram read %s\n", + host->nfc->write_by_sram ? "and write" : ""); + return 0; +} + +static struct platform_driver atmel_nand_nfc_driver; /* * Probe for the NAND device. */ -static int __init atmel_nand_probe(struct platform_device *pdev) +static int atmel_nand_probe(struct platform_device *pdev) { struct atmel_nand_host *host; struct mtd_info *mtd; struct nand_chip *nand_chip; - struct resource *regs; struct resource *mem; - int res; - -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *partitions = NULL; - int num_partitions = 0; -#endif - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n"); - return -ENXIO; - } + struct mtd_part_parser_data ppdata = {}; + int res, irq; /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL); - if (!host) { - printk(KERN_ERR "atmel_nand: failed to allocate device structure.\n"); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) return -ENOMEM; - } - host->io_base = ioremap(mem->start, mem->end - mem->start + 1); - if (host->io_base == NULL) { - printk(KERN_ERR "atmel_nand: ioremap failed\n"); - res = -EIO; + res = platform_driver_register(&atmel_nand_nfc_driver); + if (res) + dev_err(&pdev->dev, "atmel_nand: can't register NFC driver\n"); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->io_base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(host->io_base)) { + dev_err(&pdev->dev, "atmel_nand: ioremap resource failed\n"); + res = PTR_ERR(host->io_base); goto err_nand_ioremap; } + host->io_phys = (dma_addr_t)mem->start; mtd = &host->mtd; nand_chip = &host->nand_chip; - host->board = pdev->dev.platform_data; host->dev = &pdev->dev; + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + /* Only when CONFIG_OF is enabled of_node can be parsed */ + res = atmel_of_init_port(host, pdev->dev.of_node); + if (res) + goto err_nand_ioremap; + } else { + memcpy(&host->board, dev_get_platdata(&pdev->dev), + sizeof(struct atmel_nand_data)); + } nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; @@ -417,62 +1999,100 @@ static int __init atmel_nand_probe(struct platform_device *pdev) /* Set address of NAND IO lines */ nand_chip->IO_ADDR_R = host->io_base; nand_chip->IO_ADDR_W = host->io_base; - nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; - if (host->board->rdy_pin) - nand_chip->dev_ready = atmel_nand_device_ready; + if (nand_nfc.is_initialized) { + /* NFC driver is probed and initialized */ + host->nfc = &nand_nfc; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!regs && hard_ecc) { - printk(KERN_ERR "atmel_nand: can't get I/O resource " - "regs\nFalling back on software ECC\n"); - } + nand_chip->select_chip = nfc_select_chip; + nand_chip->dev_ready = nfc_device_ready; + nand_chip->cmdfunc = nfc_nand_command; - nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ - if (no_ecc) - nand_chip->ecc.mode = NAND_ECC_NONE; - if (hard_ecc && regs) { - host->ecc = ioremap(regs->start, regs->end - regs->start + 1); - if (host->ecc == NULL) { - printk(KERN_ERR "atmel_nand: ioremap failed\n"); - res = -EIO; - goto err_ecc_ioremap; + /* Initialize the interrupt for NFC */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(host->dev, "Cannot get HSMC irq!\n"); + res = irq; + goto err_nand_ioremap; } - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.calculate = atmel_nand_calculate; - nand_chip->ecc.correct = atmel_nand_correct; - nand_chip->ecc.hwctl = atmel_nand_hwctl; - nand_chip->ecc.read_page = atmel_nand_read_page; - nand_chip->ecc.bytes = 4; + + res = devm_request_irq(&pdev->dev, irq, hsmc_interrupt, + 0, "hsmc", host); + if (res) { + dev_err(&pdev->dev, "Unable to request HSMC irq %d\n", + irq); + goto err_nand_ioremap; + } + } else { + res = atmel_nand_set_enable_ready_pins(mtd); + if (res) + goto err_nand_ioremap; + + nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; } + nand_chip->ecc.mode = host->board.ecc_mode; nand_chip->chip_delay = 20; /* 20us command delay time */ - if (host->board->bus_width_16) { /* 16-bit bus width */ + if (host->board.bus_width_16) /* 16-bit bus width */ nand_chip->options |= NAND_BUSWIDTH_16; - nand_chip->read_buf = atmel_read_buf16; - nand_chip->write_buf = atmel_write_buf16; - } else { - nand_chip->read_buf = atmel_read_buf; - nand_chip->write_buf = atmel_write_buf; - } + + nand_chip->read_buf = atmel_read_buf; + nand_chip->write_buf = atmel_write_buf; platform_set_drvdata(pdev, host); atmel_nand_enable(host); - if (host->board->det_pin) { - if (gpio_get_value(host->board->det_pin)) { - printk(KERN_INFO "No SmartMedia card inserted.\n"); + if (gpio_is_valid(host->board.det_pin)) { + res = devm_gpio_request(&pdev->dev, + host->board.det_pin, "nand_det"); + if (res < 0) { + dev_err(&pdev->dev, + "can't request det gpio %d\n", + host->board.det_pin); + goto err_no_card; + } + + res = gpio_direction_input(host->board.det_pin); + if (res < 0) { + dev_err(&pdev->dev, + "can't request input direction det gpio %d\n", + host->board.det_pin); + goto err_no_card; + } + + if (gpio_get_value(host->board.det_pin)) { + dev_info(&pdev->dev, "No SmartMedia card inserted.\n"); res = -ENXIO; goto err_no_card; } } - if (on_flash_bbt) { - printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); - nand_chip->options |= NAND_USE_FLASH_BBT; + if (host->board.on_flash_bbt || on_flash_bbt) { + dev_info(&pdev->dev, "Use On Flash BBT\n"); + nand_chip->bbt_options |= NAND_BBT_USE_FLASH; } + if (!host->board.has_dma) + use_dma = 0; + + if (use_dma) { + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + host->dma_chan = dma_request_channel(mask, NULL, NULL); + if (!host->dma_chan) { + dev_err(host->dev, "Failed to request DMA channel\n"); + use_dma = 0; + } + } + if (use_dma) + dev_info(host->dev, "Using %s for DMA transfers.\n", + dma_chan_name(host->dma_chan)); + else + dev_info(host->dev, "No DMA support for NAND access.\n"); + /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1, NULL)) { res = -ENXIO; @@ -480,39 +2100,21 @@ static int __init atmel_nand_probe(struct platform_device *pdev) } if (nand_chip->ecc.mode == NAND_ECC_HW) { - /* ECC is calculated for the whole page (1 step) */ - nand_chip->ecc.size = mtd->writesize; - - /* set ECC page size and oob layout */ - switch (mtd->writesize) { - case 512: - nand_chip->ecc.layout = &atmel_oobinfo_small; - ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528); - break; - case 1024: - nand_chip->ecc.layout = &atmel_oobinfo_large; - ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056); - break; - case 2048: - nand_chip->ecc.layout = &atmel_oobinfo_large; - ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112); - break; - case 4096: - nand_chip->ecc.layout = &atmel_oobinfo_large; - ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224); - break; - default: - /* page size not handled by HW ECC */ - /* switching back to soft ECC */ - nand_chip->ecc.mode = NAND_ECC_SOFT; - nand_chip->ecc.calculate = NULL; - nand_chip->ecc.correct = NULL; - nand_chip->ecc.hwctl = NULL; - nand_chip->ecc.read_page = NULL; - nand_chip->ecc.postpad = 0; - nand_chip->ecc.prepad = 0; - nand_chip->ecc.bytes = 0; - break; + if (host->has_pmecc) + res = atmel_pmecc_nand_init_params(pdev, host); + else + res = atmel_hw_nand_init_params(pdev, host); + + if (res != 0) + goto err_hw_ecc; + } + + /* initialize the nfc configuration register */ + if (host->nfc && host->nfc->use_nfc_sram) { + res = nfc_sram_init(mtd); + if (res) { + host->nfc->use_nfc_sram = false; + dev_err(host->dev, "Disable use nfc sram for data transfer.\n"); } } @@ -522,52 +2124,30 @@ static int __init atmel_nand_probe(struct platform_device *pdev) goto err_scan_tail; } -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS mtd->name = "atmel_nand"; - num_partitions = parse_mtd_partitions(mtd, part_probes, - &partitions, 0); -#endif - if (num_partitions <= 0 && host->board->partition_info) - partitions = host->board->partition_info(mtd->size, - &num_partitions); - - if ((!partitions) || (num_partitions == 0)) { - printk(KERN_ERR "atmel_nand: No partitions defined, or unsupported device.\n"); - res = -ENXIO; - goto err_no_partitions; - } - - res = add_mtd_partitions(mtd, partitions, num_partitions); -#else - res = add_mtd_device(mtd); -#endif - + ppdata.of_node = pdev->dev.of_node; + res = mtd_device_parse_register(mtd, NULL, &ppdata, + host->board.parts, host->board.num_parts); if (!res) return res; -#ifdef CONFIG_MTD_PARTITIONS -err_no_partitions: -#endif - nand_release(mtd); err_scan_tail: + if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); +err_hw_ecc: err_scan_ident: err_no_card: atmel_nand_disable(host); - platform_set_drvdata(pdev, NULL); - if (host->ecc) - iounmap(host->ecc); -err_ecc_ioremap: - iounmap(host->io_base); + if (host->dma_chan) + dma_release_channel(host->dma_chan); err_nand_ioremap: - kfree(host); return res; } /* * Remove a NAND device. */ -static int __exit atmel_nand_remove(struct platform_device *pdev) +static int atmel_nand_remove(struct platform_device *pdev) { struct atmel_nand_host *host = platform_get_drvdata(pdev); struct mtd_info *mtd = &host->mtd; @@ -576,36 +2156,90 @@ static int __exit atmel_nand_remove(struct platform_device *pdev) atmel_nand_disable(host); - if (host->ecc) - iounmap(host->ecc); - iounmap(host->io_base); - kfree(host); + if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) { + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); + pmerrloc_writel(host->pmerrloc_base, ELDIS, + PMERRLOC_DISABLE); + } + + if (host->dma_chan) + dma_release_channel(host->dma_chan); + + platform_driver_unregister(&atmel_nand_nfc_driver); return 0; } -static struct platform_driver atmel_nand_driver = { - .remove = __exit_p(atmel_nand_remove), - .driver = { - .name = "atmel_nand", - .owner = THIS_MODULE, - }, +static const struct of_device_id atmel_nand_dt_ids[] = { + { .compatible = "atmel,at91rm9200-nand" }, + { /* sentinel */ } }; -static int __init atmel_nand_init(void) +MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids); + +static int atmel_nand_nfc_probe(struct platform_device *pdev) { - return platform_driver_probe(&atmel_nand_driver, atmel_nand_probe); + struct atmel_nfc *nfc = &nand_nfc; + struct resource *nfc_cmd_regs, *nfc_hsmc_regs, *nfc_sram; + + nfc_cmd_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->base_cmd_regs = devm_ioremap_resource(&pdev->dev, nfc_cmd_regs); + if (IS_ERR(nfc->base_cmd_regs)) + return PTR_ERR(nfc->base_cmd_regs); + + nfc_hsmc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + nfc->hsmc_regs = devm_ioremap_resource(&pdev->dev, nfc_hsmc_regs); + if (IS_ERR(nfc->hsmc_regs)) + return PTR_ERR(nfc->hsmc_regs); + + nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (nfc_sram) { + nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, nfc_sram); + if (IS_ERR(nfc->sram_bank0)) { + dev_warn(&pdev->dev, "Fail to ioremap the NFC sram with error: %ld. So disable NFC sram.\n", + PTR_ERR(nfc->sram_bank0)); + } else { + nfc->use_nfc_sram = true; + nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start; + + if (pdev->dev.of_node) + nfc->write_by_sram = of_property_read_bool( + pdev->dev.of_node, + "atmel,write-by-sram"); + } + } + + nfc->is_initialized = true; + dev_info(&pdev->dev, "NFC is probed.\n"); + return 0; } +static const struct of_device_id atmel_nand_nfc_match[] = { + { .compatible = "atmel,sama5d3-nfc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_nand_nfc_match); -static void __exit atmel_nand_exit(void) -{ - platform_driver_unregister(&atmel_nand_driver); -} +static struct platform_driver atmel_nand_nfc_driver = { + .driver = { + .name = "atmel_nand_nfc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_nand_nfc_match), + }, + .probe = atmel_nand_nfc_probe, +}; +static struct platform_driver atmel_nand_driver = { + .probe = atmel_nand_probe, + .remove = atmel_nand_remove, + .driver = { + .name = "atmel_nand", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_nand_dt_ids), + }, +}; -module_init(atmel_nand_init); -module_exit(atmel_nand_exit); +module_platform_driver(atmel_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rick Bronson"); diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h index 578c776e135..8a1e9a68675 100644 --- a/drivers/mtd/nand/atmel_nand_ecc.h +++ b/drivers/mtd/nand/atmel_nand_ecc.h @@ -3,7 +3,7 @@ * Based on AT91SAM9260 datasheet revision B. * * Copyright (C) 2007 Andrew Victor - * Copyright (C) 2007 Atmel Corporation. + * Copyright (C) 2007 - 2012 Atmel Corporation. * * 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 @@ -36,4 +36,116 @@ #define ATMEL_ECC_NPR 0x10 /* NParity register */ #define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */ +/* PMECC Register Definitions */ +#define ATMEL_PMECC_CFG 0x000 /* Configuration Register */ +#define PMECC_CFG_BCH_ERR2 (0 << 0) +#define PMECC_CFG_BCH_ERR4 (1 << 0) +#define PMECC_CFG_BCH_ERR8 (2 << 0) +#define PMECC_CFG_BCH_ERR12 (3 << 0) +#define PMECC_CFG_BCH_ERR24 (4 << 0) + +#define PMECC_CFG_SECTOR512 (0 << 4) +#define PMECC_CFG_SECTOR1024 (1 << 4) + +#define PMECC_CFG_PAGE_1SECTOR (0 << 8) +#define PMECC_CFG_PAGE_2SECTORS (1 << 8) +#define PMECC_CFG_PAGE_4SECTORS (2 << 8) +#define PMECC_CFG_PAGE_8SECTORS (3 << 8) + +#define PMECC_CFG_READ_OP (0 << 12) +#define PMECC_CFG_WRITE_OP (1 << 12) + +#define PMECC_CFG_SPARE_ENABLE (1 << 16) +#define PMECC_CFG_SPARE_DISABLE (0 << 16) + +#define PMECC_CFG_AUTO_ENABLE (1 << 20) +#define PMECC_CFG_AUTO_DISABLE (0 << 20) + +#define ATMEL_PMECC_SAREA 0x004 /* Spare area size */ +#define ATMEL_PMECC_SADDR 0x008 /* PMECC starting address */ +#define ATMEL_PMECC_EADDR 0x00c /* PMECC ending address */ +#define ATMEL_PMECC_CLK 0x010 /* PMECC clock control */ +#define PMECC_CLK_133MHZ (2 << 0) + +#define ATMEL_PMECC_CTRL 0x014 /* PMECC control register */ +#define PMECC_CTRL_RST (1 << 0) +#define PMECC_CTRL_DATA (1 << 1) +#define PMECC_CTRL_USER (1 << 2) +#define PMECC_CTRL_ENABLE (1 << 4) +#define PMECC_CTRL_DISABLE (1 << 5) + +#define ATMEL_PMECC_SR 0x018 /* PMECC status register */ +#define PMECC_SR_BUSY (1 << 0) +#define PMECC_SR_ENABLE (1 << 4) + +#define ATMEL_PMECC_IER 0x01c /* PMECC interrupt enable */ +#define PMECC_IER_ENABLE (1 << 0) +#define ATMEL_PMECC_IDR 0x020 /* PMECC interrupt disable */ +#define PMECC_IER_DISABLE (1 << 0) +#define ATMEL_PMECC_IMR 0x024 /* PMECC interrupt mask */ +#define PMECC_IER_MASK (1 << 0) +#define ATMEL_PMECC_ISR 0x028 /* PMECC interrupt status */ +#define ATMEL_PMECC_ECCx 0x040 /* PMECC ECC x */ +#define ATMEL_PMECC_REMx 0x240 /* PMECC REM x */ + +/* PMERRLOC Register Definitions */ +#define ATMEL_PMERRLOC_ELCFG 0x000 /* Error location config */ +#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) +#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) +#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) + +#define ATMEL_PMERRLOC_ELPRIM 0x004 /* Error location primitive */ +#define ATMEL_PMERRLOC_ELEN 0x008 /* Error location enable */ +#define ATMEL_PMERRLOC_ELDIS 0x00c /* Error location disable */ +#define PMERRLOC_DISABLE (1 << 0) + +#define ATMEL_PMERRLOC_ELSR 0x010 /* Error location status */ +#define PMERRLOC_ELSR_BUSY (1 << 0) +#define ATMEL_PMERRLOC_ELIER 0x014 /* Error location int enable */ +#define ATMEL_PMERRLOC_ELIDR 0x018 /* Error location int disable */ +#define ATMEL_PMERRLOC_ELIMR 0x01c /* Error location int mask */ +#define ATMEL_PMERRLOC_ELISR 0x020 /* Error location int status */ +#define PMERRLOC_ERR_NUM_MASK (0x1f << 8) +#define PMERRLOC_CALC_DONE (1 << 0) +#define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */ +#define ATMEL_PMERRLOC_ELx 0x08c /* Error location x */ + +/* Register access macros for PMECC */ +#define pmecc_readl_relaxed(addr, reg) \ + readl_relaxed((addr) + ATMEL_PMECC_##reg) + +#define pmecc_writel(addr, reg, value) \ + writel((value), (addr) + ATMEL_PMECC_##reg) + +#define pmecc_readb_ecc_relaxed(addr, sector, n) \ + readb_relaxed((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n)) + +#define pmecc_readl_rem_relaxed(addr, sector, n) \ + readl_relaxed((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4)) + +#define pmerrloc_readl_relaxed(addr, reg) \ + readl_relaxed((addr) + ATMEL_PMERRLOC_##reg) + +#define pmerrloc_writel(addr, reg, value) \ + writel((value), (addr) + ATMEL_PMERRLOC_##reg) + +#define pmerrloc_writel_sigma_relaxed(addr, n, value) \ + writel_relaxed((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4)) + +#define pmerrloc_readl_sigma_relaxed(addr, n) \ + readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4)) + +#define pmerrloc_readl_el_relaxed(addr, n) \ + readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4)) + +/* Galois field dimension */ +#define PMECC_GF_DIMENSION_13 13 +#define PMECC_GF_DIMENSION_14 14 + +#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000 +#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000 + +/* Time out value for reading PMECC status register */ +#define PMECC_MAX_TIMEOUT_MS 100 + #endif diff --git a/drivers/mtd/nand/atmel_nand_nfc.h b/drivers/mtd/nand/atmel_nand_nfc.h new file mode 100644 index 00000000000..4efd117cd3a --- /dev/null +++ b/drivers/mtd/nand/atmel_nand_nfc.h @@ -0,0 +1,98 @@ +/* + * Atmel Nand Flash Controller (NFC) - System peripherals regsters. + * Based on SAMA5D3 datasheet. + * + * © Copyright 2013 Atmel Corporation. + * + * 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. + */ + +#ifndef ATMEL_NAND_NFC_H +#define ATMEL_NAND_NFC_H + +/* + * HSMC NFC registers + */ +#define ATMEL_HSMC_NFC_CFG 0x00 /* NFC Configuration Register */ +#define NFC_CFG_PAGESIZE (7 << 0) +#define NFC_CFG_PAGESIZE_512 (0 << 0) +#define NFC_CFG_PAGESIZE_1024 (1 << 0) +#define NFC_CFG_PAGESIZE_2048 (2 << 0) +#define NFC_CFG_PAGESIZE_4096 (3 << 0) +#define NFC_CFG_PAGESIZE_8192 (4 << 0) +#define NFC_CFG_WSPARE (1 << 8) +#define NFC_CFG_RSPARE (1 << 9) +#define NFC_CFG_NFC_DTOCYC (0xf << 16) +#define NFC_CFG_NFC_DTOMUL (0x7 << 20) +#define NFC_CFG_NFC_SPARESIZE (0x7f << 24) +#define NFC_CFG_NFC_SPARESIZE_BIT_POS 24 + +#define ATMEL_HSMC_NFC_CTRL 0x04 /* NFC Control Register */ +#define NFC_CTRL_ENABLE (1 << 0) +#define NFC_CTRL_DISABLE (1 << 1) + +#define ATMEL_HSMC_NFC_SR 0x08 /* NFC Status Register */ +#define NFC_SR_XFR_DONE (1 << 16) +#define NFC_SR_CMD_DONE (1 << 17) +#define NFC_SR_RB_EDGE (1 << 24) + +#define ATMEL_HSMC_NFC_IER 0x0c +#define ATMEL_HSMC_NFC_IDR 0x10 +#define ATMEL_HSMC_NFC_IMR 0x14 +#define ATMEL_HSMC_NFC_CYCLE0 0x18 /* NFC Address Cycle Zero */ +#define ATMEL_HSMC_NFC_ADDR_CYCLE0 (0xff) + +#define ATMEL_HSMC_NFC_BANK 0x1c /* NFC Bank Register */ +#define ATMEL_HSMC_NFC_BANK0 (0 << 0) +#define ATMEL_HSMC_NFC_BANK1 (1 << 0) + +#define nfc_writel(addr, reg, value) \ + writel((value), (addr) + ATMEL_HSMC_NFC_##reg) + +#define nfc_readl(addr, reg) \ + readl_relaxed((addr) + ATMEL_HSMC_NFC_##reg) + +/* + * NFC Address Command definitions + */ +#define NFCADDR_CMD_CMD1 (0xff << 2) /* Command for Cycle 1 */ +#define NFCADDR_CMD_CMD1_BIT_POS 2 +#define NFCADDR_CMD_CMD2 (0xff << 10) /* Command for Cycle 2 */ +#define NFCADDR_CMD_CMD2_BIT_POS 10 +#define NFCADDR_CMD_VCMD2 (0x1 << 18) /* Valid Cycle 2 Command */ +#define NFCADDR_CMD_ACYCLE (0x7 << 19) /* Number of Address required */ +#define NFCADDR_CMD_ACYCLE_NONE (0x0 << 19) +#define NFCADDR_CMD_ACYCLE_1 (0x1 << 19) +#define NFCADDR_CMD_ACYCLE_2 (0x2 << 19) +#define NFCADDR_CMD_ACYCLE_3 (0x3 << 19) +#define NFCADDR_CMD_ACYCLE_4 (0x4 << 19) +#define NFCADDR_CMD_ACYCLE_5 (0x5 << 19) +#define NFCADDR_CMD_ACYCLE_BIT_POS 19 +#define NFCADDR_CMD_CSID (0x7 << 22) /* Chip Select Identifier */ +#define NFCADDR_CMD_CSID_0 (0x0 << 22) +#define NFCADDR_CMD_CSID_1 (0x1 << 22) +#define NFCADDR_CMD_CSID_2 (0x2 << 22) +#define NFCADDR_CMD_CSID_3 (0x3 << 22) +#define NFCADDR_CMD_CSID_4 (0x4 << 22) +#define NFCADDR_CMD_CSID_5 (0x5 << 22) +#define NFCADDR_CMD_CSID_6 (0x6 << 22) +#define NFCADDR_CMD_CSID_7 (0x7 << 22) +#define NFCADDR_CMD_DATAEN (0x1 << 25) /* Data Transfer Enable */ +#define NFCADDR_CMD_DATADIS (0x0 << 25) /* Data Transfer Disable */ +#define NFCADDR_CMD_NFCRD (0x0 << 26) /* NFC Read Enable */ +#define NFCADDR_CMD_NFCWR (0x1 << 26) /* NFC Write Enable */ +#define NFCADDR_CMD_NFCBUSY (0x1 << 27) /* NFC Busy */ + +#define nfc_cmd_addr1234_writel(cmd, addr1234, nfc_base) \ + writel((addr1234), (cmd) + nfc_base) + +#define nfc_cmd_readl(bitstatus, nfc_base) \ + readl_relaxed((bitstatus) + nfc_base) + +#define NFC_TIME_OUT_MS 100 +#define NFC_SRAM_BANK1_OFFSET 0x1200 + +#endif diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 3ffe05db492..bc5c518828d 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -10,44 +10,32 @@ */ #include <linux/slab.h> -#include <linux/init.h> +#include <linux/gpio.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/platform_device.h> #include <asm/io.h> +#include <asm/mach-au1x00/au1000.h> +#include <asm/mach-au1x00/au1550nd.h> -#include <asm/mach-au1x00/au1xxx.h> -#include <asm/mach-db1x00/bcsr.h> -/* - * MTD structure for NAND controller - */ -static struct mtd_info *au1550_mtd = NULL; -static void __iomem *p_nand; -static int nand_width = 1; /* default x8 */ -static void (*au1550_write_byte)(struct mtd_info *, u_char); +struct au1550nd_ctx { + struct mtd_info info; + struct nand_chip chip; -/* - * Define partitions for flash device - */ -static const struct mtd_partition partition_info[] = { - { - .name = "NAND FS 0", - .offset = 0, - .size = 8 * 1024 * 1024}, - { - .name = "NAND FS 1", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL} + int cs; + void __iomem *base; + void (*write_byte)(struct mtd_info *, u_char); }; /** * au_read_byte - read one byte from the chip * @mtd: MTD device structure * - * read function for 8bit buswith + * read function for 8bit buswidth */ static u_char au_read_byte(struct mtd_info *mtd) { @@ -62,7 +50,7 @@ static u_char au_read_byte(struct mtd_info *mtd) * @mtd: MTD device structure * @byte: pointer to data byte to write * - * write function for 8it buswith + * write function for 8it buswidth */ static void au_write_byte(struct mtd_info *mtd, u_char byte) { @@ -72,11 +60,10 @@ static void au_write_byte(struct mtd_info *mtd, u_char byte) } /** - * au_read_byte16 - read one byte endianess aware from the chip + * au_read_byte16 - read one byte endianness aware from the chip * @mtd: MTD device structure * - * read function for 16bit buswith with - * endianess conversion + * read function for 16bit buswidth with endianness conversion */ static u_char au_read_byte16(struct mtd_info *mtd) { @@ -87,12 +74,11 @@ static u_char au_read_byte16(struct mtd_info *mtd) } /** - * au_write_byte16 - write one byte endianess aware to the chip + * au_write_byte16 - write one byte endianness aware to the chip * @mtd: MTD device structure * @byte: pointer to data byte to write * - * write function for 16bit buswith with - * endianess conversion + * write function for 16bit buswidth with endianness conversion */ static void au_write_byte16(struct mtd_info *mtd, u_char byte) { @@ -105,8 +91,7 @@ static void au_write_byte16(struct mtd_info *mtd, u_char byte) * au_read_word - read one word from the chip * @mtd: MTD device structure * - * read function for 16bit buswith without - * endianess conversion + * read function for 16bit buswidth without endianness conversion */ static u16 au_read_word(struct mtd_info *mtd) { @@ -122,7 +107,7 @@ static u16 au_read_word(struct mtd_info *mtd) * @buf: data buffer * @len: number of bytes to write * - * write function for 8bit buswith + * write function for 8bit buswidth */ static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { @@ -141,7 +126,7 @@ static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) * @buf: buffer to store date * @len: number of bytes to read * - * read function for 8bit buswith + * read function for 8bit buswidth */ static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) { @@ -155,34 +140,12 @@ static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) } /** - * au_verify_buf - Verify chip data against buffer - * @mtd: MTD device structure - * @buf: buffer containing the data to compare - * @len: number of bytes to compare - * - * verify function for 8bit buswith - */ -static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i = 0; i < len; i++) { - if (buf[i] != readb(this->IO_ADDR_R)) - return -EFAULT; - au_sync(); - } - - return 0; -} - -/** * au_write_buf16 - write buffer to chip * @mtd: MTD device structure * @buf: data buffer * @len: number of bytes to write * - * write function for 16bit buswith + * write function for 16bit buswidth */ static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) { @@ -204,7 +167,7 @@ static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) * @buf: buffer to store date * @len: number of bytes to read * - * read function for 16bit buswith + * read function for 16bit buswidth */ static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) { @@ -219,29 +182,6 @@ static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) } } -/** - * au_verify_buf16 - Verify chip data against buffer - * @mtd: MTD device structure - * @buf: buffer containing the data to compare - * @len: number of bytes to compare - * - * verify function for 16bit buswith - */ -static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) { - if (p[i] != readw(this->IO_ADDR_R)) - return -EFAULT; - au_sync(); - } - return 0; -} - /* Select the chip by setting nCE to low */ #define NAND_CTL_SETNCE 1 /* Deselect the chip by setting nCE to high */ @@ -257,24 +197,25 @@ static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) { - register struct nand_chip *this = mtd->priv; + struct au1550nd_ctx *ctx = container_of(mtd, struct au1550nd_ctx, info); + struct nand_chip *this = mtd->priv; switch (cmd) { case NAND_CTL_SETCLE: - this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; + this->IO_ADDR_W = ctx->base + MEM_STNAND_CMD; break; case NAND_CTL_CLRCLE: - this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; + this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; break; case NAND_CTL_SETALE: - this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; + this->IO_ADDR_W = ctx->base + MEM_STNAND_ADDR; break; case NAND_CTL_CLRALE: - this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; + this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; /* FIXME: Nobody knows why this is necessary, * but it works only that way */ udelay(1); @@ -282,7 +223,7 @@ static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) case NAND_CTL_SETNCE: /* assert (force assert) chip enable */ - au_writel((1 << (4 + NAND_CS)), MEM_STNDCTL); + au_writel((1 << (4 + ctx->cs)), MEM_STNDCTL); break; case NAND_CTL_CLRNCE: @@ -329,9 +270,10 @@ static void au1550_select_chip(struct mtd_info *mtd, int chip) */ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) { - register struct nand_chip *this = mtd->priv; + struct au1550nd_ctx *ctx = container_of(mtd, struct au1550nd_ctx, info); + struct nand_chip *this = mtd->priv; int ce_override = 0, i; - ulong flags; + unsigned long flags = 0; /* Begin command latch cycle */ au1550_hwcontrol(mtd, NAND_CTL_SETCLE); @@ -352,9 +294,9 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i column -= 256; readcmd = NAND_CMD_READ1; } - au1550_write_byte(mtd, readcmd); + ctx->write_byte(mtd, readcmd); } - au1550_write_byte(mtd, command); + ctx->write_byte(mtd, command); /* Set ALE and clear CLE to start address cycle */ au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); @@ -365,12 +307,13 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) + if (this->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; - au1550_write_byte(mtd, column); + ctx->write_byte(mtd, column); } if (page_addr != -1) { - au1550_write_byte(mtd, (u8)(page_addr & 0xff)); + ctx->write_byte(mtd, (u8)(page_addr & 0xff)); if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || @@ -388,11 +331,12 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i au1550_hwcontrol(mtd, NAND_CTL_SETNCE); } - au1550_write_byte(mtd, (u8)(page_addr >> 8)); + ctx->write_byte(mtd, (u8)(page_addr >> 8)); /* One more address cycle for devices > 32MiB */ if (this->chipsize > (32 << 20)) - au1550_write_byte(mtd, (u8)((page_addr >> 16) & 0x0f)); + ctx->write_byte(mtd, + ((page_addr >> 16) & 0x0f)); } /* Latch in address */ au1550_hwcontrol(mtd, NAND_CTL_CLRALE); @@ -438,121 +382,77 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i while(!this->dev_ready(mtd)); } - -/* - * Main initialization routine - */ -static int __init au1xxx_nand_init(void) +static int find_nand_cs(unsigned long nand_base) { - struct nand_chip *this; - u16 boot_swapboot = 0; /* default value */ - int retval; - u32 mem_staddr; - u32 nand_phys; - - /* Allocate memory for MTD device structure and private data */ - au1550_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!au1550_mtd) { - printk("Unable to allocate NAND MTD dev structure.\n"); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&au1550_mtd[1]); + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR); + unsigned long addr, staddr, start, mask, end; + int i; - /* Link the private data with the MTD structure */ - au1550_mtd->priv = this; - au1550_mtd->owner = THIS_MODULE; + for (i = 0; i < 4; i++) { + addr = 0x1000 + (i * 0x10); /* CSx */ + staddr = __raw_readl(base + addr + 0x08); /* STADDRx */ + /* figure out the decoded range of this CS */ + start = (staddr << 4) & 0xfffc0000; + mask = (staddr << 18) & 0xfffc0000; + end = (start | (start - 1)) & ~(start ^ mask); + if ((nand_base >= start) && (nand_base < end)) + return i; + } + return -ENODEV; +} - /* MEM_STNDCTL: disable ints, disable nand boot */ - au_writel(0, MEM_STNDCTL); +static int au1550nd_probe(struct platform_device *pdev) +{ + struct au1550nd_platdata *pd; + struct au1550nd_ctx *ctx; + struct nand_chip *this; + struct resource *r; + int ret, cs; -#ifdef CONFIG_MIPS_PB1550 - /* set gpio206 high */ - au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR); + pd = dev_get_platdata(&pdev->dev); + if (!pd) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } - boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr_read(BCSR_STATUS) >> 6) & 0x1); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; - switch (boot_swapboot) { - case 0: - case 2: - case 8: - case 0xC: - case 0xD: - /* x16 NAND Flash */ - nand_width = 0; - break; - case 1: - case 9: - case 3: - case 0xE: - case 0xF: - /* x8 NAND Flash */ - nand_width = 1; - break; - default: - printk("Pb1550 NAND: bad boot:swap\n"); - retval = -EINVAL; - goto outmem; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no NAND memory resource\n"); + ret = -ENODEV; + goto out1; } -#endif - - /* Configure chip-select; normally done by boot code, e.g. YAMON */ -#ifdef NAND_STCFG - if (NAND_CS == 0) { - au_writel(NAND_STCFG, MEM_STCFG0); - au_writel(NAND_STTIME, MEM_STTIME0); - au_writel(NAND_STADDR, MEM_STADDR0); + if (request_mem_region(r->start, resource_size(r), "au1550-nand")) { + dev_err(&pdev->dev, "cannot claim NAND memory area\n"); + ret = -ENOMEM; + goto out1; } - if (NAND_CS == 1) { - au_writel(NAND_STCFG, MEM_STCFG1); - au_writel(NAND_STTIME, MEM_STTIME1); - au_writel(NAND_STADDR, MEM_STADDR1); - } - if (NAND_CS == 2) { - au_writel(NAND_STCFG, MEM_STCFG2); - au_writel(NAND_STTIME, MEM_STTIME2); - au_writel(NAND_STADDR, MEM_STADDR2); - } - if (NAND_CS == 3) { - au_writel(NAND_STCFG, MEM_STCFG3); - au_writel(NAND_STTIME, MEM_STTIME3); - au_writel(NAND_STADDR, MEM_STADDR3); - } -#endif - - /* Locate NAND chip-select in order to determine NAND phys address */ - mem_staddr = 0x00000000; - if (((au_readl(MEM_STCFG0) & 0x7) == 0x5) && (NAND_CS == 0)) - mem_staddr = au_readl(MEM_STADDR0); - else if (((au_readl(MEM_STCFG1) & 0x7) == 0x5) && (NAND_CS == 1)) - mem_staddr = au_readl(MEM_STADDR1); - else if (((au_readl(MEM_STCFG2) & 0x7) == 0x5) && (NAND_CS == 2)) - mem_staddr = au_readl(MEM_STADDR2); - else if (((au_readl(MEM_STCFG3) & 0x7) == 0x5) && (NAND_CS == 3)) - mem_staddr = au_readl(MEM_STADDR3); - - if (mem_staddr == 0x00000000) { - printk("Au1xxx NAND: ERROR WITH NAND CHIP-SELECT\n"); - kfree(au1550_mtd); - return 1; + + ctx->base = ioremap_nocache(r->start, 0x1000); + if (!ctx->base) { + dev_err(&pdev->dev, "cannot remap NAND memory area\n"); + ret = -ENODEV; + goto out2; } - nand_phys = (mem_staddr << 4) & 0xFFFC0000; - p_nand = ioremap(nand_phys, 0x1000); + this = &ctx->chip; + ctx->info.priv = this; + ctx->info.owner = THIS_MODULE; - /* make controller and MTD agree */ - if (NAND_CS == 0) - nand_width = au_readl(MEM_STCFG0) & (1 << 22); - if (NAND_CS == 1) - nand_width = au_readl(MEM_STCFG1) & (1 << 22); - if (NAND_CS == 2) - nand_width = au_readl(MEM_STCFG2) & (1 << 22); - if (NAND_CS == 3) - nand_width = au_readl(MEM_STCFG3) & (1 << 22); + /* figure out which CS# r->start belongs to */ + cs = find_nand_cs(r->start); + if (cs < 0) { + dev_err(&pdev->dev, "cannot detect NAND chipselect\n"); + ret = -ENODEV; + goto out3; + } + ctx->cs = cs; - /* Set address of hardware control function */ this->dev_ready = au1550_device_ready; this->select_chip = au1550_select_chip; this->cmdfunc = au1550_command; @@ -561,55 +461,58 @@ static int __init au1xxx_nand_init(void) this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; - - if (!nand_width) + if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; - this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte; - au1550_write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; + this->read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte; + ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte; this->read_word = au_read_word; - this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf; - this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf; - this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf; - - /* Scan to find existence of the device */ - if (nand_scan(au1550_mtd, 1)) { - retval = -ENXIO; - goto outio; + this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf; + this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf; + + ret = nand_scan(&ctx->info, 1); + if (ret) { + dev_err(&pdev->dev, "NAND scan failed with %d\n", ret); + goto out3; } - /* Register the partitions */ - add_mtd_partitions(au1550_mtd, partition_info, ARRAY_SIZE(partition_info)); + mtd_device_register(&ctx->info, pd->parts, pd->num_parts); - return 0; + platform_set_drvdata(pdev, ctx); - outio: - iounmap(p_nand); + return 0; - outmem: - kfree(au1550_mtd); - return retval; +out3: + iounmap(ctx->base); +out2: + release_mem_region(r->start, resource_size(r)); +out1: + kfree(ctx); + return ret; } -module_init(au1xxx_nand_init); - -/* - * Clean up routine - */ -static void __exit au1550_cleanup(void) +static int au1550nd_remove(struct platform_device *pdev) { - /* Release resources, unregister device */ - nand_release(au1550_mtd); + struct au1550nd_ctx *ctx = platform_get_drvdata(pdev); + struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - /* Free the MTD device structure */ - kfree(au1550_mtd); - - /* Unmap */ - iounmap(p_nand); + nand_release(&ctx->info); + iounmap(ctx->base); + release_mem_region(r->start, 0x1000); + kfree(ctx); + return 0; } -module_exit(au1550_cleanup); +static struct platform_driver au1550nd_driver = { + .driver = { + .name = "au1550-nand", + .owner = THIS_MODULE, + }, + .probe = au1550nd_probe, + .remove = au1550nd_remove, +}; + +module_platform_driver(au1550nd_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Embedded Edge, LLC"); diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c deleted file mode 100644 index 7c95da1f612..00000000000 --- a/drivers/mtd/nand/autcpu12.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * drivers/mtd/autcpu12.c - * - * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> - * - * Derived from drivers/mtd/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.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 NAND flash device found on the - * autronix autcpu12 board, which is a SmartMediaCard. It supports - * 16MiB, 32MiB and 64MiB cards. - * - * - * 02-12-2002 TG Cleanup of module params - * - * 02-20-2002 TG adjusted for different rd/wr address support - * added support for read device ready/busy line - * added page_cache - * - * 10-06-2002 TG 128K card support added - */ - -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <mach/hardware.h> -#include <asm/sizes.h> -#include <mach/autcpu12.h> - -/* - * MTD structure for AUTCPU12 board - */ -static struct mtd_info *autcpu12_mtd = NULL; -static void __iomem *autcpu12_fio_base; - -/* - * Define partitions for flash devices - */ -static struct mtd_partition partition_info16k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 8 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 8 * SZ_1M, - .size = 8 * SZ_1M }, -}; - -static struct mtd_partition partition_info32k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 8 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 8 * SZ_1M, - .size = 24 * SZ_1M }, -}; - -static struct mtd_partition partition_info64k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 16 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 16 * SZ_1M, - .size = 48 * SZ_1M }, -}; - -static struct mtd_partition partition_info128k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 16 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 16 * SZ_1M, - .size = 112 * SZ_1M }, -}; - -#define NUM_PARTITIONS16K 2 -#define NUM_PARTITIONS32K 2 -#define NUM_PARTITIONS64K 2 -#define NUM_PARTITIONS128K 2 -/* - * hardware specific access to control-lines - * - * ALE bit 4 autcpu12_pedr - * CLE bit 5 autcpu12_pedr - * NCE bit 0 fio_ctrl - * - */ -static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - void __iomem *addr; - unsigned char bits; - - addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET; - bits = (ctrl & NAND_CLE) << 4; - bits |= (ctrl & NAND_ALE) << 2; - writeb((readb(addr) & ~0x30) | bits, addr); - - addr = autcpu12_fio_base + AUTCPU12_SMC_SELECT_OFFSET; - writeb((readb(addr) & ~0x1) | (ctrl & NAND_NCE), addr); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * read device ready pin - */ -int autcpu12_device_ready(struct mtd_info *mtd) -{ - void __iomem *addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET; - - return readb(addr) & AUTCPU12_SMC_RDY; -} - -/* - * Main initialization routine - */ -static int __init autcpu12_init(void) -{ - struct nand_chip *this; - int err = 0; - - /* Allocate memory for MTD device structure and private data */ - autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), - GFP_KERNEL); - if (!autcpu12_mtd) { - printk("Unable to allocate AUTCPU12 NAND MTD device structure.\n"); - err = -ENOMEM; - goto out; - } - - /* map physical address */ - autcpu12_fio_base = ioremap(AUTCPU12_PHYS_SMC, SZ_1K); - if (!autcpu12_fio_base) { - printk("Ioremap autcpu12 SmartMedia Card failed\n"); - err = -EIO; - goto out_mtd; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&autcpu12_mtd[1]); - - /* Initialize structures */ - memset(autcpu12_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - autcpu12_mtd->priv = this; - autcpu12_mtd->owner = THIS_MODULE; - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = autcpu12_fio_base; - this->IO_ADDR_W = autcpu12_fio_base; - this->cmd_ctrl = autcpu12_hwcontrol; - this->dev_ready = autcpu12_device_ready; - /* 20 us command delay time */ - this->chip_delay = 20; - this->ecc.mode = NAND_ECC_SOFT; - - /* Enable the following for a flash based bad block table */ - /* - this->options = NAND_USE_FLASH_BBT; - */ - this->options = NAND_USE_FLASH_BBT; - - /* Scan to find existance of the device */ - if (nand_scan(autcpu12_mtd, 1)) { - err = -ENXIO; - goto out_ior; - } - - /* Register the partitions */ - switch (autcpu12_mtd->size) { - case SZ_16M: - add_mtd_partitions(autcpu12_mtd, partition_info16k, - NUM_PARTITIONS16K); - break; - case SZ_32M: - add_mtd_partitions(autcpu12_mtd, partition_info32k, - NUM_PARTITIONS32K); - break; - case SZ_64M: - add_mtd_partitions(autcpu12_mtd, partition_info64k, - NUM_PARTITIONS64K); - break; - case SZ_128M: - add_mtd_partitions(autcpu12_mtd, partition_info128k, - NUM_PARTITIONS128K); - break; - default: - printk("Unsupported SmartMedia device\n"); - err = -ENXIO; - goto out_ior; - } - goto out; - - out_ior: - iounmap(autcpu12_fio_base); - out_mtd: - kfree(autcpu12_mtd); - out: - return err; -} - -module_init(autcpu12_init); - -/* - * Clean up routine - */ -static void __exit autcpu12_cleanup(void) -{ - /* Release resources, unregister device */ - nand_release(autcpu12_mtd); - - /* unmap physical address */ - iounmap(autcpu12_fio_base); - - /* Free the MTD device structure */ - kfree(autcpu12_mtd); -} - -module_exit(autcpu12_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); -MODULE_DESCRIPTION("Glue layer for SmartMediaCard on autronix autcpu12"); diff --git a/drivers/mtd/nand/bcm47xxnflash/Makefile b/drivers/mtd/nand/bcm47xxnflash/Makefile new file mode 100644 index 00000000000..f05b119e134 --- /dev/null +++ b/drivers/mtd/nand/bcm47xxnflash/Makefile @@ -0,0 +1,4 @@ +bcm47xxnflash-y += main.o +bcm47xxnflash-y += ops_bcm4706.o + +obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash.o diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h new file mode 100644 index 00000000000..c005a62330b --- /dev/null +++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h @@ -0,0 +1,26 @@ +#ifndef __BCM47XXNFLASH_H +#define __BCM47XXNFLASH_H + +#ifndef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> + +struct bcm47xxnflash { + struct bcma_drv_cc *cc; + + struct nand_chip nand_chip; + struct mtd_info mtd; + + unsigned curr_command; + int curr_page_addr; + int curr_column; + + u8 id_data[8]; +}; + +int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n); + +#endif /* BCM47XXNFLASH */ diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c new file mode 100644 index 00000000000..10744591131 --- /dev/null +++ b/drivers/mtd/nand/bcm47xxnflash/main.c @@ -0,0 +1,80 @@ +/* + * BCM47XX NAND flash driver + * + * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "bcm47xxnflash.h" + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/bcma/bcma.h> + +MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rafał Miłecki"); + +static const char *probes[] = { "bcm47xxpart", NULL }; + +static int bcm47xxnflash_probe(struct platform_device *pdev) +{ + struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev); + struct bcm47xxnflash *b47n; + int err = 0; + + b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL); + if (!b47n) + return -ENOMEM; + + b47n->nand_chip.priv = b47n; + b47n->mtd.owner = THIS_MODULE; + b47n->mtd.priv = &b47n->nand_chip; /* Required */ + b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash); + + if (b47n->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { + err = bcm47xxnflash_ops_bcm4706_init(b47n); + } else { + pr_err("Device not supported\n"); + err = -ENOTSUPP; + } + if (err) { + pr_err("Initialization failed: %d\n", err); + return err; + } + + err = mtd_device_parse_register(&b47n->mtd, probes, NULL, NULL, 0); + if (err) { + pr_err("Failed to register MTD device: %d\n", err); + return err; + } + + return 0; +} + +static int bcm47xxnflash_remove(struct platform_device *pdev) +{ + struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev); + + if (nflash->mtd) + mtd_device_unregister(nflash->mtd); + + return 0; +} + +static struct platform_driver bcm47xxnflash_driver = { + .probe = bcm47xxnflash_probe, + .remove = bcm47xxnflash_remove, + .driver = { + .name = "bcma_nflash", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bcm47xxnflash_driver); diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c new file mode 100644 index 00000000000..b2ab373c9ee --- /dev/null +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c @@ -0,0 +1,413 @@ +/* + * BCM47XX NAND flash driver + * + * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "bcm47xxnflash.h" + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/bcma/bcma.h> + +/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has + * shown ~1000 retries as maxiumum. */ +#define NFLASH_READY_RETRIES 10000 + +#define NFLASH_SECTOR_SIZE 512 + +#define NCTL_CMD0 0x00010000 +#define NCTL_CMD1W 0x00080000 +#define NCTL_READ 0x00100000 +#define NCTL_WRITE 0x00200000 +#define NCTL_SPECADDR 0x01000000 +#define NCTL_READY 0x04000000 +#define NCTL_ERR 0x08000000 +#define NCTL_CSA 0x40000000 +#define NCTL_START 0x80000000 + +/************************************************** + * Various helpers + **************************************************/ + +static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock) +{ + return ((ns * 1000 * clock) / 1000000) + 1; +} + +static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code) +{ + int i = 0; + + bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code); + for (i = 0; i < NFLASH_READY_RETRIES; i++) { + if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) { + i = 0; + break; + } + } + if (i) { + pr_err("NFLASH control command not ready!\n"); + return -EBUSY; + } + return 0; +} + +static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc) +{ + int i; + + for (i = 0; i < NFLASH_READY_RETRIES; i++) { + if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) { + if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & + BCMA_CC_NFLASH_CTL_ERR) { + pr_err("Error on polling\n"); + return -EBUSY; + } else { + return 0; + } + } + } + + pr_err("Polling timeout!\n"); + return -EBUSY; +} + +/************************************************** + * R/W + **************************************************/ + +static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + + u32 ctlcode; + u32 *dest = (u32 *)buf; + int i; + int toread; + + BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); + /* Don't validate column using nand_chip->page_shift, it may be bigger + * when accessing OOB */ + + while (len) { + /* We can read maximum of 0x200 bytes at once */ + toread = min(len, 0x200); + + /* Set page and column */ + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR, + b47n->curr_column); + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR, + b47n->curr_page_addr); + + /* Prepare to read */ + ctlcode = NCTL_CSA | NCTL_CMD1W | 0x00040000 | 0x00020000 | + NCTL_CMD0; + ctlcode |= NAND_CMD_READSTART << 8; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) + return; + if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc)) + return; + + /* Eventually read some data :) */ + for (i = 0; i < toread; i += 4, dest++) { + ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ; + if (i == toread - 4) /* Last read goes without that */ + ctlcode &= ~NCTL_CSA; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, + ctlcode)) + return; + *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA); + } + + b47n->curr_column += toread; + len -= toread; + } +} + +static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + struct bcma_drv_cc *cc = b47n->cc; + + u32 ctlcode; + const u32 *data = (u32 *)buf; + int i; + + BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); + /* Don't validate column using nand_chip->page_shift, it may be bigger + * when accessing OOB */ + + for (i = 0; i < len; i += 4, data++) { + bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data); + + ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE; + if (i == len - 4) /* Last read goes without that */ + ctlcode &= ~NCTL_CSA; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) { + pr_err("%s ctl_cmd didn't work!\n", __func__); + return; + } + } + + b47n->curr_column += len; +} + +/************************************************** + * NAND chip ops + **************************************************/ + +/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */ +static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd, + int chip) +{ + return; +} + +/* + * Default nand_command and nand_command_lp don't match BCM4706 hardware layout. + * For example, reading chip id is performed in a non-standard way. + * Setting column and page is also handled differently, we use a special + * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert + * standard commands would be much more complicated. + */ +static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd, + unsigned command, int column, + int page_addr) +{ + struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + struct bcma_drv_cc *cc = b47n->cc; + u32 ctlcode; + int i; + + if (column != -1) + b47n->curr_column = column; + if (page_addr != -1) + b47n->curr_page_addr = page_addr; + + switch (command) { + case NAND_CMD_RESET: + pr_warn("Chip reset not implemented yet\n"); + break; + case NAND_CMD_READID: + ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0; + ctlcode |= NAND_CMD_READID; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) { + pr_err("READID error\n"); + break; + } + + /* + * Reading is specific, last one has to go without NCTL_CSA + * bit. We don't know how many reads NAND subsystem is going + * to perform, so cache everything. + */ + for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) { + ctlcode = NCTL_CSA | NCTL_READ; + if (i == ARRAY_SIZE(b47n->id_data) - 1) + ctlcode &= ~NCTL_CSA; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, + ctlcode)) { + pr_err("READID error\n"); + break; + } + b47n->id_data[i] = + bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA) + & 0xFF; + } + + break; + case NAND_CMD_STATUS: + ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) + pr_err("STATUS command error\n"); + break; + case NAND_CMD_READ0: + break; + case NAND_CMD_READOOB: + if (page_addr != -1) + b47n->curr_column += mtd->writesize; + break; + case NAND_CMD_ERASE1: + bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, + b47n->curr_page_addr); + ctlcode = 0x00040000 | NCTL_CMD1W | NCTL_CMD0 | + NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8); + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) + pr_err("ERASE1 failed\n"); + break; + case NAND_CMD_ERASE2: + break; + case NAND_CMD_SEQIN: + /* Set page and column */ + bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR, + b47n->curr_column); + bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, + b47n->curr_page_addr); + + /* Prepare to write */ + ctlcode = 0x40000000 | 0x00040000 | 0x00020000 | 0x00010000; + ctlcode |= NAND_CMD_SEQIN; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) + pr_err("SEQIN failed\n"); + break; + case NAND_CMD_PAGEPROG: + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, 0x00010000 | + NAND_CMD_PAGEPROG)) + pr_err("PAGEPROG failed\n"); + if (bcm47xxnflash_ops_bcm4706_poll(cc)) + pr_err("PAGEPROG not ready\n"); + break; + default: + pr_err("Command 0x%X unsupported\n", command); + break; + } + b47n->curr_command = command; +} + +static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + struct bcma_drv_cc *cc = b47n->cc; + u32 tmp = 0; + + switch (b47n->curr_command) { + case NAND_CMD_READID: + if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) { + pr_err("Requested invalid id_data: %d\n", + b47n->curr_column); + return 0; + } + return b47n->id_data[b47n->curr_column++]; + case NAND_CMD_STATUS: + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ)) + return 0; + return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff; + case NAND_CMD_READOOB: + bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4); + return tmp & 0xFF; + } + + pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command); + return 0; +} + +static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd, + uint8_t *buf, int len) +{ + struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + + switch (b47n->curr_command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + bcm47xxnflash_ops_bcm4706_read(mtd, buf, len); + return; + } + + pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command); +} + +static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + + switch (b47n->curr_command) { + case NAND_CMD_SEQIN: + bcm47xxnflash_ops_bcm4706_write(mtd, buf, len); + return; + } + + pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command); +} + +/************************************************** + * Init + **************************************************/ + +int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) +{ + int err; + u32 freq; + u16 clock; + u8 w0, w1, w2, w3, w4; + + unsigned long chipsize; /* MiB */ + u8 tbits, col_bits, col_size, row_bits, row_bsize; + u32 val; + + b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; + b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; + b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte; + b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; + b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf; + b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; + b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */ + + /* Enable NAND flash access */ + bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG, + BCMA_CC_4706_FLASHSCFG_NF1); + + /* Configure wait counters */ + if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) { + freq = 100000000; + } else { + freq = bcma_chipco_pll_read(b47n->cc, 4); + freq = (freq * 0xFFF) >> 3; + freq = (freq * 25000000) >> 3; + } + clock = freq / 1000000; + w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock); + w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock); + w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); + w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); + w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock); + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0, + (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0)); + + /* Scan NAND */ + err = nand_scan(&b47n->mtd, 1); + if (err) { + pr_err("Could not scan NAND flash: %d\n", err); + goto exit; + } + + /* Configure FLASH */ + chipsize = b47n->nand_chip.chipsize >> 20; + tbits = ffs(chipsize); /* find first bit set */ + if (!tbits || tbits != fls(chipsize)) { + pr_err("Invalid flash size: 0x%lX\n", chipsize); + err = -ENOTSUPP; + goto exit; + } + tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */ + + col_bits = b47n->nand_chip.page_shift + 1; + col_size = (col_bits + 7) / 8; + + row_bits = tbits - col_bits + 1; + row_bsize = (row_bits + 7) / 8; + + val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2; + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val); + +exit: + if (err) + bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG, + ~BCMA_CC_4706_FLASHSCFG_NF1); + return err; +} diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c deleted file mode 100644 index a930666d068..00000000000 --- a/drivers/mtd/nand/bcm_umi_bch.c +++ /dev/null @@ -1,213 +0,0 @@ -/***************************************************************************** -* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. -* -* Unless you and Broadcom execute a separate written software license -* agreement governing use of this software, this software is licensed to you -* under the terms of the GNU General Public License version 2, available at -* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -* -* Notwithstanding the above, under no circumstances may you combine this -* software in any way with any other Broadcom software provided under a -* license other than the GPL, without Broadcom's express prior written -* consent. -*****************************************************************************/ - -/* ---- Include Files ---------------------------------------------------- */ -#include "nand_bcm_umi.h" - -/* ---- External Variable Declarations ----------------------------------- */ -/* ---- External Function Prototypes ------------------------------------- */ -/* ---- Public Variables ------------------------------------------------- */ -/* ---- Private Constants and Types -------------------------------------- */ - -/* ---- Private Function Prototypes -------------------------------------- */ -static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page); -static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf); - -/* ---- Private Variables ------------------------------------------------ */ - -/* -** nand_hw_eccoob -** New oob placement block for use with hardware ecc generation. -*/ -static struct nand_ecclayout nand_hw_eccoob_512 = { - /* Reserve 5 for BI indicator */ - .oobfree = { -#if (NAND_ECC_NUM_BYTES > 3) - {.offset = 0, .length = 2} -#else - {.offset = 0, .length = 5}, - {.offset = 6, .length = 7} -#endif - } -}; - -/* -** We treat the OOB for a 2K page as if it were 4 512 byte oobs, -** except the BI is at byte 0. -*/ -static struct nand_ecclayout nand_hw_eccoob_2048 = { - /* Reserve 0 as BI indicator */ - .oobfree = { -#if (NAND_ECC_NUM_BYTES > 10) - {.offset = 1, .length = 2}, -#elif (NAND_ECC_NUM_BYTES > 7) - {.offset = 1, .length = 5}, - {.offset = 16, .length = 6}, - {.offset = 32, .length = 6}, - {.offset = 48, .length = 6} -#else - {.offset = 1, .length = 8}, - {.offset = 16, .length = 9}, - {.offset = 32, .length = 9}, - {.offset = 48, .length = 9} -#endif - } -}; - -/* We treat the OOB for a 4K page as if it were 8 512 byte oobs, - * except the BI is at byte 0. */ -static struct nand_ecclayout nand_hw_eccoob_4096 = { - /* Reserve 0 as BI indicator */ - .oobfree = { -#if (NAND_ECC_NUM_BYTES > 10) - {.offset = 1, .length = 2}, - {.offset = 16, .length = 3}, - {.offset = 32, .length = 3}, - {.offset = 48, .length = 3}, - {.offset = 64, .length = 3}, - {.offset = 80, .length = 3}, - {.offset = 96, .length = 3}, - {.offset = 112, .length = 3} -#else - {.offset = 1, .length = 5}, - {.offset = 16, .length = 6}, - {.offset = 32, .length = 6}, - {.offset = 48, .length = 6}, - {.offset = 64, .length = 6}, - {.offset = 80, .length = 6}, - {.offset = 96, .length = 6}, - {.offset = 112, .length = 6} -#endif - } -}; - -/* ---- Private Functions ------------------------------------------------ */ -/* ==== Public Functions ================================================= */ - -/**************************************************************************** -* -* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function -* @mtd: mtd info structure -* @chip: nand chip info structure -* @buf: buffer to store read data -* -***************************************************************************/ -static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t * buf, - int page) -{ - int sectorIdx = 0; - int eccsize = chip->ecc.size; - int eccsteps = chip->ecc.steps; - uint8_t *datap = buf; - uint8_t eccCalc[NAND_ECC_NUM_BYTES]; - int sectorOobSize = mtd->oobsize / eccsteps; - int stat; - - for (sectorIdx = 0; sectorIdx < eccsteps; - sectorIdx++, datap += eccsize) { - if (sectorIdx > 0) { - /* Seek to page location within sector */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, - -1); - } - - /* Enable hardware ECC before reading the buf */ - nand_bcm_umi_bch_enable_read_hwecc(); - - /* Read in data */ - bcm_umi_nand_read_buf(mtd, datap, eccsize); - - /* Pause hardware ECC after reading the buf */ - nand_bcm_umi_bch_pause_read_ecc_calc(); - - /* Read the OOB ECC */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + sectorIdx * sectorOobSize, -1); - nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, - NAND_ECC_NUM_BYTES, - chip->oob_poi + - sectorIdx * sectorOobSize); - - /* Correct any ECC detected errors */ - stat = - nand_bcm_umi_bch_correct_page(datap, eccCalc, - NAND_ECC_NUM_BYTES); - - /* Update Stats */ - if (stat < 0) { -#if defined(NAND_BCM_UMI_DEBUG) - printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", - __func__, sectorIdx); - printk(KERN_WARNING - "%s data %02x %02x %02x %02x " - "%02x %02x %02x %02x\n", - __func__, datap[0], datap[1], datap[2], datap[3], - datap[4], datap[5], datap[6], datap[7]); - printk(KERN_WARNING - "%s ecc %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x " - "%02x %02x %02x\n", - __func__, eccCalc[0], eccCalc[1], eccCalc[2], - eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], - eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], - eccCalc[11], eccCalc[12]); - BUG(); -#endif - mtd->ecc_stats.failed++; - } else { -#if defined(NAND_BCM_UMI_DEBUG) - if (stat > 0) { - printk(KERN_INFO - "%s %d correctable_errors detected\n", - __func__, stat); - } -#endif - mtd->ecc_stats.corrected += stat; - } - } - return 0; -} - -/**************************************************************************** -* -* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function -* @mtd: mtd info structure -* @chip: nand chip info structure -* @buf: data buffer -* -***************************************************************************/ -static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) -{ - int sectorIdx = 0; - int eccsize = chip->ecc.size; - int eccsteps = chip->ecc.steps; - const uint8_t *datap = buf; - uint8_t *oobp = chip->oob_poi; - int sectorOobSize = mtd->oobsize / eccsteps; - - for (sectorIdx = 0; sectorIdx < eccsteps; - sectorIdx++, datap += eccsize, oobp += sectorOobSize) { - /* Enable hardware ECC before writing the buf */ - nand_bcm_umi_bch_enable_write_hwecc(); - bcm_umi_nand_write_buf(mtd, datap, eccsize); - nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, - NAND_ECC_NUM_BYTES); - } - - bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); -} diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c deleted file mode 100644 index dfe262c726f..00000000000 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ /dev/null @@ -1,581 +0,0 @@ -/***************************************************************************** -* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. -* -* Unless you and Broadcom execute a separate written software license -* agreement governing use of this software, this software is licensed to you -* under the terms of the GNU General Public License version 2, available at -* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -* -* Notwithstanding the above, under no circumstances may you combine this -* software in any way with any other Broadcom software provided under a -* license other than the GPL, without Broadcom's express prior written -* consent. -*****************************************************************************/ - -/* ---- Include Files ---------------------------------------------------- */ -#include <linux/module.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/ioport.h> -#include <linux/device.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/nand_ecc.h> -#include <linux/mtd/partitions.h> - -#include <asm/mach-types.h> -#include <asm/system.h> - -#include <mach/reg_nand.h> -#include <mach/reg_umi.h> - -#include "nand_bcm_umi.h" - -#include <mach/memory_settings.h> - -#define USE_DMA 1 -#include <mach/dma.h> -#include <linux/dma-mapping.h> -#include <linux/completion.h> - -/* ---- External Variable Declarations ----------------------------------- */ -/* ---- External Function Prototypes ------------------------------------- */ -/* ---- Public Variables ------------------------------------------------- */ -/* ---- Private Constants and Types -------------------------------------- */ -static const __devinitconst char gBanner[] = KERN_INFO \ - "BCM UMI MTD NAND Driver: 1.00\n"; - -#ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - -#if NAND_ECC_BCH -static uint8_t scan_ff_pattern[] = { 0xff }; - -static struct nand_bbt_descr largepage_bbt = { - .options = 0, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern -}; -#endif - -/* -** Preallocate a buffer to avoid having to do this every dma operation. -** This is the size of the preallocated coherent DMA buffer. -*/ -#if USE_DMA -#define DMA_MIN_BUFLEN 512 -#define DMA_MAX_BUFLEN PAGE_SIZE -#define USE_DIRECT_IO(len) (((len) < DMA_MIN_BUFLEN) || \ - ((len) > DMA_MAX_BUFLEN)) - -/* - * The current NAND data space goes from 0x80001900 to 0x80001FFF, - * which is only 0x700 = 1792 bytes long. This is too small for 2K, 4K page - * size NAND flash. Need to break the DMA down to multiple 1Ks. - * - * Need to make sure REG_NAND_DATA_PADDR + DMA_MAX_LEN < 0x80002000 - */ -#define DMA_MAX_LEN 1024 - -#else /* !USE_DMA */ -#define DMA_MIN_BUFLEN 0 -#define DMA_MAX_BUFLEN 0 -#define USE_DIRECT_IO(len) 1 -#endif -/* ---- Private Function Prototypes -------------------------------------- */ -static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len); -static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf, - int len); - -/* ---- Private Variables ------------------------------------------------ */ -static struct mtd_info *board_mtd; -static void __iomem *bcm_umi_io_base; -static void *virtPtr; -static dma_addr_t physPtr; -static struct completion nand_comp; - -/* ---- Private Functions ------------------------------------------------ */ -#if NAND_ECC_BCH -#include "bcm_umi_bch.c" -#else -#include "bcm_umi_hamming.c" -#endif - -#if USE_DMA - -/* Handler called when the DMA finishes. */ -static void nand_dma_handler(DMA_Device_t dev, int reason, void *userData) -{ - complete(&nand_comp); -} - -static int nand_dma_init(void) -{ - int rc; - - rc = dma_set_device_handler(DMA_DEVICE_NAND_MEM_TO_MEM, - nand_dma_handler, NULL); - if (rc != 0) { - printk(KERN_ERR "dma_set_device_handler failed: %d\n", rc); - return rc; - } - - virtPtr = - dma_alloc_coherent(NULL, DMA_MAX_BUFLEN, &physPtr, GFP_KERNEL); - if (virtPtr == NULL) { - printk(KERN_ERR "NAND - Failed to allocate memory for DMA buffer\n"); - return -ENOMEM; - } - - return 0; -} - -static void nand_dma_term(void) -{ - if (virtPtr != NULL) - dma_free_coherent(NULL, DMA_MAX_BUFLEN, virtPtr, physPtr); -} - -static void nand_dma_read(void *buf, int len) -{ - int offset = 0; - int tmp_len = 0; - int len_left = len; - DMA_Handle_t hndl; - - if (virtPtr == NULL) - panic("nand_dma_read: virtPtr == NULL\n"); - - if ((void *)physPtr == NULL) - panic("nand_dma_read: physPtr == NULL\n"); - - hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM); - if (hndl < 0) { - printk(KERN_ERR - "nand_dma_read: unable to allocate dma channel: %d\n", - (int)hndl); - panic("\n"); - } - - while (len_left > 0) { - if (len_left > DMA_MAX_LEN) { - tmp_len = DMA_MAX_LEN; - len_left -= DMA_MAX_LEN; - } else { - tmp_len = len_left; - len_left = 0; - } - - init_completion(&nand_comp); - dma_transfer_mem_to_mem(hndl, REG_NAND_DATA_PADDR, - physPtr + offset, tmp_len); - wait_for_completion(&nand_comp); - - offset += tmp_len; - } - - dma_free_channel(hndl); - - if (buf != NULL) - memcpy(buf, virtPtr, len); -} - -static void nand_dma_write(const void *buf, int len) -{ - int offset = 0; - int tmp_len = 0; - int len_left = len; - DMA_Handle_t hndl; - - if (buf == NULL) - panic("nand_dma_write: buf == NULL\n"); - - if (virtPtr == NULL) - panic("nand_dma_write: virtPtr == NULL\n"); - - if ((void *)physPtr == NULL) - panic("nand_dma_write: physPtr == NULL\n"); - - memcpy(virtPtr, buf, len); - - - hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM); - if (hndl < 0) { - printk(KERN_ERR - "nand_dma_write: unable to allocate dma channel: %d\n", - (int)hndl); - panic("\n"); - } - - while (len_left > 0) { - if (len_left > DMA_MAX_LEN) { - tmp_len = DMA_MAX_LEN; - len_left -= DMA_MAX_LEN; - } else { - tmp_len = len_left; - len_left = 0; - } - - init_completion(&nand_comp); - dma_transfer_mem_to_mem(hndl, physPtr + offset, - REG_NAND_DATA_PADDR, tmp_len); - wait_for_completion(&nand_comp); - - offset += tmp_len; - } - - dma_free_channel(hndl); -} - -#endif - -static int nand_dev_ready(struct mtd_info *mtd) -{ - return nand_bcm_umi_dev_ready(); -} - -/**************************************************************************** -* -* bcm_umi_nand_inithw -* -* This routine does the necessary hardware (board-specific) -* initializations. This includes setting up the timings, etc. -* -***************************************************************************/ -int bcm_umi_nand_inithw(void) -{ - /* Configure nand timing parameters */ - REG_UMI_NAND_TCR &= ~0x7ffff; - REG_UMI_NAND_TCR |= HW_CFG_NAND_TCR; - -#if !defined(CONFIG_MTD_NAND_BCM_UMI_HWCS) - /* enable software control of CS */ - REG_UMI_NAND_TCR |= REG_UMI_NAND_TCR_CS_SWCTRL; -#endif - - /* keep NAND chip select asserted */ - REG_UMI_NAND_RCSR |= REG_UMI_NAND_RCSR_CS_ASSERTED; - - REG_UMI_NAND_TCR &= ~REG_UMI_NAND_TCR_WORD16; - /* enable writes to flash */ - REG_UMI_MMD_ICR |= REG_UMI_MMD_ICR_FLASH_WP; - - writel(NAND_CMD_RESET, bcm_umi_io_base + REG_NAND_CMD_OFFSET); - nand_bcm_umi_wait_till_ready(); - -#if NAND_ECC_BCH - nand_bcm_umi_bch_config_ecc(NAND_ECC_NUM_BYTES); -#endif - - return 0; -} - -/* Used to turn latch the proper register for access. */ -static void bcm_umi_nand_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - /* send command to hardware */ - struct nand_chip *chip = mtd->priv; - if (ctrl & NAND_CTRL_CHANGE) { - if (ctrl & NAND_CLE) { - chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_CMD_OFFSET; - goto CMD; - } - if (ctrl & NAND_ALE) { - chip->IO_ADDR_W = - bcm_umi_io_base + REG_NAND_ADDR_OFFSET; - goto CMD; - } - chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; - } - -CMD: - /* Send command to chip directly */ - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf, - int len) -{ - if (USE_DIRECT_IO(len)) { - /* Do it the old way if the buffer is small or too large. - * Probably quicker than starting and checking dma. */ - int i; - struct nand_chip *this = mtd->priv; - - for (i = 0; i < len; i++) - writeb(buf[i], this->IO_ADDR_W); - } -#if USE_DMA - else - nand_dma_write(buf, len); -#endif -} - -static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) -{ - if (USE_DIRECT_IO(len)) { - int i; - struct nand_chip *this = mtd->priv; - - for (i = 0; i < len; i++) - buf[i] = readb(this->IO_ADDR_R); - } -#if USE_DMA - else - nand_dma_read(buf, len); -#endif -} - -static uint8_t readbackbuf[NAND_MAX_PAGESIZE]; -static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, - int len) -{ - /* - * Try to readback page with ECC correction. This is necessary - * for MLC parts which may have permanently stuck bits. - */ - struct nand_chip *chip = mtd->priv; - int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0); - if (ret < 0) - return -EFAULT; - else { - if (memcmp(readbackbuf, buf, len) == 0) - return 0; - - return -EFAULT; - } - return 0; -} - -static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) -{ - struct nand_chip *this; - struct resource *r; - int err = 0; - - printk(gBanner); - - /* Allocate memory for MTD device structure and private data */ - board_mtd = - kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), - GFP_KERNEL); - if (!board_mtd) { - printk(KERN_WARNING - "Unable to allocate NAND MTD device structure.\n"); - return -ENOMEM; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - if (!r) - return -ENXIO; - - /* map physical address */ - bcm_umi_io_base = ioremap(r->start, r->end - r->start + 1); - - if (!bcm_umi_io_base) { - printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n"); - kfree(board_mtd); - return -EIO; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&board_mtd[1]); - - /* Initialize structures */ - memset((char *)board_mtd, 0, sizeof(struct mtd_info)); - memset((char *)this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - board_mtd->priv = this; - - /* Initialize the NAND hardware. */ - if (bcm_umi_nand_inithw() < 0) { - printk(KERN_ERR "BCM UMI NAND chip could not be initialized\n"); - iounmap(bcm_umi_io_base); - kfree(board_mtd); - return -EIO; - } - - /* Set address of NAND IO lines */ - this->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; - this->IO_ADDR_R = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; - - /* Set command delay time, see datasheet for correct value */ - this->chip_delay = 0; - /* Assign the device ready function, if available */ - this->dev_ready = nand_dev_ready; - this->options = 0; - - this->write_buf = bcm_umi_nand_write_buf; - this->read_buf = bcm_umi_nand_read_buf; - this->verify_buf = bcm_umi_nand_verify_buf; - - this->cmd_ctrl = bcm_umi_nand_hwcontrol; - this->ecc.mode = NAND_ECC_HW; - this->ecc.size = 512; - this->ecc.bytes = NAND_ECC_NUM_BYTES; -#if NAND_ECC_BCH - this->ecc.read_page = bcm_umi_bch_read_page_hwecc; - this->ecc.write_page = bcm_umi_bch_write_page_hwecc; -#else - this->ecc.correct = nand_correct_data512; - this->ecc.calculate = bcm_umi_hamming_get_hw_ecc; - this->ecc.hwctl = bcm_umi_hamming_enable_hwecc; -#endif - -#if USE_DMA - err = nand_dma_init(); - if (err != 0) - return err; -#endif - - /* Figure out the size of the device that we have. - * We need to do this to figure out which ECC - * layout we'll be using. - */ - - err = nand_scan_ident(board_mtd, 1, NULL); - if (err) { - printk(KERN_ERR "nand_scan failed: %d\n", err); - iounmap(bcm_umi_io_base); - kfree(board_mtd); - return err; - } - - /* Now that we know the nand size, we can setup the ECC layout */ - - switch (board_mtd->writesize) { /* writesize is the pagesize */ - case 4096: - this->ecc.layout = &nand_hw_eccoob_4096; - break; - case 2048: - this->ecc.layout = &nand_hw_eccoob_2048; - break; - case 512: - this->ecc.layout = &nand_hw_eccoob_512; - break; - default: - { - printk(KERN_ERR "NAND - Unrecognized pagesize: %d\n", - board_mtd->writesize); - return -EINVAL; - } - } - -#if NAND_ECC_BCH - if (board_mtd->writesize > 512) { - if (this->options & NAND_USE_FLASH_BBT) - largepage_bbt.options = NAND_BBT_SCAN2NDPAGE; - this->badblock_pattern = &largepage_bbt; - } -#endif - - /* Now finish off the scan, now that ecc.layout has been initialized. */ - - err = nand_scan_tail(board_mtd); - if (err) { - printk(KERN_ERR "nand_scan failed: %d\n", err); - iounmap(bcm_umi_io_base); - kfree(board_mtd); - return err; - } - - /* Register the partitions */ - { - int nr_partitions; - struct mtd_partition *partition_info; - - board_mtd->name = "bcm_umi-nand"; - nr_partitions = - parse_mtd_partitions(board_mtd, part_probes, - &partition_info, 0); - - if (nr_partitions <= 0) { - printk(KERN_ERR "BCM UMI NAND: Too few partitions - %d\n", - nr_partitions); - iounmap(bcm_umi_io_base); - kfree(board_mtd); - return -EIO; - } - add_mtd_partitions(board_mtd, partition_info, nr_partitions); - } - - /* Return happy */ - return 0; -} - -static int bcm_umi_nand_remove(struct platform_device *pdev) -{ -#if USE_DMA - nand_dma_term(); -#endif - - /* Release resources, unregister device */ - nand_release(board_mtd); - - /* unmap physical address */ - iounmap(bcm_umi_io_base); - - /* Free the MTD device structure */ - kfree(board_mtd); - - return 0; -} - -#ifdef CONFIG_PM -static int bcm_umi_nand_suspend(struct platform_device *pdev, - pm_message_t state) -{ - printk(KERN_ERR "MTD NAND suspend is being called\n"); - return 0; -} - -static int bcm_umi_nand_resume(struct platform_device *pdev) -{ - printk(KERN_ERR "MTD NAND resume is being called\n"); - return 0; -} -#else -#define bcm_umi_nand_suspend NULL -#define bcm_umi_nand_resume NULL -#endif - -static struct platform_driver nand_driver = { - .driver = { - .name = "bcm-nand", - .owner = THIS_MODULE, - }, - .probe = bcm_umi_nand_probe, - .remove = bcm_umi_nand_remove, - .suspend = bcm_umi_nand_suspend, - .resume = bcm_umi_nand_resume, -}; - -static int __init nand_init(void) -{ - return platform_driver_register(&nand_driver); -} - -static void __exit nand_exit(void) -{ - platform_driver_unregister(&nand_driver); -} - -module_init(nand_init); -module_exit(nand_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Broadcom"); -MODULE_DESCRIPTION("BCM UMI MTD NAND driver"); diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 79947bea4d5..722898aea7a 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -37,7 +37,6 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> @@ -171,7 +170,7 @@ static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) { - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } /* @@ -558,7 +557,7 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, } static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { bf5xx_nand_read_buf(mtd, buf, mtd->writesize); bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -566,11 +565,13 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip return 0; } -static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) +static int bf5xx_nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) { bf5xx_nand_write_buf(mtd, buf, mtd->writesize); bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; } /* @@ -656,26 +657,19 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) /* * Device management interface */ -static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info) +static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) { struct mtd_info *mtd = &info->mtd; - -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts = info->platform->partitions; int nr = info->platform->nr_partitions; - return add_mtd_partitions(mtd, parts, nr); -#else - return add_mtd_device(mtd); -#endif + return mtd_device_register(mtd, parts, nr); } -static int __devexit bf5xx_nand_remove(struct platform_device *pdev) +static int bf5xx_nand_remove(struct platform_device *pdev) { struct bf5xx_nand_info *info = to_nand_info(pdev); - platform_set_drvdata(pdev, NULL); - /* first thing we need to do is release all our mtds * and their partitions, then go through freeing the * resources used @@ -685,9 +679,6 @@ static int __devexit bf5xx_nand_remove(struct platform_device *pdev) peripheral_free_list(bfin_nfc_pin_req); bf5xx_nand_dma_remove(info); - /* free the common resources */ - kfree(info); - return 0; } @@ -707,9 +698,11 @@ static int bf5xx_nand_scan(struct mtd_info *mtd) 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(); } @@ -726,7 +719,7 @@ static int bf5xx_nand_scan(struct mtd_info *mtd) * it can allocate all necessary resources then calls the * nand layer to look for devices */ -static int __devinit bf5xx_nand_probe(struct platform_device *pdev) +static int bf5xx_nand_probe(struct platform_device *pdev) { struct bf5xx_nand_platform *plat = to_nand_plat(pdev); struct bf5xx_nand_info *info = NULL; @@ -746,11 +739,10 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev) return -EFAULT; } - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) { - dev_err(&pdev->dev, "no memory for flash info\n"); err = -ENOMEM; - goto out_err_kzalloc; + goto out_err; } platform_set_drvdata(pdev, info); @@ -795,7 +787,7 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev) /* initialise the hardware */ err = bf5xx_nand_hw_init(info); if (err) - goto out_err_hw_init; + goto out_err; /* setup hardware ECC data struct */ if (hardware_ecc) { @@ -832,10 +824,7 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev) out_err_nand_scan: bf5xx_nand_dma_remove(info); -out_err_hw_init: - platform_set_drvdata(pdev, NULL); - kfree(info); -out_err_kzalloc: +out_err: peripheral_free_list(bfin_nfc_pin_req); return err; @@ -866,7 +855,7 @@ static int bf5xx_nand_resume(struct platform_device *dev) /* driver device registration */ static struct platform_driver bf5xx_nand_driver = { .probe = bf5xx_nand_probe, - .remove = __devexit_p(bf5xx_nand_remove), + .remove = bf5xx_nand_remove, .suspend = bf5xx_nand_suspend, .resume = bf5xx_nand_resume, .driver = { @@ -875,21 +864,7 @@ static struct platform_driver bf5xx_nand_driver = { }, }; -static int __init bf5xx_nand_init(void) -{ - printk(KERN_INFO "%s, Version %s (c) 2007 Analog Devices, Inc.\n", - DRV_DESC, DRV_VERSION); - - return platform_driver_register(&bf5xx_nand_driver); -} - -static void __exit bf5xx_nand_exit(void) -{ - platform_driver_unregister(&bf5xx_nand_driver); -} - -module_init(bf5xx_nand_init); -module_exit(bf5xx_nand_exit); +module_platform_driver(bf5xx_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRV_AUTHOR); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index e06c8983978..4e66726da9a 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/module.h> #include <asm/io.h> #define CAFE_NAND_CTRL1 0x00 @@ -57,7 +58,6 @@ struct cafe_priv { struct nand_chip nand; - struct mtd_partition *parts; struct pci_dev *pdev; void __iomem *mmio; struct rs_control *rs; @@ -90,9 +90,7 @@ static unsigned int numtimings; static int timing[3]; module_param_array(timing, int, &numtimings, 0644); -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; -#endif /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -104,7 +102,7 @@ static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; static int cafe_device_ready(struct mtd_info *mtd) { struct cafe_priv *cafe = mtd->priv; - int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000); + int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000); uint32_t irqs = cafe_readl(cafe, NAND_IRQ); cafe_writel(cafe, irqs, NAND_IRQ); @@ -305,13 +303,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, case NAND_CMD_SEQIN: case NAND_CMD_RNDIN: case NAND_CMD_STATUS: - case NAND_CMD_DEPLETE1: case NAND_CMD_RNDOUT: - case NAND_CMD_STATUS_ERROR: - case NAND_CMD_STATUS_ERROR0: - case NAND_CMD_STATUS_ERROR1: - case NAND_CMD_STATUS_ERROR2: - case NAND_CMD_STATUS_ERROR3: cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); return; } @@ -366,25 +358,27 @@ static int cafe_nand_write_oob(struct mtd_info *mtd, /* Don't use -- use nand_read_oob_std for now */ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 1; + return 0; } /** - * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * - * The hw generator calculates the error syndrome automatically. Therefor + * The hw generator calculates the error syndrome automatically. Therefore * we need a special oob layout and handling. */ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct cafe_priv *cafe = mtd->priv; + unsigned int max_bitflips = 0; cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", cafe_readl(cafe, NAND_ECC_RESULT), @@ -451,10 +445,11 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, } else { dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); mtd->ecc_stats.corrected += n; + max_bitflips = max_t(unsigned int, max_bitflips, n); } } - return 0; + return max_bitflips; } static struct nand_ecclayout cafe_oobinfo_2048 = { @@ -519,8 +514,9 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { }; -static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) +static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct cafe_priv *cafe = mtd->priv; @@ -529,19 +525,25 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, /* Set up ECC autogeneration */ cafe->ctl2 |= (1<<30); + + return 0; } static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + status = chip->ecc.write_page(mtd, chip, buf, oob_required); + + if (status < 0) + return status; /* * Cached progamming disabled for now, Not sure if its worth the @@ -568,13 +570,6 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, status = chip->waitfunc(mtd, chip); } -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - if (chip->verify_buf(mtd, buf, mtd->writesize)) - return -EIO; -#endif return 0; } @@ -584,7 +579,7 @@ static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) } /* F_2[X]/(X**6+X+1) */ -static unsigned short __devinit gf64_mul(u8 a, u8 b) +static unsigned short gf64_mul(u8 a, u8 b) { u8 c; unsigned int i; @@ -603,7 +598,7 @@ static unsigned short __devinit gf64_mul(u8 a, u8 b) } /* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ -static u16 __devinit gf4096_mul(u16 a, u16 b) +static u16 gf4096_mul(u16 a, u16 b) { u8 ah, al, bh, bl, ch, cl; @@ -618,24 +613,22 @@ static u16 __devinit gf4096_mul(u16 a, u16 b) return (ch << 6) ^ cl; } -static int __devinit cafe_mul(int x) +static int cafe_mul(int x) { if (x == 0) return 1; return gf4096_mul(x, 0xe01); } -static int __devinit cafe_nand_probe(struct pci_dev *pdev, +static int cafe_nand_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct mtd_info *mtd; struct cafe_priv *cafe; uint32_t ctrl; int err = 0; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *parts; - int nr_parts; -#endif + int old_dma; + struct nand_buffers *nbuf; /* Very old versions shared the same PCI ident for all three functions on the chip. Verify the class too... */ @@ -649,10 +642,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, pci_set_master(pdev); mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); - if (!mtd) { - dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); + if (!mtd) return -ENOMEM; - } cafe = (void *)(&mtd[1]); mtd->dev.parent = &pdev->dev; @@ -666,13 +657,6 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, err = -ENOMEM; goto out_free_mtd; } - cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), - &cafe->dmaaddr, GFP_KERNEL); - if (!cafe->dmabuf) { - err = -ENOMEM; - goto out_ior; - } - cafe->nand.buffers = (void *)cafe->dmabuf + 2112; cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); if (!cafe->rs) { @@ -690,7 +674,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.chip_delay = 0; /* Enable the following for a flash based bad block table */ - cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + cafe->nand.bbt_options = NAND_BBT_USE_FLASH; + cafe->nand.options = NAND_OWN_BUFFERS; if (skipbbt) { cafe->nand.options |= NAND_SKIP_BBTSCAN; @@ -731,7 +716,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - goto out_free_dma; + goto out_ior; } /* Disable master reset, enable NAND clock */ @@ -745,6 +730,32 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_writel(cafe, 0x7006, GLOBAL_CTRL); cafe_writel(cafe, 0x700a, GLOBAL_CTRL); + /* Enable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); + cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + cafe_readl(cafe, GLOBAL_CTRL), + cafe_readl(cafe, GLOBAL_IRQ_MASK)); + + /* Do not use the DMA for the nand_scan_ident() */ + old_dma = usedma; + usedma = 0; + + /* Scan to find existence of the device */ + if (nand_scan_ident(mtd, 2, NULL)) { + err = -ENXIO; + goto out_irq; + } + + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, + 2112 + sizeof(struct nand_buffers) + + mtd->writesize + mtd->oobsize, + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) { + err = -ENOMEM; + goto out_irq; + } + cafe->nand.buffers = nbuf = (void *)cafe->dmabuf + 2112; + /* Set up DMA address */ cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); if (sizeof(cafe->dmaaddr) > 4) @@ -756,16 +767,13 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); - /* Enable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); - cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", - cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); + /* this driver does not need the @ecccalc and @ecccode */ + nbuf->ecccalc = NULL; + nbuf->ecccode = NULL; + nbuf->databuf = (uint8_t *)(nbuf + 1); - /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 2, NULL)) { - err = -ENXIO; - goto out_irq; - } + /* Restore the DMA flag */ + usedma = old_dma; cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ if (mtd->writesize == 2048) @@ -783,11 +791,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, } else { printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", mtd->writesize); - goto out_irq; + goto out_free_dma; } cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.strength = 4; cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; cafe->nand.ecc.calculate = (void *)cafe_nand_bug; cafe->nand.ecc.correct = (void *)cafe_nand_bug; @@ -799,32 +808,24 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, err = nand_scan_tail(mtd); if (err) - goto out_irq; + goto out_free_dma; pci_set_drvdata(pdev, mtd); - /* We register the whole device first, separate from the partitions */ - add_mtd_device(mtd); - -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS mtd->name = "cafe_nand"; -#endif - nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0); - if (nr_parts > 0) { - cafe->parts = parts; - dev_info(&cafe->pdev->dev, "%d partitions found\n", nr_parts); - add_mtd_partitions(mtd, parts, nr_parts); - } -#endif + mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + goto out; + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, + 2112 + sizeof(struct nand_buffers) + + mtd->writesize + mtd->oobsize, + cafe->dmabuf, cafe->dmaaddr); out_irq: /* Disable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); - out_free_dma: - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); out_ior: pci_iounmap(pdev, cafe->mmio); out_free_mtd: @@ -833,19 +834,21 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, return err; } -static void __devexit cafe_nand_remove(struct pci_dev *pdev) +static void cafe_nand_remove(struct pci_dev *pdev) { struct mtd_info *mtd = pci_get_drvdata(pdev); struct cafe_priv *cafe = mtd->priv; - del_mtd_device(mtd); /* Disable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); nand_release(mtd); free_rs(cafe->rs); pci_iounmap(pdev, cafe->mmio); - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + dma_free_coherent(&cafe->pdev->dev, + 2112 + sizeof(struct nand_buffers) + + mtd->writesize + mtd->oobsize, + cafe->dmabuf, cafe->dmaaddr); kfree(mtd); } @@ -900,21 +903,11 @@ static struct pci_driver cafe_nand_pci_driver = { .name = "CAFÉ NAND", .id_table = cafe_nand_tbl, .probe = cafe_nand_probe, - .remove = __devexit_p(cafe_nand_remove), + .remove = cafe_nand_remove, .resume = cafe_nand_resume, }; -static int __init cafe_nand_init(void) -{ - return pci_register_driver(&cafe_nand_pci_driver); -} - -static void __exit cafe_nand_exit(void) -{ - pci_unregister_driver(&cafe_nand_pci_driver); -} -module_init(cafe_nand_init); -module_exit(cafe_nand_exit); +module_pci_driver(cafe_nand_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 6e649527825..66ec95e6ca6 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -22,6 +22,7 @@ #include <linux/mtd/partitions.h> #include <linux/slab.h> #include <linux/gpio.h> +#include <linux/module.h> #include <asm/io.h> #include <asm/irq.h> @@ -50,8 +51,6 @@ static struct mtd_partition partition_info[] = { }; #define NUM_PARTITIONS (ARRAY_SIZE(partition_info)) -const char *part_probes[] = { "cmdlinepart", NULL }; - static u_char cmx270_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; @@ -77,18 +76,6 @@ static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len) *buf++ = readl(this->IO_ADDR_R) >> 16; } -static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i=0; i<len; i++) - if (buf[i] != (u_char)(readl(this->IO_ADDR_R) >> 16)) - return -EFAULT; - - return 0; -} - static inline void nand_cs_on(void) { gpio_set_value(GPIO_NAND_CS, 0); @@ -151,9 +138,6 @@ static int cmx270_device_ready(struct mtd_info *mtd) static int __init cmx270_init(void) { struct nand_chip *this; - const char *part_type; - struct mtd_partition *mtd_parts; - int mtd_parts_nb = 0; int ret; if (!(machine_is_armcore() && cpu_is_pxa27x())) @@ -180,7 +164,6 @@ static int __init cmx270_init(void) sizeof(struct nand_chip), GFP_KERNEL); if (!cmx270_nand_mtd) { - pr_debug("Unable to allocate CM-X270 NAND MTD device structure.\n"); ret = -ENOMEM; goto err_kzalloc; } @@ -213,7 +196,6 @@ static int __init cmx270_init(void) this->read_byte = cmx270_read_byte; this->read_buf = cmx270_read_buf; this->write_buf = cmx270_write_buf; - this->verify_buf = cmx270_verify_buf; /* Scan to find existence of the device */ if (nand_scan (cmx270_nand_mtd, 1)) { @@ -222,23 +204,9 @@ static int __init cmx270_init(void) goto err_scan; } -#ifdef CONFIG_MTD_CMDLINE_PARTS - mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes, - &mtd_parts, 0); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (!mtd_parts_nb) { - mtd_parts = partition_info; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - /* Register the partitions */ - pr_notice("Using %s partition definition\n", part_type); - ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb); + ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL, + partition_info, NUM_PARTITIONS); if (ret) goto err_scan; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 9f1b451005c..88109d375ae 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -197,9 +197,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) } /* Allocate memory for MTD device structure and private data */ - new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + new_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!new_mtd) { - printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n"); err = -ENOMEM; goto out; } @@ -207,10 +206,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) /* Get pointer to private data */ this = (struct nand_chip *)(&new_mtd[1]); - /* Initialize structures */ - memset(new_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ new_mtd->priv = this; new_mtd->owner = THIS_MODULE; @@ -237,11 +232,12 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) this->ecc.hwctl = cs_enable_hwecc; this->ecc.calculate = cs_calculate_ecc; this->ecc.correct = nand_correct_data; + this->ecc.strength = 1; /* Enable the following for a flash based bad block table */ - this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR; + this->bbt_options = NAND_BBT_USE_FLASH; - /* Scan to find existance of the device */ + /* Scan to find existence of the device */ if (nand_scan(new_mtd, 1)) { err = -ENXIO; goto out_ior; @@ -277,23 +273,12 @@ static int is_geode(void) return 0; } - -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - - static int __init cs553x_init(void) { int err = -ENXIO; int i; uint64_t val; -#ifdef CONFIG_MTD_PARTITIONS - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = NULL; -#endif - /* If the CPU isn't a Geode GX or LX, abort */ if (!is_geode()) return -ENXIO; @@ -322,19 +307,9 @@ static int __init cs553x_init(void) do mtdconcat etc. if we want to. */ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { if (cs553x_mtd[i]) { - /* If any devices registered, return success. Else the last error. */ -#ifdef CONFIG_MTD_PARTITIONS - mtd_parts_nb = parse_mtd_partitions(cs553x_mtd[i], part_probes, &mtd_parts, 0); - if (mtd_parts_nb > 0) { - printk(KERN_NOTICE "Using command line partition definition\n"); - add_mtd_partitions(cs553x_mtd[i], mtd_parts, mtd_parts_nb); - } else { - add_mtd_device(cs553x_mtd[i]); - } -#else - add_mtd_device(cs553x_mtd[i]); -#endif + mtd_device_parse_register(cs553x_mtd[i], NULL, NULL, + NULL, 0); err = 0; } } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index a90fde3ede2..b922c8efcf4 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -24,7 +24,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/err.h> @@ -33,12 +32,12 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <linux/slab.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/of_mtd.h> -#include <mach/nand.h> -#include <mach/aemif.h> - -#include <asm/mach-types.h> - +#include <linux/platform_data/mtd-davinci.h> +#include <linux/platform_data/mtd-davinci-aemif.h> /* * This is a device driver for the NAND flash controller found on the @@ -60,7 +59,6 @@ struct davinci_nand_info { struct device *dev; struct clk *clk; - bool partitioned; bool is_readmode; @@ -489,7 +487,7 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd) * ten ECC bytes plus the manufacturer's bad block marker byte, and * and not overlapping the default BBT markers. */ -static struct nand_ecclayout hwecc4_small __initconst = { +static struct nand_ecclayout hwecc4_small = { .eccbytes = 10, .eccpos = { 0, 1, 2, 3, 4, /* offset 5 holds the badblock marker */ @@ -505,7 +503,7 @@ static struct nand_ecclayout hwecc4_small __initconst = { * storing ten ECC bytes plus the manufacturer's bad block marker byte, * and not overlapping the default BBT markers. */ -static struct nand_ecclayout hwecc4_2048 __initconst = { +static struct nand_ecclayout hwecc4_2048 = { .eccbytes = 40, .eccpos = { /* at the end of spare sector */ @@ -522,9 +520,88 @@ static struct nand_ecclayout hwecc4_2048 __initconst = { }, }; -static int __init nand_davinci_probe(struct platform_device *pdev) +#if defined(CONFIG_OF) +static const struct of_device_id davinci_nand_of_match[] = { + {.compatible = "ti,davinci-nand", }, + {.compatible = "ti,keystone-nand", }, + {}, +}; +MODULE_DEVICE_TABLE(of, davinci_nand_of_match); + +static struct davinci_nand_pdata + *nand_davinci_get_pdata(struct platform_device *pdev) +{ + if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { + struct davinci_nand_pdata *pdata; + const char *mode; + u32 prop; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct davinci_nand_pdata), + GFP_KERNEL); + pdev->dev.platform_data = pdata; + if (!pdata) + return ERR_PTR(-ENOMEM); + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-chipselect", &prop)) + pdev->id = prop; + else + return ERR_PTR(-EINVAL); + + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-mask-ale", &prop)) + pdata->mask_ale = prop; + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-mask-cle", &prop)) + pdata->mask_cle = prop; + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-mask-chipsel", &prop)) + pdata->mask_chipsel = prop; + if (!of_property_read_string(pdev->dev.of_node, + "nand-ecc-mode", &mode) || + !of_property_read_string(pdev->dev.of_node, + "ti,davinci-ecc-mode", &mode)) { + if (!strncmp("none", mode, 4)) + pdata->ecc_mode = NAND_ECC_NONE; + if (!strncmp("soft", mode, 4)) + pdata->ecc_mode = NAND_ECC_SOFT; + if (!strncmp("hw", mode, 2)) + pdata->ecc_mode = NAND_ECC_HW; + } + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-ecc-bits", &prop)) + pdata->ecc_bits = prop; + + prop = of_get_nand_bus_width(pdev->dev.of_node); + if (0 < prop || !of_property_read_u32(pdev->dev.of_node, + "ti,davinci-nand-buswidth", &prop)) + if (prop == 16) + pdata->options |= NAND_BUSWIDTH_16; + if (of_property_read_bool(pdev->dev.of_node, + "nand-on-flash-bbt") || + of_property_read_bool(pdev->dev.of_node, + "ti,davinci-nand-use-bbt")) + pdata->bbt_options = NAND_BBT_USE_FLASH; + + if (of_device_is_compatible(pdev->dev.of_node, + "ti,keystone-nand")) { + pdata->options |= NAND_NO_SUBPAGE_WRITE; + } + } + + return dev_get_platdata(&pdev->dev); +} +#else +static struct davinci_nand_pdata + *nand_davinci_get_pdata(struct platform_device *pdev) +{ + return dev_get_platdata(&pdev->dev); +} +#endif + +static int nand_davinci_probe(struct platform_device *pdev) { - struct davinci_nand_pdata *pdata = pdev->dev.platform_data; + struct davinci_nand_pdata *pdata; struct davinci_nand_info *info; struct resource *res1; struct resource *res2; @@ -534,6 +611,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev) uint32_t val; nand_ecc_modes_t ecc_mode; + pdata = nand_davinci_get_pdata(pdev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + /* insist on board-specific configuration */ if (!pdata) return -ENODEV; @@ -542,12 +623,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev) if (pdev->id < 0 || pdev->id > 3) return -ENODEV; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - dev_err(&pdev->dev, "unable to allocate memory\n"); - ret = -ENOMEM; - goto err_nomem; - } + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; platform_set_drvdata(pdev, info); @@ -555,16 +633,23 @@ static int __init nand_davinci_probe(struct platform_device *pdev) res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res1 || !res2) { dev_err(&pdev->dev, "resource missing\n"); - ret = -EINVAL; - goto err_nomem; + return -EINVAL; } - vaddr = ioremap(res1->start, resource_size(res1)); - base = ioremap(res2->start, resource_size(res2)); - if (!vaddr || !base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EINVAL; - goto err_ioremap; + vaddr = devm_ioremap_resource(&pdev->dev, res1); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + + /* + * This registers range is used to setup NAND settings. In case with + * TI AEMIF driver, the same memory address range is requested already + * by AEMIF, so we cannot request it twice, just ioremap. + * The AEMIF and NAND drivers not use the same registers in this range. + */ + base = devm_ioremap(&pdev->dev, res2->start, resource_size(res2)); + if (!base) { + dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res2); + return -EADDRNOTAVAIL; } info->dev = &pdev->dev; @@ -582,7 +667,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.chip_delay = 0; info->chip.select_chip = nand_davinci_select_chip; - /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ + /* options such as NAND_BBT_USE_FLASH */ + info->chip.bbt_options = pdata->bbt_options; + /* options such as 16-bit widths */ info->chip.options = pdata->options; info->chip.bbt_td = pdata->bbt_td; info->chip.bbt_md = pdata->bbt_md; @@ -630,7 +717,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); if (ret == -EBUSY) - goto err_ecc; + return ret; info->chip.ecc.calculate = nand_davinci_calculate_4bit; info->chip.ecc.correct = nand_davinci_correct_4bit; @@ -643,47 +730,27 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.ecc.bytes = 3; } info->chip.ecc.size = 512; + info->chip.ecc.strength = pdata->ecc_bits; break; default: - ret = -EINVAL; - goto err_ecc; + return -EINVAL; } info->chip.ecc.mode = ecc_mode; - info->clk = clk_get(&pdev->dev, "aemif"); + info->clk = devm_clk_get(&pdev->dev, "aemif"); if (IS_ERR(info->clk)) { ret = PTR_ERR(info->clk); dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); - goto err_clk; + return ret; } - ret = clk_enable(info->clk); + ret = clk_prepare_enable(info->clk); if (ret < 0) { dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", ret); goto err_clk_enable; } - /* - * Setup Async configuration register in case we did not boot from - * NAND and so bootloader did not bother to set it up. - */ - val = davinci_nand_readl(info, A1CR_OFFSET + info->core_chipsel * 4); - - /* Extended Wait is not valid and Select Strobe mode is not used */ - val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK); - if (info->chip.options & NAND_BUSWIDTH_16) - val |= 0x1; - - davinci_nand_writel(info, A1CR_OFFSET + info->core_chipsel * 4, val); - - ret = davinci_aemif_setup_timing(info->timing, info->base, - info->core_chipsel); - if (ret < 0) { - dev_dbg(&pdev->dev, "NAND timing values setup fail\n"); - goto err_timing; - } - spin_lock_irq(&davinci_nand_lock); /* put CSxNAND into NAND mode */ @@ -697,7 +764,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); - goto err_scan; + goto err; } /* Update ECC layout if needed ... for 1-bit HW ECC, the default @@ -711,7 +778,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) if (!chunks || info->mtd.oobsize < 16) { dev_dbg(&pdev->dev, "too small\n"); ret = -EINVAL; - goto err_scan; + goto err; } /* For small page chips, preserve the manufacturer's @@ -742,7 +809,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "no 4-bit ECC support yet " "for 4KiB-page NAND\n"); ret = -EIO; - goto err_scan; + goto err; syndrome_done: info->chip.ecc.layout = &info->ecclayout; @@ -750,46 +817,20 @@ syndrome_done: ret = nand_scan_tail(&info->mtd); if (ret < 0) - goto err_scan; - - if (mtd_has_partitions()) { - struct mtd_partition *mtd_parts = NULL; - int mtd_parts_nb = 0; + goto err; - if (mtd_has_cmdlinepart()) { - static const char *probes[] __initconst = - { "cmdlinepart", NULL }; + if (pdata->parts) + ret = mtd_device_parse_register(&info->mtd, NULL, NULL, + pdata->parts, pdata->nr_parts); + else { + struct mtd_part_parser_data ppdata; - mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, - &mtd_parts, 0); - } - - if (mtd_parts_nb <= 0) { - mtd_parts = pdata->parts; - mtd_parts_nb = pdata->nr_parts; - } - - /* Register any partitions */ - if (mtd_parts_nb > 0) { - ret = add_mtd_partitions(&info->mtd, - mtd_parts, mtd_parts_nb); - if (ret == 0) - info->partitioned = true; - } - - } else if (pdata->nr_parts) { - dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", - pdata->nr_parts, info->mtd.name); + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(&info->mtd, NULL, &ppdata, + NULL, 0); } - - /* If there's no partition info, just package the whole chip - * as a single MTD device. - */ - if (!info->partitioned) - ret = add_mtd_device(&info->mtd) ? -ENODEV : 0; - if (ret < 0) - goto err_scan; + goto err; val = davinci_nand_readl(info, NRCSR_OFFSET); dev_info(&pdev->dev, "controller rev. %d.%d\n", @@ -797,78 +838,45 @@ syndrome_done: return 0; -err_scan: -err_timing: - clk_disable(info->clk); +err: + clk_disable_unprepare(info->clk); err_clk_enable: - clk_put(info->clk); - spin_lock_irq(&davinci_nand_lock); if (ecc_mode == NAND_ECC_HW_SYNDROME) ecc4_busy = false; spin_unlock_irq(&davinci_nand_lock); - -err_ecc: -err_clk: -err_ioremap: - if (base) - iounmap(base); - if (vaddr) - iounmap(vaddr); - -err_nomem: - kfree(info); return ret; } -static int __exit nand_davinci_remove(struct platform_device *pdev) +static int nand_davinci_remove(struct platform_device *pdev) { struct davinci_nand_info *info = platform_get_drvdata(pdev); - int status; - - if (mtd_has_partitions() && info->partitioned) - status = del_mtd_partitions(&info->mtd); - else - status = del_mtd_device(&info->mtd); spin_lock_irq(&davinci_nand_lock); if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) ecc4_busy = false; spin_unlock_irq(&davinci_nand_lock); - iounmap(info->base); - iounmap(info->vaddr); - nand_release(&info->mtd); - clk_disable(info->clk); - clk_put(info->clk); - - kfree(info); + clk_disable_unprepare(info->clk); return 0; } static struct platform_driver nand_davinci_driver = { - .remove = __exit_p(nand_davinci_remove), + .probe = nand_davinci_probe, + .remove = nand_davinci_remove, .driver = { .name = "davinci_nand", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(davinci_nand_of_match), }, }; MODULE_ALIAS("platform:davinci_nand"); -static int __init nand_davinci_init(void) -{ - return platform_driver_probe(&nand_davinci_driver, nand_davinci_probe); -} -module_init(nand_davinci_init); - -static void __exit nand_davinci_exit(void) -{ - platform_driver_unregister(&nand_davinci_driver); -} -module_exit(nand_davinci_exit); +module_platform_driver(nand_davinci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 8c8d3c86c0e..9f2012a3e76 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -16,13 +16,12 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ - #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/wait.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/pci.h> #include <linux/mtd/mtd.h> #include <linux/module.h> @@ -44,16 +43,16 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting." /* We define a macro here that combines all interrupts this driver uses into * a single constant value, for convenience. */ -#define DENALI_IRQ_ALL (INTR_STATUS0__DMA_CMD_COMP | \ - INTR_STATUS0__ECC_TRANSACTION_DONE | \ - INTR_STATUS0__ECC_ERR | \ - INTR_STATUS0__PROGRAM_FAIL | \ - INTR_STATUS0__LOAD_COMP | \ - INTR_STATUS0__PROGRAM_COMP | \ - INTR_STATUS0__TIME_OUT | \ - INTR_STATUS0__ERASE_FAIL | \ - INTR_STATUS0__RST_COMP | \ - INTR_STATUS0__ERASE_COMP) +#define DENALI_IRQ_ALL (INTR_STATUS__DMA_CMD_COMP | \ + INTR_STATUS__ECC_TRANSACTION_DONE | \ + INTR_STATUS__ECC_ERR | \ + INTR_STATUS__PROGRAM_FAIL | \ + INTR_STATUS__LOAD_COMP | \ + INTR_STATUS__PROGRAM_COMP | \ + INTR_STATUS__TIME_OUT | \ + INTR_STATUS__ERASE_FAIL | \ + INTR_STATUS__RST_COMP | \ + INTR_STATUS__ERASE_COMP) /* indicates whether or not the internal value for the flash bank is * valid or not */ @@ -88,37 +87,6 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting." * format the bank into the proper bits for the controller */ #define BANK(x) ((x) << 24) -/* List of platforms this NAND controller has be integrated into */ -static const struct pci_device_id denali_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, - { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, - { /* end: all zeroes */ } -}; - - -/* these are static lookup tables that give us easy access to - * registers in the NAND controller. - */ -static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, - INTR_STATUS1, - INTR_STATUS2, - INTR_STATUS3}; - -static const uint32_t device_reset_banks[4] = {DEVICE_RESET__BANK0, - DEVICE_RESET__BANK1, - DEVICE_RESET__BANK2, - DEVICE_RESET__BANK3}; - -static const uint32_t operation_timeout[4] = {INTR_STATUS0__TIME_OUT, - INTR_STATUS1__TIME_OUT, - INTR_STATUS2__TIME_OUT, - INTR_STATUS3__TIME_OUT}; - -static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP, - INTR_STATUS1__RST_COMP, - INTR_STATUS2__RST_COMP, - INTR_STATUS3__RST_COMP}; - /* forward declarations */ static void clear_interrupts(struct denali_nand_info *denali); static uint32_t wait_for_irq(struct denali_nand_info *denali, @@ -157,7 +125,6 @@ static void reset_buf(struct denali_nand_info *denali) static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte) { - BUG_ON(denali->buf.tail >= sizeof(denali->buf.buf)); denali->buf.buf[denali->buf.tail++] = byte; } @@ -180,19 +147,17 @@ static void read_status(struct denali_nand_info *denali) static void reset_bank(struct denali_nand_info *denali) { uint32_t irq_status = 0; - uint32_t irq_mask = reset_complete[denali->flash_bank] | - operation_timeout[denali->flash_bank]; - int bank = 0; + uint32_t irq_mask = INTR_STATUS__RST_COMP | + INTR_STATUS__TIME_OUT; clear_interrupts(denali); - bank = device_reset_banks[denali->flash_bank]; - iowrite32(bank, denali->flash_reg + DEVICE_RESET); + iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET); irq_status = wait_for_irq(denali, irq_mask); - if (irq_status & operation_timeout[denali->flash_bank]) - dev_err(&denali->dev->dev, "reset bank failed.\n"); + if (irq_status & INTR_STATUS__TIME_OUT) + dev_err(denali->dev, "reset bank failed.\n"); } /* Reset the flash controller */ @@ -200,29 +165,28 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali) { uint32_t i; - dev_dbg(&denali->dev->dev, "%s, Line %d, Function: %s\n", + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); - for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) - iowrite32(reset_complete[i] | operation_timeout[i], - denali->flash_reg + intr_status_addresses[i]); + for (i = 0 ; i < denali->max_banks; i++) + iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT, + denali->flash_reg + INTR_STATUS(i)); - for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { - iowrite32(device_reset_banks[i], - denali->flash_reg + DEVICE_RESET); + for (i = 0 ; i < denali->max_banks; i++) { + iowrite32(1 << i, denali->flash_reg + DEVICE_RESET); while (!(ioread32(denali->flash_reg + - intr_status_addresses[i]) & - (reset_complete[i] | operation_timeout[i]))) + INTR_STATUS(i)) & + (INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT))) cpu_relax(); - if (ioread32(denali->flash_reg + intr_status_addresses[i]) & - operation_timeout[i]) - dev_dbg(&denali->dev->dev, + if (ioread32(denali->flash_reg + INTR_STATUS(i)) & + INTR_STATUS__TIME_OUT) + dev_dbg(denali->dev, "NAND Reset operation timed out on bank %d\n", i); } - for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) - iowrite32(reset_complete[i] | operation_timeout[i], - denali->flash_reg + intr_status_addresses[i]); + for (i = 0; i < denali->max_banks; i++) + iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT, + denali->flash_reg + INTR_STATUS(i)); return PASS; } @@ -254,7 +218,7 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali, uint16_t acc_clks; uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; - dev_dbg(&denali->dev->dev, "%s, Line %d, Function: %s\n", + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); en_lo = CEIL_DIV(Trp[mode], CLK_X); @@ -291,7 +255,7 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali, acc_clks++; if ((data_invalid - acc_clks * CLK_X) < 2) - dev_warn(&denali->dev->dev, "%s, Line %d: Warning!\n", + dev_warn(denali->dev, "%s, Line %d: Warning!\n", __FILE__, __LINE__); addr_2_data = CEIL_DIV(Tadl[mode], CLK_X); @@ -419,7 +383,7 @@ static void get_hynix_nand_para(struct denali_nand_info *denali, #endif break; default: - dev_warn(&denali->dev->dev, + dev_warn(denali->dev, "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." "Will use default parameter values instead.\n", device_id); @@ -431,17 +395,17 @@ static void get_hynix_nand_para(struct denali_nand_info *denali, */ static void find_valid_banks(struct denali_nand_info *denali) { - uint32_t id[LLD_MAX_FLASH_BANKS]; + uint32_t id[denali->max_banks]; int i; denali->total_used_banks = 1; - for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { + for (i = 0; i < denali->max_banks; i++) { index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90); index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0); index_addr_read_data(denali, (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); - dev_dbg(&denali->dev->dev, + dev_dbg(denali->dev, "Return 1st ID for bank[%d]: %x\n", i, id[i]); if (i == 0) { @@ -461,16 +425,27 @@ static void find_valid_banks(struct denali_nand_info *denali) * Multichip support is not enabled. */ if (denali->total_used_banks != 1) { - dev_err(&denali->dev->dev, + dev_err(denali->dev, "Sorry, Intel CE4100 only supports " "a single NAND device.\n"); BUG(); } } - dev_dbg(&denali->dev->dev, + dev_dbg(denali->dev, "denali->total_used_banks: %d\n", denali->total_used_banks); } +/* + * Use the configuration feature register to determine the maximum number of + * banks that the hardware supports. + */ +static void detect_max_banks(struct denali_nand_info *denali) +{ + uint32_t features = ioread32(denali->flash_reg + FEATURES); + + denali->max_banks = 2 << (features & FEATURES__N_BANKS); +} + static void detect_partition_feature(struct denali_nand_info *denali) { /* For MRST platform, denali->fwblks represent the @@ -480,15 +455,15 @@ static void detect_partition_feature(struct denali_nand_info *denali) * blocks it can't touch. * */ if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { - if ((ioread32(denali->flash_reg + PERM_SRC_ID_1) & - PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { + if ((ioread32(denali->flash_reg + PERM_SRC_ID(1)) & + PERM_SRC_ID__SRCID) == SPECTRA_PARTITION_ID) { denali->fwblks = - ((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & - MIN_MAX_BANK_1__MIN_VALUE) * + ((ioread32(denali->flash_reg + MIN_MAX_BANK(1)) & + MIN_MAX_BANK__MIN_VALUE) * denali->blksperchip) + - (ioread32(denali->flash_reg + MIN_BLK_ADDR_1) & - MIN_BLK_ADDR_1__VALUE); + (ioread32(denali->flash_reg + MIN_BLK_ADDR(1)) & + MIN_BLK_ADDR__VALUE); } else denali->fwblks = SPECTRA_START_BLOCK; } else @@ -501,7 +476,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) uint32_t id_bytes[5], addr; uint8_t i, maf_id, device_id; - dev_dbg(&denali->dev->dev, + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); @@ -530,7 +505,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) get_hynix_nand_para(denali, device_id); } - dev_info(&denali->dev->dev, + dev_info(denali->dev, "Dump timing register values:" "acc_clks: %d, re_2_we: %d, re_2_re: %d\n" "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n" @@ -560,7 +535,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) static void denali_set_intr_modes(struct denali_nand_info *denali, uint16_t INT_ENABLE) { - dev_dbg(&denali->dev->dev, "%s, Line %d, Function: %s\n", + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); if (INT_ENABLE) @@ -580,6 +555,7 @@ static inline bool is_flash_bank_valid(int flash_bank) static void denali_irq_init(struct denali_nand_info *denali) { uint32_t int_mask = 0; + int i; /* Disable global interrupts */ denali_set_intr_modes(denali, false); @@ -587,10 +563,8 @@ static void denali_irq_init(struct denali_nand_info *denali) int_mask = DENALI_IRQ_ALL; /* Clear all status bits */ - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS0); - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS1); - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS2); - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS3); + for (i = 0; i < denali->max_banks; ++i) + iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i)); denali_irq_enable(denali, int_mask); } @@ -604,10 +578,10 @@ static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali) static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask) { - iowrite32(int_mask, denali->flash_reg + INTR_EN0); - iowrite32(int_mask, denali->flash_reg + INTR_EN1); - iowrite32(int_mask, denali->flash_reg + INTR_EN2); - iowrite32(int_mask, denali->flash_reg + INTR_EN3); + int i; + + for (i = 0; i < denali->max_banks; ++i) + iowrite32(int_mask, denali->flash_reg + INTR_EN(i)); } /* This function only returns when an interrupt that this driver cares about @@ -624,7 +598,7 @@ static inline void clear_interrupt(struct denali_nand_info *denali, { uint32_t intr_status_reg = 0; - intr_status_reg = intr_status_addresses[denali->flash_bank]; + intr_status_reg = INTR_STATUS(denali->flash_bank); iowrite32(irq_mask, denali->flash_reg + intr_status_reg); } @@ -645,7 +619,7 @@ static uint32_t read_interrupt_status(struct denali_nand_info *denali) { uint32_t intr_status_reg = 0; - intr_status_reg = intr_status_addresses[denali->flash_bank]; + intr_status_reg = INTR_STATUS(denali->flash_bank); return ioread32(denali->flash_reg + intr_status_reg); } @@ -715,7 +689,7 @@ static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) if (comp_res == 0) { /* timeout */ - printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", + pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n", intr_status, irq_mask); intr_status = 0; @@ -724,7 +698,7 @@ static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) } /* This helper function setups the registers for ECC and whether or not - * the spare area will be transfered. */ + * the spare area will be transferred. */ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, bool transfer_spare) { @@ -754,7 +728,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, irq_mask = 0; if (op == DENALI_READ) - irq_mask = INTR_STATUS0__LOAD_COMP; + irq_mask = INTR_STATUS__LOAD_COMP; else if (op == DENALI_WRITE) irq_mask = 0; else @@ -800,7 +774,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) { - dev_err(&denali->dev->dev, + dev_err(denali->dev, "cmd, page, addr on timeout " "(0x%x, 0x%x, 0x%x)\n", cmd, denali->page, addr); @@ -861,8 +835,8 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__PROGRAM_COMP | - INTR_STATUS0__PROGRAM_FAIL; + uint32_t irq_mask = INTR_STATUS__PROGRAM_COMP | + INTR_STATUS__PROGRAM_FAIL; int status = 0; denali->page = page; @@ -875,11 +849,11 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) { - dev_err(&denali->dev->dev, "OOB write failed\n"); + dev_err(denali->dev, "OOB write failed\n"); status = -EIO; } } else { - dev_err(&denali->dev->dev, "unable to send pipeline command\n"); + dev_err(denali->dev, "unable to send pipeline command\n"); status = -EIO; } return status; @@ -889,7 +863,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t irq_mask = INTR_STATUS0__LOAD_COMP, + uint32_t irq_mask = INTR_STATUS__LOAD_COMP, irq_status = 0, addr = 0x0, cmd = 0x0; denali->page = page; @@ -904,7 +878,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) - dev_err(&denali->dev->dev, "page on OOB timeout %d\n", + dev_err(denali->dev, "page on OOB timeout %d\n", denali->page); /* We set the device back to MAIN_ACCESS here as I observed @@ -922,7 +896,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) /* this function examines buffers to see if they contain data that * indicate that the buffer is part of an erased region of flash. */ -bool is_erased(uint8_t *buf, int len) +static bool is_erased(uint8_t *buf, int len) { int i = 0; for (i = 0; i < len; i++) @@ -940,11 +914,12 @@ bool is_erased(uint8_t *buf, int len) #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, - uint32_t irq_status) + uint32_t irq_status, unsigned int *max_bitflips) { bool check_erased_page = false; + unsigned int bitflips = 0; - if (irq_status & INTR_STATUS0__ECC_ERR) { + if (irq_status & INTR_STATUS__ECC_ERR) { /* read the ECC errors. we'll ignore them for now */ uint32_t err_address = 0, err_correction_info = 0; uint32_t err_byte = 0, err_sector = 0, err_device = 0; @@ -965,7 +940,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, if (ECC_ERROR_CORRECTABLE(err_correction_info)) { /* If err_byte is larger than ECC_SECTOR_SIZE, - * means error happend in OOB, so we ignore + * means error happened in OOB, so we ignore * it. It's no need for us to correct it * err_device is represented the NAND error * bits are happened in if there are more @@ -981,6 +956,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, /* correct the ECC error */ buf[offset] ^= err_correction_value; denali->mtd.ecc_stats.corrected++; + bitflips++; } } else { /* if the error is not correctable, need to @@ -995,11 +971,12 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, * for a while for this interrupt * */ while (!(read_interrupt_status(denali) & - INTR_STATUS0__ECC_TRANSACTION_DONE)) + INTR_STATUS__ECC_TRANSACTION_DONE)) cpu_relax(); clear_interrupts(denali); denali_set_intr_modes(denali, true); } + *max_bitflips = bitflips; return check_erased_page; } @@ -1041,18 +1018,17 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op) /* writes a page. user specifies type, and this function handles the * configuration details. */ -static void write_page(struct mtd_info *mtd, struct nand_chip *chip, +static int write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, bool raw_xfer) { struct denali_nand_info *denali = mtd_to_denali(mtd); - struct pci_dev *pci_dev = denali->dev; dma_addr_t addr = denali->buf.dma_buf; size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP | - INTR_STATUS0__PROGRAM_FAIL; + uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP | + INTR_STATUS__PROGRAM_FAIL; /* if it is a raw xfer, we want to disable ecc, and send * the spare area. @@ -1071,7 +1047,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize); } - pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_TODEVICE); + dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE); clear_interrupts(denali); denali_enable_dma(denali, true); @@ -1082,16 +1058,18 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) { - dev_err(&denali->dev->dev, + dev_err(denali->dev, "timeout on write_page (type = %d)\n", raw_xfer); denali->status = - (irq_status & INTR_STATUS0__PROGRAM_FAIL) ? + (irq_status & INTR_STATUS__PROGRAM_FAIL) ? NAND_STATUS_FAIL : PASS; } denali_enable_dma(denali, false); - pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_TODEVICE); + dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE); + + return 0; } /* NAND core entry points */ @@ -1100,24 +1078,24 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, * writing a page with ECC or without is similar, all the work is done * by write_page above. * */ -static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) +static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { /* for regular page writes, we let HW handle all the ECC * data written to the device. */ - write_page(mtd, chip, buf, false); + return write_page(mtd, chip, buf, false); } /* This is the callback that the NAND core calls to write a page without ECC. - * raw access is similiar to ECC page writes, so all the work is done in the + * raw access is similar to ECC page writes, so all the work is done in the * write_page() function above. */ -static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) +static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { /* for raw page writes, we want to disable ECC and simply write whatever data is in the buffer. */ - write_page(mtd, chip, buf, true); + return write_page(mtd, chip, buf, true); } static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, @@ -1127,30 +1105,29 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, } static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { read_oob_data(mtd, chip->oob_poi, page); - return 0; /* notify NAND core to send command to - NAND device. */ + return 0; } static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { + unsigned int max_bitflips; struct denali_nand_info *denali = mtd_to_denali(mtd); - struct pci_dev *pci_dev = denali->dev; dma_addr_t addr = denali->buf.dma_buf; size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__ECC_TRANSACTION_DONE | - INTR_STATUS0__ECC_ERR; + uint32_t irq_mask = INTR_STATUS__ECC_TRANSACTION_DONE | + INTR_STATUS__ECC_ERR; bool check_erased_page = false; if (page != denali->page) { - dev_err(&denali->dev->dev, "IN %s: page %d is not" + dev_err(denali->dev, "IN %s: page %d is not" " equal to denali->page %d, investigate!!", __func__, page, denali->page); BUG(); @@ -1159,7 +1136,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, setup_ecc_for_xfer(denali, true, false); denali_enable_dma(denali, true); - pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE); clear_interrupts(denali); denali_setup_dma(denali, DENALI_READ); @@ -1167,11 +1144,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); - pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE); memcpy(buf, denali->buf.buf, mtd->writesize); - check_erased_page = handle_ecc(denali, buf, irq_status); + check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips); denali_enable_dma(denali, false); if (check_erased_page) { @@ -1185,23 +1162,22 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, denali->mtd.ecc_stats.failed++; } } - return 0; + return max_bitflips; } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); - struct pci_dev *pci_dev = denali->dev; dma_addr_t addr = denali->buf.dma_buf; size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP; + uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP; if (page != denali->page) { - dev_err(&denali->dev->dev, "IN %s: page %d is not" + dev_err(denali->dev, "IN %s: page %d is not" " equal to denali->page %d, investigate!!", __func__, page, denali->page); BUG(); @@ -1210,7 +1186,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, setup_ecc_for_xfer(denali, false, true); denali_enable_dma(denali, true); - pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE); clear_interrupts(denali); denali_setup_dma(denali, DENALI_READ); @@ -1218,7 +1194,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); - pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE); denali_enable_dma(denali, false); @@ -1257,7 +1233,7 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) return status; } -static void denali_erase(struct mtd_info *mtd, int page) +static int denali_erase(struct mtd_info *mtd, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -1271,11 +1247,10 @@ static void denali_erase(struct mtd_info *mtd, int page) index_addr(denali, (uint32_t)cmd, 0x1); /* wait for erase to complete or failure to occur */ - irq_status = wait_for_irq(denali, INTR_STATUS0__ERASE_COMP | - INTR_STATUS0__ERASE_FAIL); + irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP | + INTR_STATUS__ERASE_FAIL); - denali->status = (irq_status & INTR_STATUS0__ERASE_FAIL) ? - NAND_STATUS_FAIL : PASS; + return (irq_status & INTR_STATUS__ERASE_FAIL) ? NAND_STATUS_FAIL : PASS; } static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, @@ -1319,8 +1294,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, /* TODO: Read OOB data */ break; default: - printk(KERN_ERR ": unsupported command" - " received 0x%x\n", cmd); + pr_err(": unsupported command received 0x%x\n", cmd); break; } } @@ -1330,7 +1304,7 @@ static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc_code) { struct denali_nand_info *denali = mtd_to_denali(mtd); - dev_err(&denali->dev->dev, + dev_err(denali->dev, "denali_ecc_calculate called unexpectedly\n"); BUG(); return -EIO; @@ -1340,7 +1314,7 @@ static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, uint8_t *read_ecc, uint8_t *calc_ecc) { struct denali_nand_info *denali = mtd_to_denali(mtd); - dev_err(&denali->dev->dev, + dev_err(denali->dev, "denali_ecc_correct called unexpectedly\n"); BUG(); return -EIO; @@ -1349,7 +1323,7 @@ static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) { struct denali_nand_info *denali = mtd_to_denali(mtd); - dev_err(&denali->dev->dev, + dev_err(denali->dev, "denali_ecc_hwctl called unexpectedly\n"); BUG(); } @@ -1365,6 +1339,7 @@ static void denali_hw_init(struct denali_nand_info *denali) * */ denali->bbtskipbytes = ioread32(denali->flash_reg + SPARE_AREA_SKIP_BYTES); + detect_max_banks(denali); denali_nand_reset(denali); iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED); iowrite32(CHIP_EN_DONT_CARE__FLAG, @@ -1417,7 +1392,7 @@ static struct nand_bbt_descr bbt_mirror_descr = { }; /* initialize driver data structures */ -void denali_drv_init(struct denali_nand_info *denali) +static void denali_drv_init(struct denali_nand_info *denali) { denali->idx = 0; @@ -1438,109 +1413,40 @@ void denali_drv_init(struct denali_nand_info *denali) denali->irq_status = 0; } -/* driver entry point */ -static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +int denali_init(struct denali_nand_info *denali) { - int ret = -ENODEV; - resource_size_t csr_base, mem_base; - unsigned long csr_len, mem_len; - struct denali_nand_info *denali; + int ret; - denali = kzalloc(sizeof(*denali), GFP_KERNEL); - if (!denali) - return -ENOMEM; - - ret = pci_enable_device(dev); - if (ret) { - printk(KERN_ERR "Spectra: pci_enable_device failed.\n"); - goto failed_alloc_memery; - } - - if (id->driver_data == INTEL_CE4100) { + if (denali->platform == INTEL_CE4100) { /* Due to a silicon limitation, we can only support * ONFI timing mode 1 and below. */ if (onfi_timing_mode < -1 || onfi_timing_mode > 1) { - printk(KERN_ERR "Intel CE4100 only supports" - " ONFI timing mode 1 or below\n"); - ret = -EINVAL; - goto failed_enable_dev; + pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n"); + return -EINVAL; } - denali->platform = INTEL_CE4100; - mem_base = pci_resource_start(dev, 0); - mem_len = pci_resource_len(dev, 1); - csr_base = pci_resource_start(dev, 1); - csr_len = pci_resource_len(dev, 1); - } else { - denali->platform = INTEL_MRST; - csr_base = pci_resource_start(dev, 0); - csr_len = pci_resource_len(dev, 0); - mem_base = pci_resource_start(dev, 1); - mem_len = pci_resource_len(dev, 1); - if (!mem_len) { - mem_base = csr_base + csr_len; - mem_len = csr_len; - } - } - - /* Is 32-bit DMA supported? */ - ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); - - if (ret) { - printk(KERN_ERR "Spectra: no usable DMA configuration\n"); - goto failed_enable_dev; - } - denali->buf.dma_buf = - pci_map_single(dev, denali->buf.buf, - DENALI_BUF_SIZE, - PCI_DMA_BIDIRECTIONAL); - - if (pci_dma_mapping_error(dev, denali->buf.dma_buf)) { - dev_err(&dev->dev, "Spectra: failed to map DMA buffer\n"); - goto failed_enable_dev; - } - - pci_set_master(dev); - denali->dev = dev; - denali->mtd.dev.parent = &dev->dev; - - ret = pci_request_regions(dev, DENALI_NAND_NAME); - if (ret) { - printk(KERN_ERR "Spectra: Unable to request memory regions\n"); - goto failed_dma_map; } - denali->flash_reg = ioremap_nocache(csr_base, csr_len); - if (!denali->flash_reg) { - printk(KERN_ERR "Spectra: Unable to remap memory region\n"); - ret = -ENOMEM; - goto failed_req_regions; - } - - denali->flash_mem = ioremap_nocache(mem_base, mem_len); - if (!denali->flash_mem) { - printk(KERN_ERR "Spectra: ioremap_nocache failed!"); - ret = -ENOMEM; - goto failed_remap_reg; - } + /* allocate a temporary buffer for nand_scan_ident() */ + denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE, + GFP_DMA | GFP_KERNEL); + if (!denali->buf.buf) + return -ENOMEM; + denali->mtd.dev.parent = denali->dev; denali_hw_init(denali); denali_drv_init(denali); /* denali_isr register is done after all the hardware * initilization is finished*/ - if (request_irq(dev->irq, denali_isr, IRQF_SHARED, + if (request_irq(denali->irq, denali_isr, IRQF_SHARED, DENALI_NAND_NAME, denali)) { - printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); - ret = -ENODEV; - goto failed_remap_mem; + pr_err("Spectra: Unable to allocate IRQ\n"); + return -ENODEV; } /* now that our ISR is registered, we can enable interrupts */ denali_set_intr_modes(denali, true); - - pci_set_drvdata(dev, denali); - denali->mtd.name = "denali-nand"; denali->mtd.owner = THIS_MODULE; denali->mtd.priv = &denali->nand; @@ -1554,18 +1460,34 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) /* scan for NAND devices attached to the controller * this is the first stage in a two step process to register * with the nand subsystem */ - if (nand_scan_ident(&denali->mtd, LLD_MAX_FLASH_BANKS, NULL)) { + if (nand_scan_ident(&denali->mtd, denali->max_banks, NULL)) { ret = -ENXIO; goto failed_req_irq; } - /* MTD supported page sizes vary by kernel. We validate our - * kernel supports the device here. - */ - if (denali->mtd.writesize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) { - ret = -ENODEV; - printk(KERN_ERR "Spectra: device size not supported by this " - "version of MTD."); + /* allocate the right size buffer now */ + devm_kfree(denali->dev, denali->buf.buf); + denali->buf.buf = devm_kzalloc(denali->dev, + denali->mtd.writesize + denali->mtd.oobsize, + GFP_KERNEL); + if (!denali->buf.buf) { + ret = -ENOMEM; + goto failed_req_irq; + } + + /* Is 32-bit DMA supported? */ + ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32)); + if (ret) { + pr_err("Spectra: no usable DMA configuration\n"); + goto failed_req_irq; + } + + denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf, + denali->mtd.writesize + denali->mtd.oobsize, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) { + dev_err(denali->dev, "Spectra: failed to map DMA buffer\n"); + ret = -EIO; goto failed_req_irq; } @@ -1597,34 +1519,38 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->nand.bbt_md = &bbt_mirror_descr; /* skip the scan for now until we have OOB read and write support */ - denali->nand.options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; + denali->nand.bbt_options |= NAND_BBT_USE_FLASH; + denali->nand.options |= NAND_SKIP_BBTSCAN; denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; /* Denali Controller only support 15bit and 8bit ECC in MRST, * so just let controller do 15bit ECC for MLC and 8bit ECC for * SLC if possible. * */ - if (denali->nand.cellinfo & 0xc && + if (!nand_is_slc(&denali->nand) && (denali->mtd.oobsize > (denali->bbtskipbytes + ECC_15BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE)))) { /* if MLC OOB size is large enough, use 15bit ECC*/ + denali->nand.ecc.strength = 15; denali->nand.ecc.layout = &nand_15bit_oob; denali->nand.ecc.bytes = ECC_15BITS; iowrite32(15, denali->flash_reg + ECC_CORRECTION); } else if (denali->mtd.oobsize < (denali->bbtskipbytes + ECC_8BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE))) { - printk(KERN_ERR "Your NAND chip OOB is not large enough to" - " contain 8bit ECC correction codes"); + pr_err("Your NAND chip OOB is not large enough to \ + contain 8bit ECC correction codes"); goto failed_req_irq; } else { + denali->nand.ecc.strength = 8; denali->nand.ecc.layout = &nand_8bit_oob; denali->nand.ecc.bytes = ECC_8BITS; iowrite32(8, denali->flash_reg + ECC_CORRECTION); } denali->nand.ecc.bytes *= denali->devnum; + denali->nand.ecc.strength *= denali->devnum; denali->nand.ecc.layout->eccbytes *= denali->mtd.writesize / ECC_SECTOR_SIZE; denali->nand.ecc.layout->oobfree[0].offset = @@ -1657,80 +1583,34 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->nand.ecc.write_page_raw = denali_write_page_raw; denali->nand.ecc.read_oob = denali_read_oob; denali->nand.ecc.write_oob = denali_write_oob; - denali->nand.erase_cmd = denali_erase; + denali->nand.erase = denali_erase; if (nand_scan_tail(&denali->mtd)) { ret = -ENXIO; goto failed_req_irq; } - ret = add_mtd_device(&denali->mtd); + ret = mtd_device_register(&denali->mtd, NULL, 0); if (ret) { - dev_err(&dev->dev, "Spectra: Failed to register MTD: %d\n", + dev_err(denali->dev, "Spectra: Failed to register MTD: %d\n", ret); goto failed_req_irq; } return 0; failed_req_irq: - denali_irq_cleanup(dev->irq, denali); -failed_remap_mem: - iounmap(denali->flash_mem); -failed_remap_reg: - iounmap(denali->flash_reg); -failed_req_regions: - pci_release_regions(dev); -failed_dma_map: - pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - PCI_DMA_BIDIRECTIONAL); -failed_enable_dev: - pci_disable_device(dev); -failed_alloc_memery: - kfree(denali); + denali_irq_cleanup(denali->irq, denali); + return ret; } +EXPORT_SYMBOL(denali_init); /* driver exit point */ -static void denali_pci_remove(struct pci_dev *dev) +void denali_remove(struct denali_nand_info *denali) { - struct denali_nand_info *denali = pci_get_drvdata(dev); - - nand_release(&denali->mtd); - del_mtd_device(&denali->mtd); - - denali_irq_cleanup(dev->irq, denali); - - iounmap(denali->flash_reg); - iounmap(denali->flash_mem); - pci_release_regions(dev); - pci_disable_device(dev); - pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - PCI_DMA_BIDIRECTIONAL); - pci_set_drvdata(dev, NULL); - kfree(denali); + denali_irq_cleanup(denali->irq, denali); + dma_unmap_single(denali->dev, denali->buf.dma_buf, + denali->mtd.writesize + denali->mtd.oobsize, + DMA_BIDIRECTIONAL); } - -MODULE_DEVICE_TABLE(pci, denali_pci_ids); - -static struct pci_driver denali_pci_driver = { - .name = DENALI_NAND_NAME, - .id_table = denali_pci_ids, - .probe = denali_pci_probe, - .remove = denali_pci_remove, -}; - -static int __devinit denali_init(void) -{ - printk(KERN_INFO "Spectra MTD driver built on %s @ %s\n", - __DATE__, __TIME__); - return pci_register_driver(&denali_pci_driver); -} - -/* Free memory */ -static void __devexit denali_exit(void) -{ - pci_unregister_driver(&denali_pci_driver); -} - -module_init(denali_init); -module_exit(denali_exit); +EXPORT_SYMBOL(denali_remove); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 3918bcb1561..96681746242 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -211,185 +211,46 @@ #define TRANSFER_MODE 0x400 #define TRANSFER_MODE__VALUE 0x0003 -#define INTR_STATUS0 0x410 -#define INTR_STATUS0__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS0__ECC_ERR 0x0002 -#define INTR_STATUS0__DMA_CMD_COMP 0x0004 -#define INTR_STATUS0__TIME_OUT 0x0008 -#define INTR_STATUS0__PROGRAM_FAIL 0x0010 -#define INTR_STATUS0__ERASE_FAIL 0x0020 -#define INTR_STATUS0__LOAD_COMP 0x0040 -#define INTR_STATUS0__PROGRAM_COMP 0x0080 -#define INTR_STATUS0__ERASE_COMP 0x0100 -#define INTR_STATUS0__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS0__LOCKED_BLK 0x0400 -#define INTR_STATUS0__UNSUP_CMD 0x0800 -#define INTR_STATUS0__INT_ACT 0x1000 -#define INTR_STATUS0__RST_COMP 0x2000 -#define INTR_STATUS0__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS0__PAGE_XFER_INC 0x8000 - -#define INTR_EN0 0x420 -#define INTR_EN0__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN0__ECC_ERR 0x0002 -#define INTR_EN0__DMA_CMD_COMP 0x0004 -#define INTR_EN0__TIME_OUT 0x0008 -#define INTR_EN0__PROGRAM_FAIL 0x0010 -#define INTR_EN0__ERASE_FAIL 0x0020 -#define INTR_EN0__LOAD_COMP 0x0040 -#define INTR_EN0__PROGRAM_COMP 0x0080 -#define INTR_EN0__ERASE_COMP 0x0100 -#define INTR_EN0__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN0__LOCKED_BLK 0x0400 -#define INTR_EN0__UNSUP_CMD 0x0800 -#define INTR_EN0__INT_ACT 0x1000 -#define INTR_EN0__RST_COMP 0x2000 -#define INTR_EN0__PIPE_CMD_ERR 0x4000 -#define INTR_EN0__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT0 0x430 -#define PAGE_CNT0__VALUE 0x00ff - -#define ERR_PAGE_ADDR0 0x440 -#define ERR_PAGE_ADDR0__VALUE 0xffff - -#define ERR_BLOCK_ADDR0 0x450 -#define ERR_BLOCK_ADDR0__VALUE 0xffff - -#define INTR_STATUS1 0x460 -#define INTR_STATUS1__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS1__ECC_ERR 0x0002 -#define INTR_STATUS1__DMA_CMD_COMP 0x0004 -#define INTR_STATUS1__TIME_OUT 0x0008 -#define INTR_STATUS1__PROGRAM_FAIL 0x0010 -#define INTR_STATUS1__ERASE_FAIL 0x0020 -#define INTR_STATUS1__LOAD_COMP 0x0040 -#define INTR_STATUS1__PROGRAM_COMP 0x0080 -#define INTR_STATUS1__ERASE_COMP 0x0100 -#define INTR_STATUS1__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS1__LOCKED_BLK 0x0400 -#define INTR_STATUS1__UNSUP_CMD 0x0800 -#define INTR_STATUS1__INT_ACT 0x1000 -#define INTR_STATUS1__RST_COMP 0x2000 -#define INTR_STATUS1__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS1__PAGE_XFER_INC 0x8000 - -#define INTR_EN1 0x470 -#define INTR_EN1__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN1__ECC_ERR 0x0002 -#define INTR_EN1__DMA_CMD_COMP 0x0004 -#define INTR_EN1__TIME_OUT 0x0008 -#define INTR_EN1__PROGRAM_FAIL 0x0010 -#define INTR_EN1__ERASE_FAIL 0x0020 -#define INTR_EN1__LOAD_COMP 0x0040 -#define INTR_EN1__PROGRAM_COMP 0x0080 -#define INTR_EN1__ERASE_COMP 0x0100 -#define INTR_EN1__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN1__LOCKED_BLK 0x0400 -#define INTR_EN1__UNSUP_CMD 0x0800 -#define INTR_EN1__INT_ACT 0x1000 -#define INTR_EN1__RST_COMP 0x2000 -#define INTR_EN1__PIPE_CMD_ERR 0x4000 -#define INTR_EN1__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT1 0x480 -#define PAGE_CNT1__VALUE 0x00ff - -#define ERR_PAGE_ADDR1 0x490 -#define ERR_PAGE_ADDR1__VALUE 0xffff - -#define ERR_BLOCK_ADDR1 0x4a0 -#define ERR_BLOCK_ADDR1__VALUE 0xffff - -#define INTR_STATUS2 0x4b0 -#define INTR_STATUS2__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS2__ECC_ERR 0x0002 -#define INTR_STATUS2__DMA_CMD_COMP 0x0004 -#define INTR_STATUS2__TIME_OUT 0x0008 -#define INTR_STATUS2__PROGRAM_FAIL 0x0010 -#define INTR_STATUS2__ERASE_FAIL 0x0020 -#define INTR_STATUS2__LOAD_COMP 0x0040 -#define INTR_STATUS2__PROGRAM_COMP 0x0080 -#define INTR_STATUS2__ERASE_COMP 0x0100 -#define INTR_STATUS2__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS2__LOCKED_BLK 0x0400 -#define INTR_STATUS2__UNSUP_CMD 0x0800 -#define INTR_STATUS2__INT_ACT 0x1000 -#define INTR_STATUS2__RST_COMP 0x2000 -#define INTR_STATUS2__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS2__PAGE_XFER_INC 0x8000 - -#define INTR_EN2 0x4c0 -#define INTR_EN2__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN2__ECC_ERR 0x0002 -#define INTR_EN2__DMA_CMD_COMP 0x0004 -#define INTR_EN2__TIME_OUT 0x0008 -#define INTR_EN2__PROGRAM_FAIL 0x0010 -#define INTR_EN2__ERASE_FAIL 0x0020 -#define INTR_EN2__LOAD_COMP 0x0040 -#define INTR_EN2__PROGRAM_COMP 0x0080 -#define INTR_EN2__ERASE_COMP 0x0100 -#define INTR_EN2__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN2__LOCKED_BLK 0x0400 -#define INTR_EN2__UNSUP_CMD 0x0800 -#define INTR_EN2__INT_ACT 0x1000 -#define INTR_EN2__RST_COMP 0x2000 -#define INTR_EN2__PIPE_CMD_ERR 0x4000 -#define INTR_EN2__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT2 0x4d0 -#define PAGE_CNT2__VALUE 0x00ff - -#define ERR_PAGE_ADDR2 0x4e0 -#define ERR_PAGE_ADDR2__VALUE 0xffff - -#define ERR_BLOCK_ADDR2 0x4f0 -#define ERR_BLOCK_ADDR2__VALUE 0xffff - -#define INTR_STATUS3 0x500 -#define INTR_STATUS3__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS3__ECC_ERR 0x0002 -#define INTR_STATUS3__DMA_CMD_COMP 0x0004 -#define INTR_STATUS3__TIME_OUT 0x0008 -#define INTR_STATUS3__PROGRAM_FAIL 0x0010 -#define INTR_STATUS3__ERASE_FAIL 0x0020 -#define INTR_STATUS3__LOAD_COMP 0x0040 -#define INTR_STATUS3__PROGRAM_COMP 0x0080 -#define INTR_STATUS3__ERASE_COMP 0x0100 -#define INTR_STATUS3__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS3__LOCKED_BLK 0x0400 -#define INTR_STATUS3__UNSUP_CMD 0x0800 -#define INTR_STATUS3__INT_ACT 0x1000 -#define INTR_STATUS3__RST_COMP 0x2000 -#define INTR_STATUS3__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS3__PAGE_XFER_INC 0x8000 - -#define INTR_EN3 0x510 -#define INTR_EN3__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN3__ECC_ERR 0x0002 -#define INTR_EN3__DMA_CMD_COMP 0x0004 -#define INTR_EN3__TIME_OUT 0x0008 -#define INTR_EN3__PROGRAM_FAIL 0x0010 -#define INTR_EN3__ERASE_FAIL 0x0020 -#define INTR_EN3__LOAD_COMP 0x0040 -#define INTR_EN3__PROGRAM_COMP 0x0080 -#define INTR_EN3__ERASE_COMP 0x0100 -#define INTR_EN3__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN3__LOCKED_BLK 0x0400 -#define INTR_EN3__UNSUP_CMD 0x0800 -#define INTR_EN3__INT_ACT 0x1000 -#define INTR_EN3__RST_COMP 0x2000 -#define INTR_EN3__PIPE_CMD_ERR 0x4000 -#define INTR_EN3__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT3 0x520 -#define PAGE_CNT3__VALUE 0x00ff - -#define ERR_PAGE_ADDR3 0x530 -#define ERR_PAGE_ADDR3__VALUE 0xffff - -#define ERR_BLOCK_ADDR3 0x540 -#define ERR_BLOCK_ADDR3__VALUE 0xffff +#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50)) +#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50)) + +#define INTR_STATUS__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS__ECC_ERR 0x0002 +#define INTR_STATUS__DMA_CMD_COMP 0x0004 +#define INTR_STATUS__TIME_OUT 0x0008 +#define INTR_STATUS__PROGRAM_FAIL 0x0010 +#define INTR_STATUS__ERASE_FAIL 0x0020 +#define INTR_STATUS__LOAD_COMP 0x0040 +#define INTR_STATUS__PROGRAM_COMP 0x0080 +#define INTR_STATUS__ERASE_COMP 0x0100 +#define INTR_STATUS__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS__LOCKED_BLK 0x0400 +#define INTR_STATUS__UNSUP_CMD 0x0800 +#define INTR_STATUS__INT_ACT 0x1000 +#define INTR_STATUS__RST_COMP 0x2000 +#define INTR_STATUS__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS__PAGE_XFER_INC 0x8000 + +#define INTR_EN__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN__ECC_ERR 0x0002 +#define INTR_EN__DMA_CMD_COMP 0x0004 +#define INTR_EN__TIME_OUT 0x0008 +#define INTR_EN__PROGRAM_FAIL 0x0010 +#define INTR_EN__ERASE_FAIL 0x0020 +#define INTR_EN__LOAD_COMP 0x0040 +#define INTR_EN__PROGRAM_COMP 0x0080 +#define INTR_EN__ERASE_COMP 0x0100 +#define INTR_EN__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN__LOCKED_BLK 0x0400 +#define INTR_EN__UNSUP_CMD 0x0800 +#define INTR_EN__INT_ACT 0x1000 +#define INTR_EN__RST_COMP 0x2000 +#define INTR_EN__PIPE_CMD_ERR 0x4000 +#define INTR_EN__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT(__bank) (0x430 + ((__bank) * 0x50)) +#define ERR_PAGE_ADDR(__bank) (0x440 + ((__bank) * 0x50)) +#define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50)) #define DATA_INTR 0x550 #define DATA_INTR__WRITE_SPACE_AV 0x0001 @@ -484,141 +345,23 @@ #define PTN_INTR_EN__ACCESS_ERROR_BANK3 0x0010 #define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020 -#define PERM_SRC_ID_0 0x830 -#define PERM_SRC_ID_0__SRCID 0x00ff -#define PERM_SRC_ID_0__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_0__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_0__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_0__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_0 0x840 -#define MIN_BLK_ADDR_0__VALUE 0xffff - -#define MAX_BLK_ADDR_0 0x850 -#define MAX_BLK_ADDR_0__VALUE 0xffff - -#define MIN_MAX_BANK_0 0x860 -#define MIN_MAX_BANK_0__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_0__MAX_VALUE 0x000c - -#define PERM_SRC_ID_1 0x870 -#define PERM_SRC_ID_1__SRCID 0x00ff -#define PERM_SRC_ID_1__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_1__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_1__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_1__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_1 0x880 -#define MIN_BLK_ADDR_1__VALUE 0xffff - -#define MAX_BLK_ADDR_1 0x890 -#define MAX_BLK_ADDR_1__VALUE 0xffff - -#define MIN_MAX_BANK_1 0x8a0 -#define MIN_MAX_BANK_1__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_1__MAX_VALUE 0x000c - -#define PERM_SRC_ID_2 0x8b0 -#define PERM_SRC_ID_2__SRCID 0x00ff -#define PERM_SRC_ID_2__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_2__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_2__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_2__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_2 0x8c0 -#define MIN_BLK_ADDR_2__VALUE 0xffff - -#define MAX_BLK_ADDR_2 0x8d0 -#define MAX_BLK_ADDR_2__VALUE 0xffff - -#define MIN_MAX_BANK_2 0x8e0 -#define MIN_MAX_BANK_2__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_2__MAX_VALUE 0x000c - -#define PERM_SRC_ID_3 0x8f0 -#define PERM_SRC_ID_3__SRCID 0x00ff -#define PERM_SRC_ID_3__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_3__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_3__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_3__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_3 0x900 -#define MIN_BLK_ADDR_3__VALUE 0xffff - -#define MAX_BLK_ADDR_3 0x910 -#define MAX_BLK_ADDR_3__VALUE 0xffff - -#define MIN_MAX_BANK_3 0x920 -#define MIN_MAX_BANK_3__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_3__MAX_VALUE 0x000c - -#define PERM_SRC_ID_4 0x930 -#define PERM_SRC_ID_4__SRCID 0x00ff -#define PERM_SRC_ID_4__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_4__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_4__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_4__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_4 0x940 -#define MIN_BLK_ADDR_4__VALUE 0xffff - -#define MAX_BLK_ADDR_4 0x950 -#define MAX_BLK_ADDR_4__VALUE 0xffff - -#define MIN_MAX_BANK_4 0x960 -#define MIN_MAX_BANK_4__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_4__MAX_VALUE 0x000c - -#define PERM_SRC_ID_5 0x970 -#define PERM_SRC_ID_5__SRCID 0x00ff -#define PERM_SRC_ID_5__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_5__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_5__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_5__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_5 0x980 -#define MIN_BLK_ADDR_5__VALUE 0xffff - -#define MAX_BLK_ADDR_5 0x990 -#define MAX_BLK_ADDR_5__VALUE 0xffff - -#define MIN_MAX_BANK_5 0x9a0 -#define MIN_MAX_BANK_5__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_5__MAX_VALUE 0x000c - -#define PERM_SRC_ID_6 0x9b0 -#define PERM_SRC_ID_6__SRCID 0x00ff -#define PERM_SRC_ID_6__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_6__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_6__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_6__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_6 0x9c0 -#define MIN_BLK_ADDR_6__VALUE 0xffff - -#define MAX_BLK_ADDR_6 0x9d0 -#define MAX_BLK_ADDR_6__VALUE 0xffff - -#define MIN_MAX_BANK_6 0x9e0 -#define MIN_MAX_BANK_6__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_6__MAX_VALUE 0x000c - -#define PERM_SRC_ID_7 0x9f0 -#define PERM_SRC_ID_7__SRCID 0x00ff -#define PERM_SRC_ID_7__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_7__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_7__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_7__PARTITION_VALID 0x8000 +#define PERM_SRC_ID(__bank) (0x830 + ((__bank) * 0x40)) +#define PERM_SRC_ID__SRCID 0x00ff +#define PERM_SRC_ID__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID__READ_ACTIVE 0x4000 +#define PERM_SRC_ID__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR(__bank) (0x840 + ((__bank) * 0x40)) +#define MIN_BLK_ADDR__VALUE 0xffff + +#define MAX_BLK_ADDR(__bank) (0x850 + ((__bank) * 0x40)) +#define MAX_BLK_ADDR__VALUE 0xffff + +#define MIN_MAX_BANK(__bank) (0x860 + ((__bank) * 0x40)) +#define MIN_MAX_BANK__MIN_VALUE 0x0003 +#define MIN_MAX_BANK__MAX_VALUE 0x000c -#define MIN_BLK_ADDR_7 0xa00 -#define MIN_BLK_ADDR_7__VALUE 0xffff - -#define MAX_BLK_ADDR_7 0xa10 -#define MAX_BLK_ADDR_7__VALUE 0xffff - -#define MIN_MAX_BANK_7 0xa20 -#define MIN_MAX_BANK_7__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_7__MAX_VALUE 0x000c /* ffsdefs.h */ #define CLEAR 0 /*use this to clear a field instead of "fail"*/ @@ -711,19 +454,17 @@ #define READ_WRITE_ENABLE_HIGH_COUNT 22 #define ECC_SECTOR_SIZE 512 -#define LLD_MAX_FLASH_BANKS 4 - -#define DENALI_BUF_SIZE (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) struct nand_buf { int head; int tail; - uint8_t buf[DENALI_BUF_SIZE]; + uint8_t *buf; dma_addr_t dma_buf; }; #define INTEL_CE4100 1 #define INTEL_MRST 2 +#define DT 3 struct denali_nand_info { struct mtd_info mtd; @@ -732,7 +473,7 @@ struct denali_nand_info { int status; int platform; struct nand_buf buf; - struct pci_dev *dev; + struct device *dev; int total_used_banks; uint32_t block; /* stored for future use */ uint16_t page; @@ -745,12 +486,17 @@ struct denali_nand_info { uint32_t irq_status; int irq_debug_array[32]; int idx; + int irq; uint32_t devnum; /* represent how many nands connected */ uint32_t fwblks; /* represent how many blocks FW used */ uint32_t totalblks; uint32_t blksperchip; uint32_t bbtskipbytes; + uint32_t max_banks; }; +extern int denali_init(struct denali_nand_info *denali); +extern void denali_remove(struct denali_nand_info *denali); + #endif /*_LLD_NAND_*/ diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c new file mode 100644 index 00000000000..35cb17f5780 --- /dev/null +++ b/drivers/mtd/nand/denali_dt.c @@ -0,0 +1,132 @@ +/* + * NAND Flash Controller Device Driver for DT + * + * Copyright © 2011, Picochip. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/slab.h> + +#include "denali.h" + +struct denali_dt { + struct denali_nand_info denali; + struct clk *clk; +}; + +static const struct of_device_id denali_nand_dt_ids[] = { + { .compatible = "denali,denali-nand-dt" }, + { /* sentinel */ } + }; + +MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); + +static u64 denali_dma_mask; + +static int denali_dt_probe(struct platform_device *ofdev) +{ + struct resource *denali_reg, *nand_data; + struct denali_dt *dt; + struct denali_nand_info *denali; + int ret; + const struct of_device_id *of_id; + + of_id = of_match_device(denali_nand_dt_ids, &ofdev->dev); + if (of_id) { + ofdev->id_entry = of_id->data; + } else { + pr_err("Failed to find the right device id.\n"); + return -ENOMEM; + } + + dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL); + if (!dt) + return -ENOMEM; + denali = &dt->denali; + + denali->platform = DT; + denali->dev = &ofdev->dev; + denali->irq = platform_get_irq(ofdev, 0); + if (denali->irq < 0) { + dev_err(&ofdev->dev, "no irq defined\n"); + return denali->irq; + } + + denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg"); + denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg); + if (IS_ERR(denali->flash_reg)) + return PTR_ERR(denali->flash_reg); + + nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data"); + denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data); + if (IS_ERR(denali->flash_mem)) + return PTR_ERR(denali->flash_mem); + + if (!of_property_read_u32(ofdev->dev.of_node, + "dma-mask", (u32 *)&denali_dma_mask)) { + denali->dev->dma_mask = &denali_dma_mask; + } else { + denali->dev->dma_mask = NULL; + } + + dt->clk = devm_clk_get(&ofdev->dev, NULL); + if (IS_ERR(dt->clk)) { + dev_err(&ofdev->dev, "no clk available\n"); + return PTR_ERR(dt->clk); + } + clk_prepare_enable(dt->clk); + + ret = denali_init(denali); + if (ret) + goto out_disable_clk; + + platform_set_drvdata(ofdev, dt); + return 0; + +out_disable_clk: + clk_disable_unprepare(dt->clk); + + return ret; +} + +static int denali_dt_remove(struct platform_device *ofdev) +{ + struct denali_dt *dt = platform_get_drvdata(ofdev); + + denali_remove(&dt->denali); + clk_disable(dt->clk); + + return 0; +} + +static struct platform_driver denali_dt_driver = { + .probe = denali_dt_probe, + .remove = denali_dt_remove, + .driver = { + .name = "denali-nand-dt", + .owner = THIS_MODULE, + .of_match_table = denali_nand_dt_ids, + }, +}; + +module_platform_driver(denali_dt_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jamie Iles"); +MODULE_DESCRIPTION("DT driver for Denali NAND controller"); diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c new file mode 100644 index 00000000000..6e2f387b823 --- /dev/null +++ b/drivers/mtd/nand/denali_pci.c @@ -0,0 +1,142 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/slab.h> + +#include "denali.h" + +#define DENALI_NAND_NAME "denali-nand-pci" + +/* List of platforms this NAND controller has be integrated into */ +static const struct pci_device_id denali_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, denali_pci_ids); + +static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + resource_size_t csr_base, mem_base; + unsigned long csr_len, mem_len; + struct denali_nand_info *denali; + + denali = kzalloc(sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + ret = pci_enable_device(dev); + if (ret) { + pr_err("Spectra: pci_enable_device failed.\n"); + goto failed_alloc_memery; + } + + if (id->driver_data == INTEL_CE4100) { + denali->platform = INTEL_CE4100; + mem_base = pci_resource_start(dev, 0); + mem_len = pci_resource_len(dev, 1); + csr_base = pci_resource_start(dev, 1); + csr_len = pci_resource_len(dev, 1); + } else { + denali->platform = INTEL_MRST; + csr_base = pci_resource_start(dev, 0); + csr_len = pci_resource_len(dev, 0); + mem_base = pci_resource_start(dev, 1); + mem_len = pci_resource_len(dev, 1); + if (!mem_len) { + mem_base = csr_base + csr_len; + mem_len = csr_len; + } + } + + pci_set_master(dev); + denali->dev = &dev->dev; + denali->irq = dev->irq; + + ret = pci_request_regions(dev, DENALI_NAND_NAME); + if (ret) { + pr_err("Spectra: Unable to request memory regions\n"); + goto failed_enable_dev; + } + + denali->flash_reg = ioremap_nocache(csr_base, csr_len); + if (!denali->flash_reg) { + pr_err("Spectra: Unable to remap memory region\n"); + ret = -ENOMEM; + goto failed_req_regions; + } + + denali->flash_mem = ioremap_nocache(mem_base, mem_len); + if (!denali->flash_mem) { + pr_err("Spectra: ioremap_nocache failed!"); + ret = -ENOMEM; + goto failed_remap_reg; + } + + ret = denali_init(denali); + if (ret) + goto failed_remap_mem; + + pci_set_drvdata(dev, denali); + + return 0; + +failed_remap_mem: + iounmap(denali->flash_mem); +failed_remap_reg: + iounmap(denali->flash_reg); +failed_req_regions: + pci_release_regions(dev); +failed_enable_dev: + pci_disable_device(dev); +failed_alloc_memery: + kfree(denali); + + return ret; +} + +/* driver exit point */ +static void denali_pci_remove(struct pci_dev *dev) +{ + struct denali_nand_info *denali = pci_get_drvdata(dev); + + denali_remove(denali); + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + pci_release_regions(dev); + pci_disable_device(dev); + kfree(denali); +} + +static struct pci_driver denali_pci_driver = { + .name = DENALI_NAND_NAME, + .id_table = denali_pci_ids, + .probe = denali_pci_probe, + .remove = denali_pci_remove, +}; + +static int denali_init_pci(void) +{ + return pci_register_driver(&denali_pci_driver); +} +module_init(denali_init_pci); + +static void denali_exit_pci(void) +{ + pci_unregister_driver(&denali_pci_driver); +} +module_exit(denali_exit_pci); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index b7f8de7b278..f68a7bccecd 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -31,13 +31,14 @@ #include <linux/mtd/doc2000.h> #include <linux/mtd/partitions.h> #include <linux/mtd/inftl.h> +#include <linux/module.h> /* Where to look for the devices? */ #ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS #define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 #endif -static unsigned long __initdata doc_locations[] = { +static unsigned long doc_locations[] __initdata = { #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) #ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, @@ -45,15 +46,13 @@ static unsigned long __initdata doc_locations[] = { 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, -#else /* CONFIG_MTD_DOCPROBE_HIGH */ +#else 0xc8000, 0xca000, 0xcc000, 0xce000, 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000, 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, -#endif /* CONFIG_MTD_DOCPROBE_HIGH */ -#else -#warning Unknown architecture for DiskOnChip. No default probe locations defined +#endif #endif 0xffffffff }; @@ -132,12 +131,12 @@ static struct rs_control *rs_decoder; /* * The HW decoder in the DoC ASIC's provides us a error syndrome, - * which we must convert to a standard syndrom usable by the generic + * which we must convert to a standard syndrome usable by the generic * Reed-Solomon library code. * * Fabrice Bellard figured this out in the old docecc code. I added * some comments, improved a minor bit and converted it to make use - * of the generic Reed-Solomon libary. tglx + * of the generic Reed-Solomon library. tglx */ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) { @@ -153,7 +152,7 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); parity = ecc[1]; - /* Initialize the syndrom buffer */ + /* Initialize the syndrome buffer */ for (i = 0; i < NROOTS; i++) s[i] = ds[0]; /* @@ -375,19 +374,6 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len) } } -static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - struct doc_priv *doc = this->priv; - void __iomem *docptr = doc->virtadr; - int i; - - for (i = 0; i < len; i++) - if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) - return -EFAULT; - return 0; -} - static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) { struct nand_chip *this = mtd->priv; @@ -400,7 +386,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - /* We cant' use dev_ready here, but at least we wait for the + /* We can't use dev_ready here, but at least we wait for the * command to complete */ udelay(50); @@ -525,26 +511,6 @@ static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len) buf[i] = ReadDOC(docptr, LastDataRead); } -static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - struct doc_priv *doc = this->priv; - void __iomem *docptr = doc->virtadr; - int i; - - /* Start read pipeline */ - ReadDOC(docptr, ReadPipeInit); - - for (i = 0; i < len - 1; i++) - if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { - ReadDOC(docptr, LastDataRead); - return i; - } - if (buf[i] != ReadDOC(docptr, LastDataRead)) - return i; - return 0; -} - static u_char doc2001plus_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; @@ -609,33 +575,6 @@ static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len) printk("\n"); } -static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - struct doc_priv *doc = this->priv; - void __iomem *docptr = doc->virtadr; - int i; - - if (debug) - printk("verifybuf of %d bytes: ", len); - - /* Start read pipeline */ - ReadDOC(docptr, Mplus_ReadPipeInit); - ReadDOC(docptr, Mplus_ReadPipeInit); - - for (i = 0; i < len - 2; i++) - if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { - ReadDOC(docptr, Mplus_LastDataRead); - ReadDOC(docptr, Mplus_LastDataRead); - return i; - } - if (buf[len - 2] != ReadDOC(docptr, Mplus_LastDataRead)) - return len - 2; - if (buf[len - 1] != ReadDOC(docptr, Mplus_LastDataRead)) - return len - 1; - return 0; -} - static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *this = mtd->priv; @@ -759,7 +698,8 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) + if (this->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; WriteDOC(column, docptr, Mplus_FlashAddress); } @@ -986,7 +926,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, dummy = ReadDOC(docptr, ECCConf); } - /* Error occured ? */ + /* Error occurred ? */ if (dummy & 0x80) { for (i = 0; i < 6; i++) { if (DoC_is_MillenniumPlus(doc)) @@ -1031,7 +971,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); else WriteDOC(DOC_ECC_DIS, docptr, ECCConf); - if (no_ecc_failures && (ret == -EBADMSG)) { + if (no_ecc_failures && mtd_is_eccerr(ret)) { printk(KERN_ERR "suppressing ECC failure\n"); ret = 0; } @@ -1071,7 +1011,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch size_t retlen; for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { - ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); if (retlen != mtd->writesize) continue; if (ret) { @@ -1096,7 +1036,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch /* Only one mediaheader was found. We want buf to contain a mediaheader on return, so we'll have to re-read the one we found. */ offs = doc->mh0_page << this->page_shift; - ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + 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"); @@ -1119,7 +1059,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { - printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; } if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) @@ -1160,7 +1099,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio /* NOTE: The lines below modify internal variables of the NAND and MTD layers; variables with have already been configured by nand_scan. Unfortunately, we didn't know before this point what these values - should be. Thus, this code is somewhat dependant on the exact + should be. Thus, this code is somewhat dependent on the exact implementation of the NAND layer. */ if (mh->UnitSizeFactor != 0xff) { this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); @@ -1227,7 +1166,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { - printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; } @@ -1360,11 +1298,9 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd) At least as nand_bbt.c is currently written. */ if ((ret = nand_scan_bbt(mtd, NULL))) return ret; - add_mtd_device(mtd); -#ifdef CONFIG_MTD_PARTITIONS + mtd_device_register(mtd, NULL, 0); if (!no_autopart) - add_mtd_partitions(mtd, parts, numparts); -#endif + mtd_device_register(mtd, parts, numparts); return 0; } @@ -1419,11 +1355,9 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) autopartitioning, but I want to give it more thought. */ if (!numparts) return -EIO; - add_mtd_device(mtd); -#ifdef CONFIG_MTD_PARTITIONS + mtd_device_register(mtd, NULL, 0); if (!no_autopart) - add_mtd_partitions(mtd, parts, numparts); -#endif + mtd_device_register(mtd, parts, numparts); return 0; } @@ -1435,7 +1369,6 @@ static inline int __init doc2000_init(struct mtd_info *mtd) this->read_byte = doc2000_read_byte; this->write_buf = doc2000_writebuf; this->read_buf = doc2000_readbuf; - this->verify_buf = doc2000_verifybuf; this->scan_bbt = nftl_scan_bbt; doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; @@ -1452,7 +1385,6 @@ static inline int __init doc2001_init(struct mtd_info *mtd) this->read_byte = doc2001_read_byte; this->write_buf = doc2001_writebuf; this->read_buf = doc2001_readbuf; - this->verify_buf = doc2001_verifybuf; ReadDOC(doc->virtadr, ChipID); ReadDOC(doc->virtadr, ChipID); @@ -1483,7 +1415,6 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) this->read_byte = doc2001plus_read_byte; this->write_buf = doc2001plus_writebuf; this->read_buf = doc2001plus_readbuf; - this->verify_buf = doc2001plus_verifybuf; this->scan_bbt = inftl_scan_bbt; this->cmd_ctrl = NULL; this->select_chip = doc2001plus_select_chip; @@ -1508,10 +1439,13 @@ static int __init doc_probe(unsigned long physadr) int reg, len, numchips; int ret = 0; + if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip")) + 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); - return -EIO; + ret = -EIO; + goto error_ioremap; } /* It's not possible to cleanly detect the DiskOnChip - the @@ -1629,7 +1563,6 @@ static int __init doc_probe(unsigned long physadr) sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); mtd = kzalloc(len, GFP_KERNEL); if (!mtd) { - printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); ret = -ENOMEM; goto fail; } @@ -1656,7 +1589,8 @@ static int __init doc_probe(unsigned long physadr) nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = 512; nand->ecc.bytes = 6; - nand->options = NAND_USE_FLASH_BBT; + nand->ecc.strength = 2; + nand->bbt_options = NAND_BBT_USE_FLASH; doc->physadr = physadr; doc->virtadr = virtadr; @@ -1678,9 +1612,9 @@ static int __init doc_probe(unsigned long physadr) /* DBB note: i believe nand_release is necessary here, as buffers may have been allocated in nand_base. Check with Thomas. FIX ME! */ - /* nand_release will call del_mtd_device, but we haven't yet - added it. This is handled without incident by - del_mtd_device, as far as I can tell. */ + /* nand_release will call mtd_device_unregister, but we + haven't yet added it. This is handled without incident by + mtd_device_unregister, as far as I can tell. */ nand_release(mtd); kfree(mtd); goto fail; @@ -1696,6 +1630,10 @@ static int __init doc_probe(unsigned long physadr) WriteDOC(save_control, virtadr, DOCControl); fail: iounmap(virtadr); + +error_ioremap: + release_mem_region(physadr, DOC_IOREMAP_LEN); + return ret; } @@ -1712,6 +1650,7 @@ static void release_nanddoc(void) nextmtd = doc->nextdoc; nand_release(mtd); iounmap(doc->virtadr); + release_mem_region(doc->physadr, DOC_IOREMAP_LEN); kfree(mtd); } } diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c new file mode 100644 index 00000000000..ce24637e14f --- /dev/null +++ b/drivers/mtd/nand/docg4.c @@ -0,0 +1,1394 @@ +/* + * Copyright © 2012 Mike Dunn <mikedunn@newsguy.com> + * + * mtd nand driver for M-Systems DiskOnChip G4 + * + * 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. + * + * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus + * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. + * Should work on these as well. Let me know! + * + * TODO: + * + * Mechanism for management of password-protected areas + * + * Hamming ecc when reading oob only + * + * According to the M-Sys documentation, this device is also available in a + * "dual-die" configuration having a 256MB capacity, but no mechanism for + * detecting this variant is documented. Currently this driver assumes 128MB + * capacity. + * + * Support for multiple cascaded devices ("floors"). Not sure which gadgets + * contain multiple G4s in a cascaded configuration, if any. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/bch.h> +#include <linux/bitrev.h> +#include <linux/jiffies.h> + +/* + * In "reliable mode" consecutive 2k pages are used in parallel (in some + * fashion) to store the same data. The data can be read back from the + * even-numbered pages in the normal manner; odd-numbered pages will appear to + * contain junk. Systems that boot from the docg4 typically write the secondary + * program loader (SPL) code in this mode. The SPL is loaded by the initial + * program loader (IPL, stored in the docg4's 2k NOR-like region that is mapped + * to the reset vector address). This module parameter enables you to use this + * driver to write the SPL. When in this mode, no more than 2k of data can be + * written at a time, because the addresses do not increment in the normal + * manner, and the starting offset must be within an even-numbered 2k region; + * i.e., invalid starting offsets are 0x800, 0xa00, 0xc00, 0xe00, 0x1800, + * 0x1a00, ... Reliable mode is a special case and should not be used unless + * you know what you're doing. + */ +static bool reliable_mode; +module_param(reliable_mode, bool, 0); +MODULE_PARM_DESC(reliable_mode, "pages are programmed in reliable mode"); + +/* + * You'll want to ignore badblocks if you're reading a partition that contains + * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since + * it does not use mtd nand's method for marking bad blocks (using oob area). + * This will also skip the check of the "page written" flag. + */ +static bool ignore_badblocks; +module_param(ignore_badblocks, bool, 0); +MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed"); + +struct docg4_priv { + struct mtd_info *mtd; + struct device *dev; + void __iomem *virtadr; + int status; + struct { + unsigned int command; + int column; + int page; + } last_command; + uint8_t oob_buf[16]; + uint8_t ecc_buf[7]; + int oob_page; + struct bch_control *bch; +}; + +/* + * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are + * shared with other diskonchip devices (P3, G3 at least). + * + * Functions with names prefixed with docg4_ are mtd / nand interface functions + * (though they may also be called internally). All others are internal. + */ + +#define DOC_IOSPACE_DATA 0x0800 + +/* register offsets */ +#define DOC_CHIPID 0x1000 +#define DOC_DEVICESELECT 0x100a +#define DOC_ASICMODE 0x100c +#define DOC_DATAEND 0x101e +#define DOC_NOP 0x103e + +#define DOC_FLASHSEQUENCE 0x1032 +#define DOC_FLASHCOMMAND 0x1034 +#define DOC_FLASHADDRESS 0x1036 +#define DOC_FLASHCONTROL 0x1038 +#define DOC_ECCCONF0 0x1040 +#define DOC_ECCCONF1 0x1042 +#define DOC_HAMMINGPARITY 0x1046 +#define DOC_BCH_SYNDROM(idx) (0x1048 + idx) + +#define DOC_ASICMODECONFIRM 0x1072 +#define DOC_CHIPID_INV 0x1074 +#define DOC_POWERMODE 0x107c + +#define DOCG4_MYSTERY_REG 0x1050 + +/* apparently used only to write oob bytes 6 and 7 */ +#define DOCG4_OOB_6_7 0x1052 + +/* DOC_FLASHSEQUENCE register commands */ +#define DOC_SEQ_RESET 0x00 +#define DOCG4_SEQ_PAGE_READ 0x03 +#define DOCG4_SEQ_FLUSH 0x29 +#define DOCG4_SEQ_PAGEWRITE 0x16 +#define DOCG4_SEQ_PAGEPROG 0x1e +#define DOCG4_SEQ_BLOCKERASE 0x24 +#define DOCG4_SEQ_SETMODE 0x45 + +/* DOC_FLASHCOMMAND register commands */ +#define DOCG4_CMD_PAGE_READ 0x00 +#define DOC_CMD_ERASECYCLE2 0xd0 +#define DOCG4_CMD_FLUSH 0x70 +#define DOCG4_CMD_READ2 0x30 +#define DOC_CMD_PROG_BLOCK_ADDR 0x60 +#define DOCG4_CMD_PAGEWRITE 0x80 +#define DOC_CMD_PROG_CYCLE2 0x10 +#define DOCG4_CMD_FAST_MODE 0xa3 /* functionality guessed */ +#define DOC_CMD_RELIABLE_MODE 0x22 +#define DOC_CMD_RESET 0xff + +/* DOC_POWERMODE register bits */ +#define DOC_POWERDOWN_READY 0x80 + +/* DOC_FLASHCONTROL register bits */ +#define DOC_CTRL_CE 0x10 +#define DOC_CTRL_UNKNOWN 0x40 +#define DOC_CTRL_FLASHREADY 0x01 + +/* DOC_ECCCONF0 register bits */ +#define DOC_ECCCONF0_READ_MODE 0x8000 +#define DOC_ECCCONF0_UNKNOWN 0x2000 +#define DOC_ECCCONF0_ECC_ENABLE 0x1000 +#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff + +/* DOC_ECCCONF1 register bits */ +#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80 +#define DOC_ECCCONF1_ECC_ENABLE 0x07 +#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20 + +/* DOC_ASICMODE register bits */ +#define DOC_ASICMODE_RESET 0x00 +#define DOC_ASICMODE_NORMAL 0x01 +#define DOC_ASICMODE_POWERDOWN 0x02 +#define DOC_ASICMODE_MDWREN 0x04 +#define DOC_ASICMODE_BDETCT_RESET 0x08 +#define DOC_ASICMODE_RSTIN_RESET 0x10 +#define DOC_ASICMODE_RAM_WE 0x20 + +/* good status values read after read/write/erase operations */ +#define DOCG4_PROGSTATUS_GOOD 0x51 +#define DOCG4_PROGSTATUS_GOOD_2 0xe0 + +/* + * On read operations (page and oob-only), the first byte read from I/O reg is a + * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read + * after reset only) or 0x51, so bit 1 is presumed to be an error indicator. + */ +#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */ + +/* anatomy of the device */ +#define DOCG4_CHIP_SIZE 0x8000000 +#define DOCG4_PAGE_SIZE 0x200 +#define DOCG4_PAGES_PER_BLOCK 0x200 +#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE) +#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE) +#define DOCG4_OOB_SIZE 0x10 +#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */ +#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */ +#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */ + +/* all but the last byte is included in ecc calculation */ +#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1) + +#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */ + +/* expected values from the ID registers */ +#define DOCG4_IDREG1_VALUE 0x0400 +#define DOCG4_IDREG2_VALUE 0xfbff + +/* primitive polynomial used to build the Galois field used by hw ecc gen */ +#define DOCG4_PRIMITIVE_POLY 0x4443 + +#define DOCG4_M 14 /* Galois field is of order 2^14 */ +#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */ + +#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */ +#define DOCG4_REDUNDANT_BBT_PAGE 24 /* page where redundant factory bbt lives */ + +/* + * Bytes 0, 1 are used as badblock marker. + * Bytes 2 - 6 are available to the user. + * Byte 7 is hamming ecc for first 7 oob bytes only. + * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14. + * Byte 15 (the last) is used by the driver as a "page written" flag. + */ +static struct nand_ecclayout docg4_oobinfo = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 5, + .oobfree = { {.offset = 2, .length = 5} } +}; + +/* + * The device has a nop register which M-Sys claims is for the purpose of + * inserting precise delays. But beware; at least some operations fail if the + * nop writes are replaced with a generic delay! + */ +static inline void write_nop(void __iomem *docptr) +{ + writew(0, docptr + DOC_NOP); +} + +static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(nand->IO_ADDR_R); +} + +static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], nand->IO_ADDR_W); +} + +static int poll_status(struct docg4_priv *doc) +{ + /* + * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL + * register. Operations known to take a long time (e.g., block erase) + * should sleep for a while before calling this. + */ + + uint16_t flash_status; + unsigned long timeo; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* hardware quirk requires reading twice initially */ + flash_status = readw(docptr + DOC_FLASHCONTROL); + + timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */ + do { + cpu_relax(); + flash_status = readb(docptr + DOC_FLASHCONTROL); + } while (!(flash_status & DOC_CTRL_FLASHREADY) && + time_before(jiffies, timeo)); + + if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) { + dev_err(doc->dev, "%s: timed out!\n", __func__); + return NAND_STATUS_FAIL; + } + + return 0; +} + + +static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) +{ + + struct docg4_priv *doc = nand->priv; + int status = NAND_STATUS_WP; /* inverse logic?? */ + dev_dbg(doc->dev, "%s...\n", __func__); + + /* report any previously unreported error */ + if (doc->status) { + status |= doc->status; + doc->status = 0; + return status; + } + + status |= poll_status(doc); + return status; +} + +static void docg4_select_chip(struct mtd_info *mtd, int chip) +{ + /* + * Select among multiple cascaded chips ("floors"). Multiple floors are + * not yet supported, so the only valid non-negative value is 0. + */ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip); + + if (chip < 0) + return; /* deselected */ + + if (chip > 0) + dev_warn(doc->dev, "multiple floors currently unsupported\n"); + + writew(0, docptr + DOC_DEVICESELECT); +} + +static void reset(struct mtd_info *mtd) +{ + /* full device reset */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + write_nop(docptr); + + writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); + + poll_status(doc); +} + +static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) +{ + /* read the 7 hw-generated ecc bytes */ + + int i; + for (i = 0; i < 7; i++) { /* hw quirk; read twice */ + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + } +} + +static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + /* + * Called after a page read when hardware reports bitflips. + * Up to four bitflips can be corrected. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + int i, numerrs, errpos[4]; + const uint8_t blank_read_hwecc[8] = { + 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; + + read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ + + /* check if read error is due to a blank page */ + if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) + return 0; /* yes */ + + /* skip additional check of "written flag" if ignore_badblocks */ + if (ignore_badblocks == false) { + + /* + * If the hw ecc bytes are not those of a blank page, there's + * still a chance that the page is blank, but was read with + * errors. Check the "written flag" in last oob byte, which + * is set to zero when a page is written. If more than half + * the bits are set, assume a blank page. Unfortunately, the + * bit flips(s) are not reported in stats. + */ + + if (nand->oob_poi[15]) { + int bit, numsetbits = 0; + unsigned long written_flag = nand->oob_poi[15]; + for_each_set_bit(bit, &written_flag, 8) + numsetbits++; + if (numsetbits > 4) { /* assume blank */ + dev_warn(doc->dev, + "error(s) in blank page " + "at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return 0; + } + } + } + + /* + * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch + * algorithm is used to decode this. However the hw operates on page + * data in a bit order that is the reverse of that of the bch alg, + * requiring that the bits be reversed on the result. Thanks to Ivan + * Djelic for his analysis! + */ + for (i = 0; i < 7; i++) + doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); + + numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, + doc->ecc_buf, NULL, errpos); + + if (numerrs == -EBADMSG) { + dev_warn(doc->dev, "uncorrectable errors at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return -EBADMSG; + } + + BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */ + + /* undo last step in BCH alg (modulo mirroring not needed) */ + for (i = 0; i < numerrs; i++) + errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); + + /* fix the errors */ + for (i = 0; i < numerrs; i++) { + + /* ignore if error within oob ecc bytes */ + if (errpos[i] > DOCG4_USERDATA_LEN * 8) + continue; + + /* if error within oob area preceeding ecc bytes... */ + if (errpos[i] > DOCG4_PAGE_SIZE * 8) + change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, + (unsigned long *)nand->oob_poi); + + else /* error in page data */ + change_bit(errpos[i], (unsigned long *)buf); + } + + dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n", + numerrs, page * DOCG4_PAGE_SIZE); + + return numerrs; +} + +static uint8_t docg4_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + + dev_dbg(doc->dev, "%s\n", __func__); + + if (doc->last_command.command == NAND_CMD_STATUS) { + int status; + + /* + * Previous nand command was status request, so nand + * infrastructure code expects to read the status here. If an + * error occurred in a previous operation, report it. + */ + doc->last_command.command = 0; + + if (doc->status) { + status = doc->status; + doc->status = 0; + } + + /* why is NAND_STATUS_WP inverse logic?? */ + else + status = NAND_STATUS_WP | NAND_STATUS_READY; + + return status; + } + + dev_warn(doc->dev, "unexpected call to read_byte()\n"); + + return 0; +} + +static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr) +{ + /* write the four address bytes packed in docg4_addr to the device */ + + void __iomem *docptr = doc->virtadr; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +static int read_progstatus(struct docg4_priv *doc) +{ + /* + * This apparently checks the status of programming. Done after an + * erasure, and after page data is written. On error, the status is + * saved, to be later retrieved by the nand infrastructure code. + */ + void __iomem *docptr = doc->virtadr; + + /* status is read from the I/O reg */ + uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); + + dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n", + __func__, status1, status2, status3); + + if (status1 != DOCG4_PROGSTATUS_GOOD + || status2 != DOCG4_PROGSTATUS_GOOD_2 + || status3 != DOCG4_PROGSTATUS_GOOD_2) { + doc->status = NAND_STATUS_FAIL; + dev_warn(doc->dev, "read_progstatus failed: " + "%02x, %02x, %02x\n", status1, status2, status3); + return -EIO; + } + return 0; +} + +static int pageprog(struct mtd_info *mtd) +{ + /* + * Final step in writing a page. Writes the contents of its + * internal buffer out to the flash array, or some such. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + int retval = 0; + + dev_dbg(doc->dev, "docg4: %s\n", __func__); + + writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + /* Just busy-wait; usleep_range() slows things down noticeably. */ + poll_status(doc); + + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + retval = read_progstatus(doc); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); + + return retval; +} + +static void sequence_reset(struct mtd_info *mtd) +{ + /* common starting sequence for all operations */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); + writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); +} + +static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in reading a page */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 page %08x\n", __func__, docg4_addr); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + write_addr(doc, docg4_addr); + + write_nop(docptr); + writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + poll_status(doc); +} + +static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in writing a page */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 addr: %x\n", __func__, docg4_addr); + sequence_reset(mtd); + + if (unlikely(reliable_mode)) { + writew(DOCG4_SEQ_SETMODE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FAST_MODE, docptr + DOC_FLASHCOMMAND); + writew(DOC_CMD_RELIABLE_MODE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + } + + writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_addr(doc, docg4_addr); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); +} + +static uint32_t mtd_to_docg4_address(int page, int column) +{ + /* + * Convert mtd address to format used by the device, 32 bit packed. + * + * Some notes on G4 addressing... The M-Sys documentation on this device + * claims that pages are 2K in length, and indeed, the format of the + * address used by the device reflects that. But within each page are + * four 512 byte "sub-pages", each with its own oob data that is + * read/written immediately after the 512 bytes of page data. This oob + * data contains the ecc bytes for the preceeding 512 bytes. + * + * Rather than tell the mtd nand infrastructure that page size is 2k, + * with four sub-pages each, we engage in a little subterfuge and tell + * the infrastructure code that pages are 512 bytes in size. This is + * done because during the course of reverse-engineering the device, I + * never observed an instance where an entire 2K "page" was read or + * written as a unit. Each "sub-page" is always addressed individually, + * its data read/written, and ecc handled before the next "sub-page" is + * addressed. + * + * This requires us to convert addresses passed by the mtd nand + * infrastructure code to those used by the device. + * + * The address that is written to the device consists of four bytes: the + * first two are the 2k page number, and the second is the index into + * the page. The index is in terms of 16-bit half-words and includes + * the preceeding oob data, so e.g., the index into the second + * "sub-page" is 0x108, and the full device address of the start of mtd + * page 0x201 is 0x00800108. + */ + int g4_page = page / 4; /* device's 2K page */ + int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ + return (g4_page << 16) | g4_index; /* pack */ +} + +static void docg4_command(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + /* handle standard nand commands */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); + + dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n", + __func__, command, page_addr, column); + + /* + * Save the command and its arguments. This enables emulation of + * standard flash devices, and also some optimizations. + */ + doc->last_command.command = command; + doc->last_command.column = column; + doc->last_command.page = page_addr; + + switch (command) { + + case NAND_CMD_RESET: + reset(mtd); + break; + + case NAND_CMD_READ0: + read_page_prologue(mtd, g4_addr); + break; + + case NAND_CMD_STATUS: + /* next call to read_byte() will expect a status */ + break; + + case NAND_CMD_SEQIN: + if (unlikely(reliable_mode)) { + uint16_t g4_page = g4_addr >> 16; + + /* writes to odd-numbered 2k pages are invalid */ + if (g4_page & 0x01) + dev_warn(doc->dev, + "invalid reliable mode address\n"); + } + + write_page_prologue(mtd, g4_addr); + + /* hack for deferred write of oob bytes */ + if (doc->oob_page == page_addr) + memcpy(nand->oob_poi, doc->oob_buf, 16); + break; + + case NAND_CMD_PAGEPROG: + pageprog(mtd); + break; + + /* we don't expect these, based on review of nand_base.c */ + case NAND_CMD_READOOB: + case NAND_CMD_READID: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + dev_warn(doc->dev, "docg4_command: " + "unexpected nand command 0x%x\n", command); + break; + + } +} + +static int read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page, bool use_ecc) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t status, edc_err, *buf16; + int bits_corrected = 0; + + dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); + + writew(DOC_ECCCONF0_READ_MODE | + DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is page data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_err(doc->dev, + "docg4_read_page: bad status: 0x%02x\n", status); + writew(0, docptr + DOC_DATAEND); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ + + /* this device always reads oob after page data */ + /* first 14 oob bytes read from I/O reg */ + docg4_read_buf(mtd, nand->oob_poi, 14); + + /* last 2 read from another reg */ + buf16 = (uint16_t *)(nand->oob_poi + 14); + *buf16 = readw(docptr + DOCG4_MYSTERY_REG); + + write_nop(docptr); + + if (likely(use_ecc == true)) { + + /* read the register that tells us if bitflip(s) detected */ + edc_err = readw(docptr + DOC_ECCCONF1); + edc_err = readw(docptr + DOC_ECCCONF1); + dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err); + + /* If bitflips are reported, attempt to correct with ecc */ + if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { + bits_corrected = correct_data(mtd, buf, page); + if (bits_corrected == -EBADMSG) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += bits_corrected; + } + } + + writew(0, docptr + DOC_DATAEND); + if (bits_corrected == -EBADMSG) /* uncorrectable errors */ + return 0; + return bits_corrected; +} + + +static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page) +{ + return read_page(mtd, nand, buf, page, false); +} + +static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page) +{ + return read_page(mtd, nand, buf, page, true); +} + +static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t status; + + dev_dbg(doc->dev, "%s: page %x\n", __func__, page); + + docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page); + + writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is oob data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_warn(doc->dev, + "docg4_read_oob failed: status = 0x%02x\n", status); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, nand->oob_poi, 16); + + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return 0; +} + +static int docg4_erase_block(struct mtd_info *mtd, int page) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t g4_page; + + dev_dbg(doc->dev, "%s: page %04x\n", __func__, page); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + /* only 2 bytes of address are written to specify erase block */ + g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */ + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + g4_page >>= 8; + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + write_nop(docptr); + + /* start the erasure */ + writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + usleep_range(500, 1000); /* erasure is long; take a snooze */ + poll_status(doc); + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + read_progstatus(doc); + + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); + + return nand->waitfunc(mtd, nand); +} + +static int write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, bool use_ecc) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint8_t ecc_buf[8]; + + dev_dbg(doc->dev, "%s...\n", __func__); + + writew(DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + + /* write the page data */ + docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); + + /* oob bytes 0 through 5 are written to I/O reg */ + docg4_write_buf16(mtd, nand->oob_poi, 6); + + /* oob byte 6 written to a separate reg */ + writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); + + write_nop(docptr); + write_nop(docptr); + + /* write hw-generated ecc bytes to oob */ + if (likely(use_ecc == true)) { + /* oob byte 7 is hamming code */ + uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); + hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ + writew(hamming, docptr + DOCG4_OOB_6_7); + write_nop(docptr); + + /* read the 7 bch bytes from ecc regs */ + read_hw_ecc(docptr, ecc_buf); + ecc_buf[7] = 0; /* clear the "page written" flag */ + } + + /* write user-supplied bytes to oob */ + else { + writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); + write_nop(docptr); + memcpy(ecc_buf, &nand->oob_poi[8], 8); + } + + docg4_write_buf16(mtd, ecc_buf, 8); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return 0; +} + +static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int oob_required) +{ + return write_page(mtd, nand, buf, false); +} + +static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int oob_required) +{ + return write_page(mtd, nand, buf, true); +} + +static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + /* + * Writing oob-only is not really supported, because MLC nand must write + * oob bytes at the same time as page data. Nonetheless, we save the + * oob buffer contents here, and then write it along with the page data + * if the same page is subsequently written. This allows user space + * utilities that write the oob data prior to the page data to work + * (e.g., nandwrite). The disdvantage is that, if the intention was to + * write oob only, the operation is quietly ignored. Also, oob can get + * corrupted if two concurrent processes are running nandwrite. + */ + + /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ + struct docg4_priv *doc = nand->priv; + doc->oob_page = page; + memcpy(doc->oob_buf, nand->oob_poi, 16); + return 0; +} + +static int __init read_factory_bbt(struct mtd_info *mtd) +{ + /* + * The device contains a read-only factory bad block table. Read it and + * update the memory-based bbt accordingly. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); + uint8_t *buf; + int i, block; + __u32 eccfailed_stats = mtd->ecc_stats.failed; + + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + read_page_prologue(mtd, g4_addr); + docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); + + /* + * If no memory-based bbt was created, exit. This will happen if module + * parameter ignore_badblocks is set. Then why even call this function? + * For an unknown reason, block erase always fails if it's the first + * operation after device power-up. The above read ensures it never is. + * Ugly, I know. + */ + if (nand->bbt == NULL) /* no memory-based bbt */ + goto exit; + + if (mtd->ecc_stats.failed > eccfailed_stats) { + /* + * Whoops, an ecc failure ocurred reading the factory bbt. + * It is stored redundantly, so we get another chance. + */ + eccfailed_stats = mtd->ecc_stats.failed; + docg4_read_page(mtd, nand, buf, 0, DOCG4_REDUNDANT_BBT_PAGE); + if (mtd->ecc_stats.failed > eccfailed_stats) { + dev_warn(doc->dev, + "The factory bbt could not be read!\n"); + goto exit; + } + } + + /* + * Parse factory bbt and update memory-based bbt. Factory bbt format is + * simple: one bit per block, block numbers increase left to right (msb + * to lsb). Bit clear means bad block. + */ + for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { + int bitnum; + unsigned long bits = ~buf[i]; + for_each_set_bit(bitnum, &bits, 8) { + int badblock = block + 7 - bitnum; + nand->bbt[badblock / 4] |= + 0x03 << ((badblock % 4) * 2); + mtd->ecc_stats.badblocks++; + dev_notice(doc->dev, "factory-marked bad block: %d\n", + badblock); + } + } + exit: + kfree(buf); + return 0; +} + +static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + /* + * Mark a block as bad. Bad blocks are marked in the oob area of the + * first page of the block. The default scan_bbt() in the nand + * infrastructure code works fine for building the memory-based bbt + * during initialization, as does the nand infrastructure function that + * checks if a block is bad by reading the bbt. This function replaces + * the nand default because writes to oob-only are not supported. + */ + + int ret, i; + uint8_t *buf; + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + struct nand_bbt_descr *bbtd = nand->badblock_pattern; + int page = (int)(ofs >> nand->page_shift); + uint32_t g4_addr = mtd_to_docg4_address(page, 0); + + dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs); + + if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) + dev_warn(doc->dev, "%s: ofs %llx not start of block!\n", + __func__, ofs); + + /* allocate blank buffer for page data */ + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* write bit-wise negation of pattern to oob buffer */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + for (i = 0; i < bbtd->len; i++) + nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; + + /* write first page of block */ + write_page_prologue(mtd, g4_addr); + docg4_write_page(mtd, nand, buf, 1); + ret = pageprog(mtd); + + kfree(buf); + + return ret; +} + +static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + /* only called when module_param ignore_badblocks is set */ + return 0; +} + +static int docg4_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* + * Put the device into "deep power-down" mode. Note that CE# must be + * deasserted for this to take effect. The xscale, e.g., can be + * configured to float this signal when the processor enters power-down, + * and a suitable pull-up ensures its deassertion. + */ + + int i; + uint8_t pwr_down; + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* poll the register that tells us we're ready to go to sleep */ + for (i = 0; i < 10; i++) { + pwr_down = readb(docptr + DOC_POWERMODE); + if (pwr_down & DOC_POWERDOWN_READY) + break; + usleep_range(1000, 4000); + } + + if (pwr_down & DOC_POWERDOWN_READY) { + dev_err(doc->dev, "suspend failed; " + "timeout polling DOC_POWERDOWN_READY\n"); + return -EIO; + } + + writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + write_nop(docptr); + + return 0; +} + +static int docg4_resume(struct platform_device *pdev) +{ + + /* + * Exit power-down. Twelve consecutive reads of the address below + * accomplishes this, assuming CE# has been asserted. + */ + + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + int i; + + dev_dbg(doc->dev, "%s...\n", __func__); + + for (i = 0; i < 12; i++) + readb(docptr + 0x1fff); + + return 0; +} + +static void __init init_mtd_structs(struct mtd_info *mtd) +{ + /* initialize mtd and nand data structures */ + + /* + * Note that some of the following initializations are not usually + * required within a nand driver because they are performed by the nand + * infrastructure code as part of nand_scan(). In this case they need + * to be initialized here because we skip call to nand_scan_ident() (the + * first half of nand_scan()). The call to nand_scan_ident() is skipped + * because for this device the chip id is not read in the manner of a + * standard nand device. Unfortunately, nand_scan_ident() does other + * things as well, such as call nand_set_defaults(). + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + + mtd->size = DOCG4_CHIP_SIZE; + mtd->name = "Msys_Diskonchip_G4"; + mtd->writesize = DOCG4_PAGE_SIZE; + mtd->erasesize = DOCG4_BLOCK_SIZE; + mtd->oobsize = DOCG4_OOB_SIZE; + nand->chipsize = DOCG4_CHIP_SIZE; + nand->chip_shift = DOCG4_CHIP_SHIFT; + nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT; + nand->chip_delay = 20; + nand->page_shift = DOCG4_PAGE_SHIFT; + nand->pagemask = 0x3ffff; + nand->badblockpos = NAND_LARGE_BADBLOCK_POS; + nand->badblockbits = 8; + nand->ecc.layout = &docg4_oobinfo; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = DOCG4_PAGE_SIZE; + nand->ecc.prepad = 8; + nand->ecc.bytes = 8; + nand->ecc.strength = DOCG4_T; + nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; + nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; + nand->controller = &nand->hwcontrol; + spin_lock_init(&nand->controller->lock); + init_waitqueue_head(&nand->controller->wq); + + /* methods */ + nand->cmdfunc = docg4_command; + nand->waitfunc = docg4_wait; + nand->select_chip = docg4_select_chip; + nand->read_byte = docg4_read_byte; + nand->block_markbad = docg4_block_markbad; + nand->read_buf = docg4_read_buf; + nand->write_buf = docg4_write_buf16; + nand->erase = docg4_erase_block; + nand->ecc.read_page = docg4_read_page; + nand->ecc.write_page = docg4_write_page; + nand->ecc.read_page_raw = docg4_read_page_raw; + nand->ecc.write_page_raw = docg4_write_page_raw; + nand->ecc.read_oob = docg4_read_oob; + nand->ecc.write_oob = docg4_write_oob; + + /* + * The way the nand infrastructure code is written, a memory-based bbt + * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt, + * nand->block_bad() is used. So when ignoring bad blocks, we skip the + * scan and define a dummy block_bad() which always returns 0. + */ + if (ignore_badblocks) { + nand->options |= NAND_SKIP_BBTSCAN; + nand->block_bad = docg4_block_neverbad; + } + +} + +static int __init read_id_reg(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t id1, id2; + + /* check for presence of g4 chip by reading id registers */ + id1 = readw(docptr + DOC_CHIPID); + id1 = readw(docptr + DOCG4_MYSTERY_REG); + id2 = readw(docptr + DOC_CHIPID_INV); + id2 = readw(docptr + DOCG4_MYSTERY_REG); + + if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) { + dev_info(doc->dev, + "NAND device: 128MiB Diskonchip G4 detected\n"); + return 0; + } + + return -ENODEV; +} + +static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; + +static int __init probe_docg4(struct platform_device *pdev) +{ + struct mtd_info *mtd; + struct nand_chip *nand; + void __iomem *virtadr; + struct docg4_priv *doc; + int len, retval; + struct resource *r; + struct device *dev = &pdev->dev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(dev, "no io memory resource defined!\n"); + return -ENODEV; + } + + virtadr = ioremap(r->start, resource_size(r)); + if (!virtadr) { + dev_err(dev, "Diskonchip ioremap failed: %pR\n", r); + return -EIO; + } + + len = sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct docg4_priv); + mtd = kzalloc(len, GFP_KERNEL); + if (mtd == NULL) { + retval = -ENOMEM; + goto fail; + } + nand = (struct nand_chip *) (mtd + 1); + doc = (struct docg4_priv *) (nand + 1); + mtd->priv = nand; + nand->priv = doc; + mtd->owner = THIS_MODULE; + doc->virtadr = virtadr; + doc->dev = dev; + + init_mtd_structs(mtd); + + /* initialize kernel bch algorithm */ + doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); + if (doc->bch == NULL) { + retval = -EINVAL; + goto fail; + } + + platform_set_drvdata(pdev, doc); + + reset(mtd); + retval = read_id_reg(mtd); + if (retval == -ENODEV) { + dev_warn(dev, "No diskonchip G4 device found.\n"); + goto fail; + } + + retval = nand_scan_tail(mtd); + if (retval) + goto fail; + + retval = read_factory_bbt(mtd); + if (retval) + goto fail; + + retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + if (retval) + goto fail; + + doc->mtd = mtd; + return 0; + + fail: + iounmap(virtadr); + if (mtd) { + /* re-declarations avoid compiler warning */ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + nand_release(mtd); /* deletes partitions and mtd devices */ + free_bch(doc->bch); + kfree(mtd); + } + + return retval; +} + +static int __exit cleanup_docg4(struct platform_device *pdev) +{ + struct docg4_priv *doc = platform_get_drvdata(pdev); + nand_release(doc->mtd); + free_bch(doc->bch); + kfree(doc->mtd); + iounmap(doc->virtadr); + return 0; +} + +static struct platform_driver docg4_driver = { + .driver = { + .name = "docg4", + .owner = THIS_MODULE, + }, + .suspend = docg4_suspend, + .resume = docg4_resume, + .remove = __exit_p(cleanup_docg4), +}; + +module_platform_driver_probe(docg4_driver, probe_docg4); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Dunn"); +MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver"); diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c deleted file mode 100644 index 86366bfba9f..00000000000 --- a/drivers/mtd/nand/edb7312.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * drivers/mtd/nand/edb7312.c - * - * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) - * - * Derived from drivers/mtd/nand/autcpu12.c - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * CLEP7312 board which utilizes the Toshiba TC58V64AFT part. This is - * a 64Mibit (8MiB x 8 bits) NAND flash device. - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <mach/hardware.h> /* for CLPS7111_VIRT_BASE */ -#include <asm/sizes.h> -#include <asm/hardware/clps7111.h> - -/* - * MTD structure for EDB7312 board - */ -static struct mtd_info *ep7312_mtd = NULL; - -/* - * Values specific to the EDB7312 board (used with EP7312 processor) - */ -#define EP7312_FIO_PBASE 0x10000000 /* Phys address of flash */ -#define EP7312_PXDR 0x0001 /* - * IO offset to Port B data register - * where the CLE, ALE and NCE pins - * are wired to. - */ -#define EP7312_PXDDR 0x0041 /* - * IO offset to Port B data direction - * register so we can control the IO - * lines. - */ - -/* - * Module stuff - */ - -static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE; -static void __iomem *ep7312_pxdr = (void __iomem *)EP7312_PXDR; -static void __iomem *ep7312_pxddr = (void __iomem *)EP7312_PXDDR; - -#ifdef CONFIG_MTD_PARTITIONS -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info[] = { - {.name = "EP7312 Nand Flash", - .offset = 0, - .size = 8 * 1024 * 1024} -}; - -#define NUM_PARTITIONS 1 - -#endif - -/* - * hardware specific access to control-lines - * - * NAND_NCE: bit 0 -> bit 6 (bit 7 = 1) - * NAND_CLE: bit 1 -> bit 4 - * NAND_ALE: bit 2 -> bit 5 - */ -static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - unsigned char bits = 0x80; - - bits |= (ctrl & (NAND_CLE | NAND_ALE)) << 3; - bits |= (ctrl & NAND_NCE) ? 0x00 : 0x40; - - clps_writeb((clps_readb(ep7312_pxdr) & 0xF0) | bits, - ep7312_pxdr); - } - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * read device ready pin - */ -static int ep7312_device_ready(struct mtd_info *mtd) -{ - return 1; -} - -#ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - -/* - * Main initialization routine - */ -static int __init ep7312_init(void) -{ - struct nand_chip *this; - const char *part_type = 0; - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - void __iomem *ep7312_fio_base; - - /* Allocate memory for MTD device structure and private data */ - ep7312_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!ep7312_mtd) { - printk("Unable to allocate EDB7312 NAND MTD device structure.\n"); - return -ENOMEM; - } - - /* map physical address */ - ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K); - if (!ep7312_fio_base) { - printk("ioremap EDB7312 NAND flash failed\n"); - kfree(ep7312_mtd); - return -EIO; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&ep7312_mtd[1]); - - /* Initialize structures */ - memset(ep7312_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - ep7312_mtd->priv = this; - ep7312_mtd->owner = THIS_MODULE; - - /* - * Set GPIO Port B control register so that the pins are configured - * to be outputs for controlling the NAND flash. - */ - clps_writeb(0xf0, ep7312_pxddr); - - /* insert callbacks */ - this->IO_ADDR_R = ep7312_fio_base; - this->IO_ADDR_W = ep7312_fio_base; - this->cmd_ctrl = ep7312_hwcontrol; - this->dev_ready = ep7312_device_ready; - /* 15 us command delay time */ - this->chip_delay = 15; - - /* Scan to find existence of the device */ - if (nand_scan(ep7312_mtd, 1)) { - iounmap((void *)ep7312_fio_base); - kfree(ep7312_mtd); - return -ENXIO; - } -#ifdef CONFIG_MTD_PARTITIONS - ep7312_mtd->name = "edb7312-nand"; - mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, &mtd_parts, 0); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (mtd_parts_nb == 0) { - mtd_parts = partition_info; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - - /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ep7312_mtd, mtd_parts, mtd_parts_nb); - - /* Return happy */ - return 0; -} - -module_init(ep7312_init); - -/* - * Clean up routine - */ -static void __exit ep7312_cleanup(void) -{ - struct nand_chip *this = (struct nand_chip *)&ep7312_mtd[1]; - - /* Release resources, unregister device */ - nand_release(ap7312_mtd); - - /* Release io resource */ - iounmap(this->IO_ADDR_R); - - /* Free the MTD device structure */ - kfree(ep7312_mtd); -} - -module_exit(ep7312_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); -MODULE_DESCRIPTION("MTD map driver for Cogent EDB7312 board"); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 7a13d42cbab..545a5c002f0 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -24,10 +24,10 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -59,7 +59,7 @@ struct fsl_elbc_mtd { unsigned int fmr; /* FCM Flash Mode Register value */ }; -/* Freescale eLBC FCM controller infomation */ +/* Freescale eLBC FCM controller information */ struct fsl_elbc_fcm_ctrl { struct nand_hw_control controller; @@ -75,7 +75,7 @@ struct fsl_elbc_fcm_ctrl { unsigned int use_mdr; /* Non zero if the MDR is to be set */ unsigned int oob; /* Non zero if operating on OOB data */ unsigned int counter; /* counter for the initializations */ - char *oob_poi; /* Place to write ECC after read back */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ }; /* These map to the positions used by the FCM hardware ECC generator */ @@ -109,20 +109,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { }; /* - * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset - * 1, so we have to adjust bad block pattern. This pattern should be used for - * x8 chips only. So far hardware does not support x16 chips anyway. - */ -static u8 scan_ff_pattern[] = { 0xff, }; - -static struct nand_bbt_descr largepage_memorybased = { - .options = 0, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern, -}; - -/* * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, * interfere with ECC positions, that's why we implement our own descriptors. * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. @@ -167,15 +153,22 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) elbc_fcm_ctrl->page = page_addr; - out_be32(&lbc->fbar, - page_addr >> (chip->phys_erase_shift - chip->page_shift)); - if (priv->page_size) { + /* + * large page size chip : FPAR[PI] save the lowest 6 bits, + * FBAR[BLK] save the other bits. + */ + out_be32(&lbc->fbar, page_addr >> 6); out_be32(&lbc->fpar, ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | (oob ? FPAR_LP_MS : 0) | column); buf_num = (page_addr & 1) << 2; } else { + /* + * small page size chip : FPAR[PI] save the lowest 5 bits, + * FBAR[BLK] save the other bits. + */ + out_be32(&lbc->fbar, page_addr >> 5); out_be32(&lbc->fpar, ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | (oob ? FPAR_SP_MS : 0) | column); @@ -244,6 +237,32 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) return -EIO; } + if (chip->ecc.mode != NAND_ECC_HW) + return 0; + + elbc_fcm_ctrl->max_bitflips = 0; + + if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { + uint32_t lteccr = in_be32(&lbc->lteccr); + /* + * if command was a full page read and the ELBC + * has the LTECCR register, then bits 12-15 (ppc order) of + * LTECCR indicates which 512 byte sub-pages had fixed errors. + * bits 28-31 are uncorrectable errors, marked elsewhere. + * for small page nand only 1 bit is used. + * if the ELBC doesn't have the lteccr register it reads 0 + * FIXME: 4 bits can be corrected on NANDs with 2k pages, so + * count the number of sub-pages with bitflips and update + * ecc_stats.corrected accordingly. + */ + if (lteccr & 0x000F000F) + out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ + if (lteccr & 0x000F0000) { + mtd->ecc_stats.corrected++; + elbc_fcm_ctrl->max_bitflips = 1; + } + } + return 0; } @@ -331,20 +350,22 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, fsl_elbc_run_command(mtd); return; - /* READID must read all 5 possible bytes while CEB is active */ case NAND_CMD_READID: - dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + case NAND_CMD_PARAM: + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command); out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_UA << FIR_OP1_SHIFT) | (FIR_OP_RBW << FIR_OP2_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); - /* 5 bytes for manuf, device and exts */ - out_be32(&lbc->fbcr, 5); - elbc_fcm_ctrl->read_bytes = 5; + out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&lbc->fbcr, 256); + elbc_fcm_ctrl->read_bytes = 256; elbc_fcm_ctrl->use_mdr = 1; - elbc_fcm_ctrl->mdr = 0; - + elbc_fcm_ctrl->mdr = column; set_addr(mtd, 0, 0, 0); fsl_elbc_run_command(mtd); return; @@ -389,9 +410,17 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, page_addr, column); elbc_fcm_ctrl->column = column; - elbc_fcm_ctrl->oob = 0; elbc_fcm_ctrl->use_mdr = 1; + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + elbc_fcm_ctrl->oob = 1; + } else { + WARN_ON(column != 0); + elbc_fcm_ctrl->oob = 0; + } + fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT); @@ -416,16 +445,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, (FIR_OP_CW1 << FIR_OP6_SHIFT) | (FIR_OP_RS << FIR_OP7_SHIFT)); - if (column >= mtd->writesize) { + if (elbc_fcm_ctrl->oob) /* OOB area --> READOOB */ - column -= mtd->writesize; fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; - elbc_fcm_ctrl->oob = 1; - } else { - WARN_ON(column != 0); + else /* First 256 bytes --> READ0 */ fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; - } } out_be32(&lbc->fcr, fcr); @@ -435,7 +460,6 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ case NAND_CMD_PAGEPROG: { - int full_page; dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " "writing %d bytes.\n", elbc_fcm_ctrl->index); @@ -445,34 +469,13 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, * write so the HW generates the ECC. */ if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 || - elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) { - out_be32(&lbc->fbcr, elbc_fcm_ctrl->index); - full_page = 0; - } else { + elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) + out_be32(&lbc->fbcr, + elbc_fcm_ctrl->index - elbc_fcm_ctrl->column); + else out_be32(&lbc->fbcr, 0); - full_page = 1; - } fsl_elbc_run_command(mtd); - - /* Read back the page in order to fill in the ECC for the - * caller. Is this really needed? - */ - if (full_page && elbc_fcm_ctrl->oob_poi) { - out_be32(&lbc->fbcr, 3); - set_addr(mtd, 6, page_addr, 1); - - elbc_fcm_ctrl->read_bytes = mtd->writesize + 9; - - fsl_elbc_do_read(chip, 1); - fsl_elbc_run_command(mtd); - - memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6, - &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3); - elbc_fcm_ctrl->index += 3; - } - - elbc_fcm_ctrl->oob_poi = NULL; return; } @@ -597,41 +600,6 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) len, avail); } -/* - * Verify buffer against the FCM Controller Data Buffer - */ -static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *chip = mtd->priv; - struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; - int i; - - if (len < 0) { - dev_err(priv->dev, "write_buf of %d bytes", len); - return -EINVAL; - } - - if ((unsigned int)len > - elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) { - dev_err(priv->dev, - "verify_buf beyond end of buffer " - "(%d requested, %u available)\n", - len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index); - - elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes; - return -EINVAL; - } - - for (i = 0; i < len; i++) - if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i]) - != buf[i]) - break; - - elbc_fcm_ctrl->index += len; - return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO; -} - /* This function is called after Program and Erase Operations to * check for success or failure. */ @@ -664,9 +632,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) if (chip->pagemask & 0xff000000) al++; - /* add to ECCM mode set in fsl_elbc_init */ - priv->fmr |= (12 << FMR_CWTO_SHIFT) | /* Timeout > 12 ms */ - (al << FMR_AL_SHIFT); + priv->fmr |= al << FMR_AL_SHIFT; dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n", chip->numchips); @@ -684,8 +650,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->page_shift); dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", chip->phys_erase_shift); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n", - chip->ecclayout); dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", chip->ecc.mode); dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", @@ -719,7 +683,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; - chip->badblock_pattern = &largepage_memorybased; } } else { dev_err(priv->dev, @@ -731,34 +694,46 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) return 0; } -static int fsl_elbc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int page) +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + fsl_elbc_read_buf(mtd, buf, mtd->writesize); - fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) mtd->ecc_stats.failed++; - return 0; + return elbc_fcm_ctrl->max_bitflips; } /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_elbc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { - struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, uint32_t data_len, + const uint8_t *buf, int oob_required) +{ fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - elbc_fcm_ctrl->oob_poi = chip->oob_poi; + return 0; } static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) @@ -774,15 +749,16 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) priv->mtd.priv = chip; priv->mtd.owner = THIS_MODULE; - /* Set the ECCM according to the settings in bootloader.*/ - priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM; + /* set timeout to maximum */ + priv->fmr = 15 << FMR_CWTO_SHIFT; + if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS) + priv->fmr |= FMR_ECCM; /* fill in nand_chip structure */ /* set up function call table */ chip->read_byte = fsl_elbc_read_byte; chip->write_buf = fsl_elbc_write_buf; chip->read_buf = fsl_elbc_read_buf; - chip->verify_buf = fsl_elbc_verify_buf; chip->select_chip = fsl_elbc_select_chip; chip->cmdfunc = fsl_elbc_cmdfunc; chip->waitfunc = fsl_elbc_wait; @@ -791,14 +767,14 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->bbt_md = &bbt_mirror_descr; /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | - NAND_USE_FLASH_BBT; + chip->bbt_options = NAND_BBT_USE_FLASH; chip->controller = &elbc_fcm_ctrl->controller; chip->priv = priv; chip->ecc.read_page = fsl_elbc_read_page; chip->ecc.write_page = fsl_elbc_write_page; + chip->ecc.write_subpage = fsl_elbc_write_subpage; /* If CS Base Register selects full hardware ECC then use it */ if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == @@ -809,6 +785,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; chip->ecc.size = 512; chip->ecc.bytes = 3; + chip->ecc.strength = 1; } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; @@ -829,29 +806,26 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) elbc_fcm_ctrl->chips[priv->bank] = NULL; kfree(priv); - kfree(elbc_fcm_ctrl); return 0; } static DEFINE_MUTEX(fsl_elbc_nand_mutex); -static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) +static int fsl_elbc_nand_probe(struct platform_device *pdev) { struct fsl_lbc_regs __iomem *lbc; struct fsl_elbc_mtd *priv; struct resource res; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl; - -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] - = { "cmdlinepart", "RedBoot", NULL }; - struct mtd_partition *parts; -#endif + = { "cmdlinepart", "RedBoot", "ofpart", NULL }; int ret; int bank; struct device *dev; struct device_node *node = pdev->dev.of_node; + struct mtd_part_parser_data ppdata; + ppdata.of_node = pdev->dev.of_node; if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; lbc = fsl_lbc_ctrl_dev->regs; @@ -886,7 +860,6 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) if (!fsl_lbc_ctrl_dev->nand) { elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL); if (!elbc_fcm_ctrl) { - dev_err(dev, "failed to allocate memory\n"); mutex_unlock(&fsl_elbc_nand_mutex); ret = -ENOMEM; goto err; @@ -904,7 +877,8 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) elbc_fcm_ctrl->chips[bank] = priv; priv->bank = bank; priv->ctrl = fsl_lbc_ctrl_dev; - priv->dev = dev; + priv->dev = &pdev->dev; + dev_set_drvdata(priv->dev, priv); priv->vbase = ioremap(res.start, resource_size(&res)); if (!priv->vbase) { @@ -913,7 +887,7 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) goto err; } - priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", (unsigned)res.start); + priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); if (!priv->mtd.name) { ret = -ENOMEM; goto err; @@ -935,26 +909,10 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) if (ret) goto err; -#ifdef CONFIG_MTD_PARTITIONS /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ - ret = parse_mtd_partitions(&priv->mtd, part_probe_types, &parts, 0); - if (ret < 0) - goto err; - -#ifdef CONFIG_MTD_OF_PARTS - if (ret == 0) { - ret = of_mtd_parse_partitions(priv->dev, node, &parts); - if (ret < 0) - goto err; - } -#endif - - if (ret > 0) - add_mtd_partitions(&priv->mtd, parts, ret); - else -#endif - add_mtd_device(&priv->mtd); + mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata, + NULL, 0); printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n", (unsigned long long)res.start, priv->bank); @@ -967,11 +925,10 @@ err: static int fsl_elbc_nand_remove(struct platform_device *pdev) { - int i; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; - for (i = 0; i < MAX_BANKS; i++) - if (elbc_fcm_ctrl->chips[i]) - fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]); + struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev); + + fsl_elbc_chip_remove(priv); mutex_lock(&fsl_elbc_nand_mutex); elbc_fcm_ctrl->counter--; @@ -1000,18 +957,7 @@ static struct platform_driver fsl_elbc_nand_driver = { .remove = fsl_elbc_nand_remove, }; -static int __init fsl_elbc_nand_init(void) -{ - return platform_driver_register(&fsl_elbc_nand_driver); -} - -static void __exit fsl_elbc_nand_exit(void) -{ - platform_driver_unregister(&fsl_elbc_nand_driver); -} - -module_init(fsl_elbc_nand_init); -module_exit(fsl_elbc_nand_exit); +module_platform_driver(fsl_elbc_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Freescale"); diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c new file mode 100644 index 00000000000..2338124dd05 --- /dev/null +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -0,0 +1,1181 @@ +/* + * Freescale Integrated Flash Controller NAND driver + * + * Copyright 2011-2012 Freescale Semiconductor, Inc + * + * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com> + * + * 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/of_address.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/fsl_ifc.h> + +#define FSL_IFC_V1_1_0 0x01010000 +#define ERR_BYTE 0xFF /* Value returned for read + bytes when read failed */ +#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait + for IFC NAND Machine */ + +struct fsl_ifc_ctrl; + +/* mtd information per set */ +struct fsl_ifc_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct fsl_ifc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ + u8 __iomem *vbase; /* Chip select base virtual address */ +}; + +/* overview of the fsl ifc controller */ +struct fsl_ifc_nand_ctrl { + struct nand_hw_control controller; + struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; + + void __iomem *addr; /* Address of assigned IFC buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes;/* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int eccread; /* Non zero for a full-page ECC read */ + unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ +}; + +static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; + +/* 512-byte page with 4-bit ECC, 8-bit */ +static struct nand_ecclayout oob_512_8bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {0, 5}, {6, 2} }, +}; + +/* 512-byte page with 4-bit ECC, 16-bit */ +static struct nand_ecclayout oob_512_16bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 6}, }, +}; + +/* 2048-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_2048_ecc4 = { + .eccbytes = 32, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + }, + .oobfree = { {2, 6}, {40, 24} }, +}; + +/* 4096-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_4096_ecc4 = { + .eccbytes = 64, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + }, + .oobfree = { {2, 6}, {72, 56} }, +}; + +/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_4096_ecc8 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 82} }, +}; + +/* 8192-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_8192_ecc4 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 208} }, +}; + +/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_8192_ecc8 = { + .eccbytes = 256, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, + }, + .oobfree = { {2, 6}, {264, 80} }, +}; + +/* + * Generic flash bbt descriptors + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* + * Set up the IFC hardware block and page address fields, and the ifc nand + * structure addr field to point to the correct IFC buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + int buf_num; + + ifc_nand_ctrl->page = page_addr; + /* Program ROW0/COL0 */ + iowrite32be(page_addr, &ifc->ifc_nand.row0); + iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); + + buf_num = page_addr & priv->bufnum_mask; + + ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); + ifc_nand_ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ifc_nand_ctrl->index += mtd->writesize; +} + +static int is_blank(struct mtd_info *mtd, unsigned int bufnum) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2); + u32 __iomem *mainarea = (u32 __iomem *)addr; + u8 __iomem *oob = addr + mtd->writesize; + int i; + + for (i = 0; i < mtd->writesize / 4; i++) { + if (__raw_readl(&mainarea[i]) != 0xffffffff) + return 0; + } + + for (i = 0; i < chip->ecc.layout->eccbytes; i++) { + int pos = chip->ecc.layout->eccpos[i]; + + if (__raw_readb(&oob[pos]) != 0xff) + return 0; + } + + return 1; +} + +/* returns nonzero if entire page is blank */ +static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, + u32 *eccstat, unsigned int bufnum) +{ + u32 reg = eccstat[bufnum / 4]; + int errors; + + errors = (reg >> ((3 - bufnum % 4) * 8)) & 15; + + return errors; +} + +/* + * execute IFC NAND command and wait for it to complete + */ +static void fsl_ifc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + u32 eccstat[4]; + int i; + + /* set the chip select for NAND Transaction */ + iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT, + &ifc->ifc_nand.nand_csel); + + dev_vdbg(priv->dev, + "%s: fir0=%08x fcr0=%08x\n", + __func__, + ioread32be(&ifc->ifc_nand.nand_fir0), + ioread32be(&ifc->ifc_nand.nand_fcr0)); + + ctrl->nand_stat = 0; + + /* start read/write seq */ + iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + + /* wait for command complete flag or timeout */ + wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, + IFC_TIMEOUT_MSECS * HZ/1000); + + /* ctrl->nand_stat will be updated from IRQ context */ + if (!ctrl->nand_stat) + dev_err(priv->dev, "Controller is not responding\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) + dev_err(priv->dev, "NAND Flash Timeout Error\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) + dev_err(priv->dev, "NAND Flash Write Protect Error\n"); + + nctrl->max_bitflips = 0; + + if (nctrl->eccread) { + int errors; + int bufnum = nctrl->page & priv->bufnum_mask; + int sector = bufnum * chip->ecc.steps; + int sector_end = sector + chip->ecc.steps - 1; + + for (i = sector / 4; i <= sector_end / 4; i++) + eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]); + + for (i = sector; i <= sector_end; i++) { + errors = check_read_ecc(mtd, ctrl, eccstat, i); + + if (errors == 15) { + /* + * Uncorrectable error. + * OK only if the whole page is blank. + * + * We disable ECCER reporting due to... + * erratum IFC-A002770 -- so report it now if we + * see an uncorrectable error in ECCSTAT. + */ + if (!is_blank(mtd, bufnum)) + ctrl->nand_stat |= + IFC_NAND_EVTER_STAT_ECCER; + break; + } + + mtd->ecc_stats.corrected += errors; + nctrl->max_bitflips = max_t(unsigned int, + nctrl->max_bitflips, + errors); + } + + nctrl->eccread = 0; + } +} + +static void fsl_ifc_do_read(struct nand_chip *chip, + int oob, + struct mtd_info *mtd) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + + /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ + if (mtd->writesize > 512) { + iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); + + iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), + &ifc->ifc_nand.nand_fcr0); + } else { + iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); + + if (oob) + iowrite32be(NAND_CMD_READOOB << + IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + else + iowrite32be(NAND_CMD_READ0 << + IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + } +} + +/* cmdfunc send commands to the IFC NAND Machine */ +static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + + /* clear the read buffer */ + ifc_nand_ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ifc_nand_ctrl->index = 0; + + switch (command) { + /* READ0 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ0: + iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, 0, page_addr, 0); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ifc_nand_ctrl->index += column; + + if (chip->ecc.mode == NAND_ECC_HW) + ifc_nand_ctrl->eccread = 1; + + fsl_ifc_do_read(chip, 0, mtd); + fsl_ifc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, column, page_addr, 1); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_ifc_do_read(chip, 1, mtd); + fsl_ifc_run_command(mtd); + + return; + + case NAND_CMD_READID: + case NAND_CMD_PARAM: { + int timing = IFC_FIR_OP_RB; + if (command == NAND_CMD_PARAM) + timing = IFC_FIR_OP_RBCD; + + iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (timing << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + iowrite32be(column, &ifc->ifc_nand.row3); + + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + iowrite32be(256, &ifc->ifc_nand.nand_fbcr); + ifc_nand_ctrl->read_bytes = 256; + + set_addr(mtd, 0, 0, 0); + fsl_ifc_run_command(mtd); + return; + } + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); + + iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), + &ifc->ifc_nand.nand_fcr0); + + iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + ifc_nand_ctrl->read_bytes = 0; + fsl_ifc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 nand_fcr0; + ifc_nand_ctrl->column = column; + ifc_nand_ctrl->oob = 0; + + if (mtd->writesize > 512) { + nand_fcr0 = + (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); + + iowrite32be( + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be( + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), + &ifc->ifc_nand.nand_fir1); + } else { + nand_fcr0 = ((NAND_CMD_PAGEPROG << + IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_SEQIN << + IFC_NAND_FCR0_CMD2_SHIFT) | + (NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD3_SHIFT)); + + iowrite32be( + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be( + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP7_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), + &ifc->ifc_nand.nand_fir1); + + if (column >= mtd->writesize) + nand_fcr0 |= + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; + else + nand_fcr0 |= + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; + } + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + ifc_nand_ctrl->oob = 1; + } + iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0); + set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + if (ifc_nand_ctrl->oob) { + iowrite32be(ifc_nand_ctrl->index - + ifc_nand_ctrl->column, + &ifc->ifc_nand.nand_fbcr); + } else { + iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + } + + fsl_ifc_run_command(mtd); + return; + } + + case NAND_CMD_STATUS: + iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + iowrite32be(1, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + if (chip->options & NAND_BUSWIDTH_16) + setbits16(ifc_nand_ctrl->addr, NAND_STATUS_WP); + else + setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP); + return; + + case NAND_CMD_RESET: + iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, + &ifc->ifc_nand.nand_fir0); + iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + fsl_ifc_run_command(mtd); + return; + + default: + dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", + __func__, command); + } +} + +static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the IFC NAND Controller Data Buffer + */ +static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %u available)\n", + __func__, len, bufsize - ifc_nand_ctrl->index); + len = bufsize - ifc_nand_ctrl->index; + } + + memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len); + ifc_nand_ctrl->index += len; +} + +/* + * Read a byte from either the IFC hardware buffer + * read function for 8-bit buswidth + */ +static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + unsigned int offset; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { + offset = ifc_nand_ctrl->index++; + return in_8(ifc_nand_ctrl->addr + offset); + } + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read two bytes from the IFC hardware buffer + * read function for 16-bit buswith + */ +static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + uint16_t data; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { + data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); + ifc_nand_ctrl->index += 2; + return (uint8_t) data; + } + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read from the IFC Controller Data Buffer + */ +static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + int avail; + + if (len < 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + avail = min((unsigned int)len, + ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); + memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail); + ifc_nand_ctrl->index += avail; + + if (len > avail) + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %d available)\n", + __func__, len, avail); +} + +/* + * This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + u32 nand_fsr; + + /* Use READ_STATUS command, but wait for the device to be ready */ + iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + iowrite32be(1, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + return nand_fsr | NAND_STATUS_WP; +} + +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + + fsl_ifc_read_buf(mtd, buf, mtd->writesize); + if (oob_required) + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) + dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n"); + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + mtd->ecc_stats.failed++; + + return nctrl->max_bitflips; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + fsl_ifc_write_buf(mtd, buf, mtd->writesize); + fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + + dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, + chip->numchips); + dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, + chip->chipsize); + dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, + chip->pagemask); + dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__, + chip->chip_delay); + dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, + chip->badblockpos); + dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, + chip->chip_shift); + dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, + chip->page_shift); + dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, + chip->phys_erase_shift); + dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, + chip->ecc.mode); + dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, + chip->ecc.steps); + dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, + chip->ecc.bytes); + dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, + chip->ecc.total); + dev_dbg(priv->dev, "%s: nand->ecc.layout = %p\n", __func__, + chip->ecc.layout); + dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); + dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); + dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, + mtd->erasesize); + dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, + mtd->writesize); + dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, + mtd->oobsize); + + return 0; +} + +static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) +{ + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + uint32_t csor = 0, csor_8k = 0, csor_ext = 0; + uint32_t cs = priv->bank; + + /* Save CSOR and CSOR_ext */ + csor = ioread32be(&ifc->csor_cs[cs].csor); + csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext); + + /* chage PageSize 8K and SpareSize 1K*/ + csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; + iowrite32be(csor_8k, &ifc->csor_cs[cs].csor); + iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext); + + /* READID */ + iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + iowrite32be(0x0, &ifc->ifc_nand.row3); + + iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr); + + /* Program ROW0/COL0 */ + iowrite32be(0x0, &ifc->ifc_nand.row0); + iowrite32be(0x0, &ifc->ifc_nand.col0); + + /* set the chip select for NAND Transaction */ + iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); + + /* start read seq */ + iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + + /* wait for command complete flag or timeout */ + wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, + IFC_TIMEOUT_MSECS * HZ/1000); + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); + + /* Restore CSOR and CSOR_ext */ + iowrite32be(csor, &ifc->csor_cs[cs].csor); + iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext); +} + +static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) +{ + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct nand_chip *chip = &priv->chip; + struct nand_ecclayout *layout; + u32 csor, ver; + + /* Fill in fsl_ifc_mtd structure */ + priv->mtd.priv = chip; + priv->mtd.owner = THIS_MODULE; + + /* fill in nand_chip structure */ + /* set up function call table */ + if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) + chip->read_byte = fsl_ifc_read_byte16; + else + chip->read_byte = fsl_ifc_read_byte; + + chip->write_buf = fsl_ifc_write_buf; + chip->read_buf = fsl_ifc_read_buf; + chip->select_chip = fsl_ifc_select_chip; + chip->cmdfunc = fsl_ifc_cmdfunc; + chip->waitfunc = fsl_ifc_wait; + + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + + iowrite32be(0x0, &ifc->ifc_nand.ncfgr); + + /* set up nand options */ + chip->bbt_options = NAND_BBT_USE_FLASH; + chip->options = NAND_NO_SUBPAGE_WRITE; + + if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { + chip->read_byte = fsl_ifc_read_byte16; + chip->options |= NAND_BUSWIDTH_16; + } else { + chip->read_byte = fsl_ifc_read_byte; + } + + chip->controller = &ifc_nand_ctrl->controller; + chip->priv = priv; + + chip->ecc.read_page = fsl_ifc_read_page; + chip->ecc.write_page = fsl_ifc_write_page; + + csor = ioread32be(&ifc->csor_cs[priv->bank].csor); + + /* Hardware generates ECC per 512 Bytes */ + chip->ecc.size = 512; + chip->ecc.bytes = 8; + chip->ecc.strength = 4; + + switch (csor & CSOR_NAND_PGS_MASK) { + case CSOR_NAND_PGS_512: + if (chip->options & NAND_BUSWIDTH_16) { + layout = &oob_512_16bit_ecc4; + } else { + layout = &oob_512_8bit_ecc4; + + /* Avoid conflict with bad block marker */ + bbt_main_descr.offs = 0; + bbt_mirror_descr.offs = 0; + } + + priv->bufnum_mask = 15; + break; + + case CSOR_NAND_PGS_2K: + layout = &oob_2048_ecc4; + priv->bufnum_mask = 3; + break; + + case CSOR_NAND_PGS_4K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_4096_ecc4; + } else { + layout = &oob_4096_ecc8; + chip->ecc.bytes = 16; + chip->ecc.strength = 8; + } + + priv->bufnum_mask = 1; + break; + + case CSOR_NAND_PGS_8K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_8192_ecc4; + } else { + layout = &oob_8192_ecc8; + chip->ecc.bytes = 16; + chip->ecc.strength = 8; + } + + priv->bufnum_mask = 0; + break; + + default: + dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); + return -ENODEV; + } + + /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ + if (csor & CSOR_NAND_ECC_DEC_EN) { + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.layout = layout; + } else { + chip->ecc.mode = NAND_ECC_SOFT; + } + + ver = ioread32be(&ifc->ifc_rev); + if (ver == FSL_IFC_V1_1_0) + fsl_ifc_sram_init(priv); + + return 0; +} + +static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) +{ + nand_release(&priv->mtd); + + kfree(priv->mtd.name); + + if (priv->vbase) + iounmap(priv->vbase); + + ifc_nand_ctrl->chips[priv->bank] = NULL; + + return 0; +} + +static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, + phys_addr_t addr) +{ + u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr); + + if (!(cspr & CSPR_V)) + return 0; + if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) + return 0; + + return (cspr & CSPR_BA) == convert_ifc_address(addr); +} + +static DEFINE_MUTEX(fsl_ifc_nand_mutex); + +static int fsl_ifc_nand_probe(struct platform_device *dev) +{ + struct fsl_ifc_regs __iomem *ifc; + struct fsl_ifc_mtd *priv; + struct resource res; + static const char *part_probe_types[] + = { "cmdlinepart", "RedBoot", "ofpart", NULL }; + int ret; + int bank; + struct device_node *node = dev->dev.of_node; + struct mtd_part_parser_data ppdata; + + ppdata.of_node = dev->dev.of_node; + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + return -ENODEV; + ifc = fsl_ifc_ctrl_dev->regs; + + /* get, allocate and map the memory resource */ + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(&dev->dev, "%s: failed to get resource\n", __func__); + return ret; + } + + /* find which chip select it is connected to */ + for (bank = 0; bank < FSL_IFC_BANK_COUNT; bank++) { + if (match_bank(ifc, bank, res.start)) + break; + } + + if (bank >= FSL_IFC_BANK_COUNT) { + dev_err(&dev->dev, "%s: address did not match any chip selects\n", + __func__); + return -ENODEV; + } + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_lock(&fsl_ifc_nand_mutex); + if (!fsl_ifc_ctrl_dev->nand) { + ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); + if (!ifc_nand_ctrl) { + mutex_unlock(&fsl_ifc_nand_mutex); + return -ENOMEM; + } + + ifc_nand_ctrl->read_bytes = 0; + ifc_nand_ctrl->index = 0; + ifc_nand_ctrl->addr = NULL; + fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; + + spin_lock_init(&ifc_nand_ctrl->controller.lock); + init_waitqueue_head(&ifc_nand_ctrl->controller.wq); + } else { + ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; + } + mutex_unlock(&fsl_ifc_nand_mutex); + + ifc_nand_ctrl->chips[bank] = priv; + priv->bank = bank; + priv->ctrl = fsl_ifc_ctrl_dev; + priv->dev = &dev->dev; + + priv->vbase = ioremap(res.start, resource_size(&res)); + if (!priv->vbase) { + dev_err(priv->dev, "%s: failed to map chip region\n", __func__); + ret = -ENOMEM; + goto err; + } + + dev_set_drvdata(priv->dev, priv); + + iowrite32be(IFC_NAND_EVTER_EN_OPC_EN | + IFC_NAND_EVTER_EN_FTOER_EN | + IFC_NAND_EVTER_EN_WPER_EN, + &ifc->ifc_nand.nand_evter_en); + + /* enable NAND Machine Interrupts */ + iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN | + IFC_NAND_EVTER_INTR_FTOERIR_EN | + IFC_NAND_EVTER_INTR_WPERIR_EN, + &ifc->ifc_nand.nand_evter_intr_en); + priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); + if (!priv->mtd.name) { + ret = -ENOMEM; + goto err; + } + + ret = fsl_ifc_chip_init(priv); + if (ret) + goto err; + + ret = nand_scan_ident(&priv->mtd, 1, NULL); + if (ret) + goto err; + + ret = fsl_ifc_chip_init_tail(&priv->mtd); + if (ret) + goto err; + + ret = nand_scan_tail(&priv->mtd); + if (ret) + goto err; + + /* First look for RedBoot table or partitions on the command + * line, these take precedence over device tree information */ + mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata, + NULL, 0); + + dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", + (unsigned long long)res.start, priv->bank); + return 0; + +err: + fsl_ifc_chip_remove(priv); + return ret; +} + +static int fsl_ifc_nand_remove(struct platform_device *dev) +{ + struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); + + fsl_ifc_chip_remove(priv); + + mutex_lock(&fsl_ifc_nand_mutex); + ifc_nand_ctrl->counter--; + if (!ifc_nand_ctrl->counter) { + fsl_ifc_ctrl_dev->nand = NULL; + kfree(ifc_nand_ctrl); + } + mutex_unlock(&fsl_ifc_nand_mutex); + + return 0; +} + +static const struct of_device_id fsl_ifc_nand_match[] = { + { + .compatible = "fsl,ifc-nand", + }, + {} +}; + +static struct platform_driver fsl_ifc_nand_driver = { + .driver = { + .name = "fsl,ifc-nand", + .owner = THIS_MODULE, + .of_match_table = fsl_ifc_nand_match, + }, + .probe = fsl_ifc_nand_probe, + .remove = fsl_ifc_nand_remove, +}; + +module_platform_driver(fsl_ifc_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale"); +MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index efdcca94ce5..4d203e84e8c 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -18,6 +18,7 @@ #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <linux/mtd/mtd.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> #include <linux/io.h> @@ -33,10 +34,7 @@ struct fsl_upm_nand { struct mtd_info mtd; struct nand_chip chip; int last_ctrl; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; -#endif - struct fsl_upm upm; uint8_t upm_addr_offset; uint8_t upm_cmd_offset; @@ -155,15 +153,13 @@ static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) fun_wait_rnb(fun); } -static int __devinit fun_chip_init(struct fsl_upm_nand *fun, - const struct device_node *upm_np, - const struct resource *io_res) +static int fun_chip_init(struct fsl_upm_nand *fun, + const struct device_node *upm_np, + const struct resource *io_res) { int ret; struct device_node *flash_np; -#ifdef CONFIG_MTD_PARTITIONS - static const char *part_types[] = { "cmdlinepart", NULL, }; -#endif + struct mtd_part_parser_data ppdata; fun->chip.IO_ADDR_R = fun->io_base; fun->chip.IO_ADDR_W = fun->io_base; @@ -197,28 +193,16 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, if (ret) goto err; -#ifdef CONFIG_MTD_PARTITIONS - ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0); - -#ifdef CONFIG_MTD_OF_PARTS - if (ret == 0) { - ret = of_mtd_parse_partitions(fun->dev, flash_np, &fun->parts); - if (ret < 0) - goto err; - } -#endif - if (ret > 0) - ret = add_mtd_partitions(&fun->mtd, fun->parts, ret); - else -#endif - ret = add_mtd_device(&fun->mtd); + ppdata.of_node = flash_np; + ret = mtd_device_parse_register(&fun->mtd, NULL, &ppdata, NULL, 0); err: of_node_put(flash_np); + if (ret) + kfree(fun->mtd.name); return ret; } -static int __devinit fun_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) +static int fun_probe(struct platform_device *ofdev) { struct fsl_upm_nand *fun; struct resource io_res; @@ -335,7 +319,7 @@ err1: return ret; } -static int __devexit fun_remove(struct platform_device *ofdev) +static int fun_remove(struct platform_device *ofdev) { struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); int i; @@ -360,27 +344,17 @@ static const struct of_device_id of_fun_match[] = { }; MODULE_DEVICE_TABLE(of, of_fun_match); -static struct of_platform_driver of_fun_driver = { +static struct platform_driver of_fun_driver = { .driver = { .name = "fsl,upm-nand", .owner = THIS_MODULE, .of_match_table = of_fun_match, }, .probe = fun_probe, - .remove = __devexit_p(fun_remove), + .remove = fun_remove, }; -static int __init fun_module_init(void) -{ - return of_register_platform_driver(&of_fun_driver); -} -module_init(fun_module_init); - -static void __exit fun_module_exit(void) -{ - of_unregister_platform_driver(&of_fun_driver); -} -module_exit(fun_module_exit); +module_platform_driver(of_fun_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 205b10b9f9b..1550692973d 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -17,6 +17,10 @@ */ #include <linux/clk.h> +#include <linux/completion.h> +#include <linux/dmaengine.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/init.h> #include <linux/module.h> @@ -27,6 +31,7 @@ #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/mtd/partitions.h> #include <linux/io.h> #include <linux/slab.h> @@ -34,7 +39,7 @@ #include <linux/amba/bus.h> #include <mtd/mtd-abi.h> -static struct nand_ecclayout fsmc_ecc1_layout = { +static struct nand_ecclayout fsmc_ecc1_128_layout = { .eccbytes = 24, .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, @@ -50,7 +55,127 @@ static struct nand_ecclayout fsmc_ecc1_layout = { } }; -static struct nand_ecclayout fsmc_ecc4_lp_layout = { +static struct nand_ecclayout fsmc_ecc1_64_layout = { + .eccbytes = 12, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + } +}; + +static struct nand_ecclayout fsmc_ecc1_16_layout = { + .eccbytes = 3, + .eccpos = {2, 3, 4}, + .oobfree = { + {.offset = 8, .length = 8}, + } +}; + +/* + * ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes + * of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_256_layout = { + .eccbytes = 208, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, + 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, + 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, + 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, + 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, + 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, + 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, + 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, + 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 3}, + {.offset = 143, .length = 3}, + {.offset = 159, .length = 3}, + {.offset = 175, .length = 3}, + {.offset = 191, .length = 3}, + {.offset = 207, .length = 3}, + {.offset = 223, .length = 3}, + {.offset = 239, .length = 3}, + {.offset = 255, .length = 1} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 97} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_128_layout = { .eccbytes = 104, .eccpos = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, @@ -82,6 +207,45 @@ static struct nand_ecclayout fsmc_ecc4_lp_layout = { }; /* + * ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of + * OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_64_layout = { + .eccbytes = 52, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 1}, + } +}; + +/* + * ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of + * OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One + * byte is free for use. + */ +static struct nand_ecclayout fsmc_ecc4_16_layout = { + .eccbytes = 13, + .eccpos = { 0, 1, 2, 3, 6, 7, 8, + 9, 10, 11, 12, 13, 14 + }, + .oobfree = { + {.offset = 15, .length = 1}, + } +}; + +/* * ECC placement definitions in oobfree type format. * There are 13 bytes of ecc for every 512 byte block and it has to be read * consecutively and immediately after the 512 byte data block for hardware to @@ -103,16 +267,6 @@ static struct fsmc_eccplace fsmc_ecc4_lp_place = { } }; -static struct nand_ecclayout fsmc_ecc4_sp_layout = { - .eccbytes = 13, - .eccpos = { 0, 1, 2, 3, 6, 7, 8, - 9, 10, 11, 12, 13, 14 - }, - .oobfree = { - {.offset = 15, .length = 1}, - } -}; - static struct fsmc_eccplace fsmc_ecc4_sp_place = { .eccplace = { {.offset = 0, .length = 4}, @@ -120,70 +274,6 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = { } }; - -#ifdef CONFIG_MTD_PARTITIONS -/* - * Default partition tables to be used if the partition information not - * provided through platform data. - * - * Default partition layout for small page(= 512 bytes) devices - * Size for "Root file system" is updated in driver based on actual device size - */ -static struct mtd_partition partition_info_16KB_blk[] = { - { - .name = "X-loader", - .offset = 0, - .size = 4*0x4000, - }, - { - .name = "U-Boot", - .offset = 0x10000, - .size = 20*0x4000, - }, - { - .name = "Kernel", - .offset = 0x60000, - .size = 256*0x4000, - }, - { - .name = "Root File System", - .offset = 0x460000, - .size = 0, - }, -}; - -/* - * Default partition layout for large page(> 512 bytes) devices - * Size for "Root file system" is updated in driver based on actual device size - */ -static struct mtd_partition partition_info_128KB_blk[] = { - { - .name = "X-loader", - .offset = 0, - .size = 4*0x20000, - }, - { - .name = "U-Boot", - .offset = 0x80000, - .size = 12*0x20000, - }, - { - .name = "Kernel", - .offset = 0x200000, - .size = 48*0x20000, - }, - { - .name = "Root File System", - .offset = 0x800000, - .size = 0, - }, -}; - -#ifdef CONFIG_MTD_CMDLINE_PARTS -const char *part_probes[] = { "cmdlinepart", NULL }; -#endif -#endif - /** * struct fsmc_nand_data - structure for FSMC NAND device state * @@ -197,6 +287,11 @@ const char *part_probes[] = { "cmdlinepart", NULL }; * @bank: Bank number for probed device. * @clk: Clock structure for FSMC. * + * @read_dma_chan: DMA channel for read access + * @write_dma_chan: DMA channel for write access to NAND + * @dma_access_complete: Completion structure + * + * @data_pa: NAND Physical port for Data. * @data_va: NAND port for Data. * @cmd_va: NAND port for Command. * @addr_va: NAND port for Address. @@ -211,13 +306,18 @@ struct fsmc_nand_data { struct fsmc_eccplace *ecc_place; unsigned int bank; + struct device *dev; + enum access_mode mode; struct clk *clk; - struct resource *resregs; - struct resource *rescmd; - struct resource *resaddr; - struct resource *resdata; + /* DMA related objects */ + struct dma_chan *read_dma_chan; + struct dma_chan *write_dma_chan; + struct completion dma_access_complete; + + struct fsmc_nand_timings *dev_timings; + dma_addr_t data_pa; void __iomem *data_va; void __iomem *cmd_va; void __iomem *addr_va; @@ -261,34 +361,35 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct nand_chip *this = mtd->priv; struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + 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 = (void __iomem *)host->cmd_va; - this->IO_ADDR_W = (void __iomem *)host->cmd_va; + this->IO_ADDR_R = host->cmd_va; + this->IO_ADDR_W = host->cmd_va; } else if (ctrl & NAND_ALE) { - this->IO_ADDR_R = (void __iomem *)host->addr_va; - this->IO_ADDR_W = (void __iomem *)host->addr_va; + this->IO_ADDR_R = host->addr_va; + this->IO_ADDR_W = host->addr_va; } else { - this->IO_ADDR_R = (void __iomem *)host->data_va; - this->IO_ADDR_W = (void __iomem *)host->data_va; + this->IO_ADDR_R = host->data_va; + this->IO_ADDR_W = host->data_va; } - if (ctrl & NAND_NCE) { - writel(readl(®s->bank_regs[bank].pc) | FSMC_ENABLE, - ®s->bank_regs[bank].pc); - } else { - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ENABLE, - ®s->bank_regs[bank].pc); - } + 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(cmd, this->IO_ADDR_W); + writeb_relaxed(cmd, this->IO_ADDR_W); } /* @@ -297,22 +398,46 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) * This routine initializes timing parameters related to NAND memory access in * FSMC registers */ -static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank, - uint32_t busw) +static void fsmc_nand_setup(void __iomem *regs, uint32_t bank, + uint32_t busw, struct fsmc_nand_timings *timings) { uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; + uint32_t tclr, tar, thiz, thold, twait, tset; + struct fsmc_nand_timings *tims; + struct fsmc_nand_timings default_timings = { + .tclr = FSMC_TCLR_1, + .tar = FSMC_TAR_1, + .thiz = FSMC_THIZ_1, + .thold = FSMC_THOLD_4, + .twait = FSMC_TWAIT_6, + .tset = FSMC_TSET_0, + }; + + if (timings) + tims = timings; + else + tims = &default_timings; + + tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; + tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; + thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT; + thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT; + twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT; + tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (busw) - writel(value | FSMC_DEVWID_16, ®s->bank_regs[bank].pc); + writel_relaxed(value | FSMC_DEVWID_16, + FSMC_NAND_REG(regs, bank, PC)); else - writel(value | FSMC_DEVWID_8, ®s->bank_regs[bank].pc); - - writel(readl(®s->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1, - ®s->bank_regs[bank].pc); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - ®s->bank_regs[bank].comm); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - ®s->bank_regs[bank].attrib); + 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)); } /* @@ -322,20 +447,20 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256, - ®s->bank_regs[bank].pc); - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCEN, - ®s->bank_regs[bank].pc); - writel(readl(®s->bank_regs[bank].pc) | FSMC_ECCEN, - ®s->bank_regs[bank].pc); + 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)); } /* * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by - * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction upto + * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to * max of 8-bits) */ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, @@ -343,37 +468,42 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + 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(®s->bank_regs[bank].sts) & FSMC_CODE_RDY) + if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) break; else cond_resched(); } while (!time_after_eq(jiffies, deadline)); - ecc_tmp = readl(®s->bank_regs[bank].ecc1); + if (time_after_eq(jiffies, deadline)) { + dev_err(host->dev, "calculate ecc timed out\n"); + return -ETIMEDOUT; + } + + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, 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(®s->bank_regs[bank].ecc2); + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, 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(®s->bank_regs[bank].ecc3); + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, 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(®s->bank_regs[bank].sts); + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); ecc[12] = (uint8_t) (ecc_tmp >> 16); return 0; @@ -381,7 +511,7 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, /* * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by - * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction upto + * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to * max of 1-bit) */ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, @@ -389,11 +519,11 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; uint32_t ecc_tmp; - ecc_tmp = readl(®s->bank_regs[bank].ecc1); + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); @@ -401,21 +531,186 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, return 0; } +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static void dma_complete(void *param) +{ + struct fsmc_nand_data *host = param; + + complete(&host->dma_access_complete); +} + +static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, + enum dma_data_direction direction) +{ + struct dma_chan *chan; + struct dma_device *dma_dev; + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_dst, dma_src, dma_addr; + dma_cookie_t cookie; + unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int ret; + + if (direction == DMA_TO_DEVICE) + chan = host->write_dma_chan; + else if (direction == DMA_FROM_DEVICE) + chan = host->read_dma_chan; + else + return -EINVAL; + + dma_dev = chan->device; + dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); + + if (direction == DMA_TO_DEVICE) { + dma_src = dma_addr; + dma_dst = host->data_pa; + } else { + dma_src = host->data_pa; + dma_dst = dma_addr; + } + + tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, + len, flags); + if (!tx) { + dev_err(host->dev, "device_prep_dma_memcpy error\n"); + ret = -EIO; + goto unmap_dma; + } + + tx->callback = dma_complete; + tx->callback_param = host; + cookie = tx->tx_submit(tx); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(host->dev, "dma_submit_error %d\n", cookie); + goto unmap_dma; + } + + dma_async_issue_pending(chan); + + ret = + wait_for_completion_timeout(&host->dma_access_complete, + msecs_to_jiffies(3000)); + if (ret <= 0) { + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dev_err(host->dev, "wait_for_completion_timeout\n"); + if (!ret) + ret = -ETIMEDOUT; + goto unmap_dma; + } + + ret = 0; + +unmap_dma: + dma_unmap_single(dma_dev->dev, dma_addr, len, direction); + + return ret; +} + +/* + * fsmc_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + 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); + } else { + for (i = 0; i < len; i++) + writeb_relaxed(buf[i], chip->IO_ADDR_W); + } +} + +/* + * fsmc_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + 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); + } else { + for (i = 0; i < len; i++) + buf[i] = readb_relaxed(chip->IO_ADDR_R); + } +} + +/* + * fsmc_read_buf_dma - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct fsmc_nand_data *host; + + host = container_of(mtd, struct fsmc_nand_data, mtd); + dma_xfer(host, buf, len, DMA_FROM_DEVICE); +} + +/* + * fsmc_write_buf_dma - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct fsmc_nand_data *host; + + host = container_of(mtd, struct fsmc_nand_data, mtd); + dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); +} + /* * fsmc_read_page_hwecc * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * @page: page number to read * - * This routine is needed for fsmc verison 8 as reading from NAND chip has to be + * This routine is needed for fsmc version 8 as reading from NAND chip has to be * performed in a strict sequence as follows: * data(512 byte) -> ecc(13 byte) - * After this read, fsmc hardware generates and reports error data bits(upto a + * After this read, fsmc hardware generates and reports error data bits(up to a * max of 8 bits) */ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); @@ -434,9 +729,9 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, */ uint16_t ecc_oob[7]; uint8_t *oob = (uint8_t *)&ecc_oob[0]; + unsigned int max_bitflips = 0; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { - chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); @@ -447,31 +742,35 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, group++; /* - * length is intentionally kept a higher multiple of 2 - * to read at least 13 bytes even in case of 16 bit NAND - * devices - */ - len = roundup(len, 2); + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); chip->read_buf(mtd, oob + j, len); j += len; } - memcpy(&ecc_code[i], oob, 13); + memcpy(&ecc_code[i], oob, chip->ecc.bytes); chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /* - * fsmc_correct_data + * fsmc_bch8_correct_data * @mtd: mtd info structure * @dat: buffer of read data * @read_ecc: ecc read from device spare area @@ -480,19 +779,51 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * calc_ecc is a 104 bit information containing maximum of 8 error * offset informations of 13 bits each in 512 bytes of read data. */ -static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, +static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + struct nand_chip *chip = mtd->priv; + void __iomem *regs = host->regs_va; unsigned int bank = host->bank; - uint16_t err_idx[8]; - uint64_t ecc_data[2]; + 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; + + /* no bit flipping */ + if (likely(num_err == 0)) + return 0; + + /* too many errors */ + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ - /* The calculated ecc is actually the correction index in data */ - memcpy(ecc_data, calc_ecc, 13); + int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8); + int bits_data = count_written_bits(dat, chip->ecc.size, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, chip->ecc.size); + return bits_data; + } + + return -EBADMSG; + } /* * ------------------- calc_ecc[] bit wise -----------|--13 bits--| @@ -503,27 +834,26 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, * uint64_t array and error offset indexes are populated in err_idx * array */ - for (i = 0; i < 8; i++) { - if (i == 4) { - err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0]; - ecc_data[1] >>= 1; - continue; - } - err_idx[i] = (ecc_data[i/4] & 0x1FFF); - ecc_data[i/4] >>= 13; - } - - num_err = (readl(®s->bank_regs[bank].sts) >> 10) & 0xF; - - if (num_err == 0xF) - return -EBADMSG; + 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)); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); i = 0; while (num_err--) { change_bit(0, (unsigned long *)&err_idx[i]); change_bit(1, (unsigned long *)&err_idx[i]); - if (err_idx[i] <= 512 * 8) { + if (err_idx[i] < chip->ecc.size * 8) { change_bit(err_idx[i], (unsigned long *)dat); i++; } @@ -531,6 +861,58 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, return i; } +static bool filter(struct dma_chan *chan, void *slave) +{ + chan->private = slave; + return true; +} + +#ifdef CONFIG_OF +static int fsmc_nand_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + u32 val; + + /* Set default NAND width to 8 bits */ + pdata->width = 8; + if (!of_property_read_u32(np, "bank-width", &val)) { + if (val == 2) { + pdata->width = 16; + } else if (val != 1) { + dev_err(&pdev->dev, "invalid bank-width %u\n", val); + return -EINVAL; + } + } + if (of_get_property(np, "nand-skip-bbtscan", NULL)) + pdata->options = NAND_SKIP_BBTSCAN; + + pdata->nand_timings = devm_kzalloc(&pdev->dev, + sizeof(*pdata->nand_timings), GFP_KERNEL); + if (!pdata->nand_timings) + return -ENOMEM; + of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings, + sizeof(*pdata->nand_timings)); + + /* Set default NAND bank to 0 */ + pdata->bank = 0; + if (!of_property_read_u32(np, "bank", &val)) { + if (val > 3) { + dev_err(&pdev->dev, "invalid bank %u\n", val); + return -EINVAL; + } + pdata->bank = val; + } + return 0; +} +#else +static int fsmc_nand_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + return -ENOSYS; +} +#endif + /* * fsmc_nand_probe - Probe function * @pdev: platform device structure @@ -538,102 +920,68 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, static int __init fsmc_nand_probe(struct platform_device *pdev) { struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node __maybe_unused *np = pdev->dev.of_node; + struct mtd_part_parser_data ppdata = {}; struct fsmc_nand_data *host; struct mtd_info *mtd; struct nand_chip *nand; - struct fsmc_regs *regs; struct resource *res; + dma_cap_mask_t mask; int ret = 0; u32 pid; int i; + if (np) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdev->dev.platform_data = pdata; + ret = fsmc_nand_probe_config_dt(pdev, np); + if (ret) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENODEV; + } + } + if (!pdata) { dev_err(&pdev->dev, "platform data is NULL\n"); return -EINVAL; } /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(*host), GFP_KERNEL); - if (!host) { - dev_err(&pdev->dev, "failed to allocate device structure\n"); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) return -ENOMEM; - } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - if (!res) { - ret = -EIO; - goto err_probe1; - } - - host->resdata = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!host->resdata) { - ret = -EIO; - goto err_probe1; - } - - host->data_va = ioremap(res->start, resource_size(res)); - if (!host->data_va) { - ret = -EIO; - goto err_probe1; - } - - host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE, - resource_size(res), pdev->name); - if (!host->resaddr) { - ret = -EIO; - goto err_probe1; - } - - host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res)); - if (!host->addr_va) { - ret = -EIO; - goto err_probe1; - } - - host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE, - resource_size(res), pdev->name); - if (!host->rescmd) { - ret = -EIO; - goto err_probe1; - } - - host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res)); - if (!host->cmd_va) { - ret = -EIO; - goto err_probe1; - } + host->data_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->data_va)) + return PTR_ERR(host->data_va); + + host->data_pa = (dma_addr_t)res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); + host->addr_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->addr_va)) + return PTR_ERR(host->addr_va); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); + host->cmd_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->cmd_va)) + return PTR_ERR(host->cmd_va); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - if (!res) { - ret = -EIO; - goto err_probe1; - } - - host->resregs = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!host->resregs) { - ret = -EIO; - goto err_probe1; - } - - host->regs_va = ioremap(res->start, resource_size(res)); - if (!host->regs_va) { - ret = -EIO; - goto err_probe1; - } + host->regs_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->regs_va)) + return PTR_ERR(host->regs_va); host->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "failed to fetch block clock\n"); - ret = PTR_ERR(host->clk); - host->clk = NULL; - goto err_probe1; + return PTR_ERR(host->clk); } - ret = clk_enable(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) - goto err_probe1; + goto err_clk_prepare_enable; /* * This device ID is actually a common AMBA ID as used on the @@ -649,7 +997,14 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->bank = pdata->bank; host->select_chip = pdata->select_bank; - regs = host->regs_va; + host->partitions = pdata->partitions; + host->nr_partitions = pdata->nr_partitions; + host->dev = &pdev->dev; + host->dev_timings = pdata->nand_timings; + host->mode = pdata->mode; + + if (host->mode == USE_DMA_ACCESS) + init_completion(&host->dma_access_complete); /* Link all private pointers */ mtd = &host->mtd; @@ -668,42 +1023,107 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.size = 512; nand->options = pdata->options; nand->select_chip = fsmc_select_chip; + nand->badblockbits = 7; if (pdata->width == FSMC_NAND_BW16) nand->options |= NAND_BUSWIDTH_16; - fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16); + switch (host->mode) { + case USE_DMA_ACCESS: + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + host->read_dma_chan = dma_request_channel(mask, filter, + pdata->read_dma_priv); + if (!host->read_dma_chan) { + dev_err(&pdev->dev, "Unable to get read dma channel\n"); + goto err_req_read_chnl; + } + host->write_dma_chan = dma_request_channel(mask, filter, + pdata->write_dma_priv); + if (!host->write_dma_chan) { + 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; + } + + fsmc_nand_setup(host->regs_va, host->bank, + nand->options & NAND_BUSWIDTH_16, + host->dev_timings); if (AMBA_REV_BITS(host->pid) >= 8) { nand->ecc.read_page = fsmc_read_page_hwecc; nand->ecc.calculate = fsmc_read_hwecc_ecc4; - nand->ecc.correct = fsmc_correct_data; + nand->ecc.correct = fsmc_bch8_correct_data; nand->ecc.bytes = 13; + nand->ecc.strength = 8; } else { nand->ecc.calculate = fsmc_read_hwecc_ecc1; nand->ecc.correct = nand_correct_data; nand->ecc.bytes = 3; + nand->ecc.strength = 1; } /* - * Scan to find existance of the device + * Scan to find existence of the device */ if (nand_scan_ident(&host->mtd, 1, NULL)) { ret = -ENXIO; dev_err(&pdev->dev, "No NAND Device found!\n"); - goto err_probe; + goto err_scan_ident; } if (AMBA_REV_BITS(host->pid) >= 8) { - if (host->mtd.writesize == 512) { - nand->ecc.layout = &fsmc_ecc4_sp_layout; + switch (host->mtd.oobsize) { + case 16: + nand->ecc.layout = &fsmc_ecc4_16_layout; host->ecc_place = &fsmc_ecc4_sp_place; - } else { - nand->ecc.layout = &fsmc_ecc4_lp_layout; + break; + case 64: + nand->ecc.layout = &fsmc_ecc4_64_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 128: + nand->ecc.layout = &fsmc_ecc4_128_layout; host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 224: + nand->ecc.layout = &fsmc_ecc4_224_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 256: + nand->ecc.layout = &fsmc_ecc4_256_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + default: + dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + BUG(); } } else { - nand->ecc.layout = &fsmc_ecc1_layout; + switch (host->mtd.oobsize) { + case 16: + nand->ecc.layout = &fsmc_ecc1_16_layout; + break; + case 64: + nand->ecc.layout = &fsmc_ecc1_64_layout; + break; + case 128: + nand->ecc.layout = &fsmc_ecc1_128_layout; + break; + default: + dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + BUG(); + } } /* Second stage of scan to fill MTD data-structures */ @@ -719,109 +1139,31 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * platform data, * default partition information present in driver. */ -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS /* - * Check if partition info passed via command line + * Check for partition info passed */ host->mtd.name = "nand"; - host->nr_partitions = parse_mtd_partitions(&host->mtd, part_probes, - &host->partitions, 0); - if (host->nr_partitions <= 0) { -#endif - /* - * Check if partition info passed via command line - */ - if (pdata->partitions) { - host->partitions = pdata->partitions; - host->nr_partitions = pdata->nr_partitions; - } else { - struct mtd_partition *partition; - int i; - - /* Select the default partitions info */ - switch (host->mtd.size) { - case 0x01000000: - case 0x02000000: - case 0x04000000: - host->partitions = partition_info_16KB_blk; - host->nr_partitions = - sizeof(partition_info_16KB_blk) / - sizeof(struct mtd_partition); - break; - case 0x08000000: - case 0x10000000: - case 0x20000000: - case 0x40000000: - host->partitions = partition_info_128KB_blk; - host->nr_partitions = - sizeof(partition_info_128KB_blk) / - sizeof(struct mtd_partition); - break; - default: - ret = -ENXIO; - pr_err("Unsupported NAND size\n"); - goto err_probe; - } - - partition = host->partitions; - for (i = 0; i < host->nr_partitions; i++, partition++) { - if (partition->size == 0) { - partition->size = host->mtd.size - - partition->offset; - break; - } - } - } -#ifdef CONFIG_MTD_CMDLINE_PARTS - } -#endif - - if (host->partitions) { - ret = add_mtd_partitions(&host->mtd, host->partitions, - host->nr_partitions); - if (ret) - goto err_probe; - } -#else - dev_info(&pdev->dev, "Registering %s as whole device\n", mtd->name); - if (!add_mtd_device(mtd)) { - ret = -ENXIO; + ppdata.of_node = np; + ret = mtd_device_parse_register(&host->mtd, NULL, &ppdata, + host->partitions, host->nr_partitions); + if (ret) goto err_probe; - } -#endif platform_set_drvdata(pdev, host); dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); return 0; err_probe: - clk_disable(host->clk); -err_probe1: - if (host->clk) - clk_put(host->clk); - if (host->regs_va) - iounmap(host->regs_va); - if (host->resregs) - release_mem_region(host->resregs->start, - resource_size(host->resregs)); - if (host->cmd_va) - iounmap(host->cmd_va); - if (host->rescmd) - release_mem_region(host->rescmd->start, - resource_size(host->rescmd)); - if (host->addr_va) - iounmap(host->addr_va); - if (host->resaddr) - release_mem_region(host->resaddr->start, - resource_size(host->resaddr)); - if (host->data_va) - iounmap(host->data_va); - if (host->resdata) - release_mem_region(host->resdata->start, - resource_size(host->resdata)); - - kfree(host); +err_scan_ident: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->write_dma_chan); +err_req_write_chnl: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->read_dma_chan); +err_req_read_chnl: + clk_disable_unprepare(host->clk); +err_clk_prepare_enable: + clk_put(host->clk); return ret; } @@ -832,56 +1174,51 @@ static int fsmc_nand_remove(struct platform_device *pdev) { struct fsmc_nand_data *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (host) { -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(&host->mtd); -#else - del_mtd_device(&host->mtd); -#endif - clk_disable(host->clk); - clk_put(host->clk); + nand_release(&host->mtd); - iounmap(host->regs_va); - release_mem_region(host->resregs->start, - resource_size(host->resregs)); - iounmap(host->cmd_va); - release_mem_region(host->rescmd->start, - resource_size(host->rescmd)); - iounmap(host->addr_va); - release_mem_region(host->resaddr->start, - resource_size(host->resaddr)); - iounmap(host->data_va); - release_mem_region(host->resdata->start, - resource_size(host->resdata)); - - kfree(host); + if (host->mode == USE_DMA_ACCESS) { + dma_release_channel(host->write_dma_chan); + dma_release_channel(host->read_dma_chan); + } + clk_disable_unprepare(host->clk); + clk_put(host->clk); } + return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int fsmc_nand_suspend(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); if (host) - clk_disable(host->clk); + clk_disable_unprepare(host->clk); return 0; } static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); - if (host) - clk_enable(host->clk); + if (host) { + clk_prepare_enable(host->clk); + fsmc_nand_setup(host->regs_va, host->bank, + host->nand.options & NAND_BUSWIDTH_16, + host->dev_timings); + } return 0; } +#endif -static const struct dev_pm_ops fsmc_nand_pm_ops = { - .suspend = fsmc_nand_suspend, - .resume = fsmc_nand_resume, +static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); + +#ifdef CONFIG_OF +static const struct of_device_id fsmc_nand_id_table[] = { + { .compatible = "st,spear600-fsmc-nand" }, + { .compatible = "stericsson,fsmc-nand" }, + {} }; +MODULE_DEVICE_TABLE(of, fsmc_nand_id_table); #endif static struct platform_driver fsmc_nand_driver = { @@ -889,24 +1226,12 @@ static struct platform_driver fsmc_nand_driver = { .driver = { .owner = THIS_MODULE, .name = "fsmc-nand", -#ifdef CONFIG_PM + .of_match_table = of_match_ptr(fsmc_nand_id_table), .pm = &fsmc_nand_pm_ops, -#endif }, }; -static int __init fsmc_nand_init(void) -{ - return platform_driver_probe(&fsmc_nand_driver, - fsmc_nand_probe); -} -module_init(fsmc_nand_init); - -static void __exit fsmc_nand_exit(void) -{ - platform_driver_unregister(&fsmc_nand_driver); -} -module_exit(fsmc_nand_exit); +module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi"); diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 0cde618bcc1..117ce333fdd 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -17,7 +17,7 @@ */ #include <linux/kernel.h> -#include <linux/init.h> +#include <linux/err.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -27,6 +27,9 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <linux/mtd/nand-gpio.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> struct gpiomtd { void __iomem *io_sync; @@ -83,231 +86,201 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) gpio_nand_dosync(gpiomtd); } -static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +static int gpio_nand_devready(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; + struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - writesb(this->IO_ADDR_W, buf, len); + return gpio_get_value(gpiomtd->plat.gpio_rdy); } -static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) +#ifdef CONFIG_OF +static const struct of_device_id gpio_nand_id_table[] = { + { .compatible = "gpio-control-nand" }, + {} +}; +MODULE_DEVICE_TABLE(of, gpio_nand_id_table); + +static int gpio_nand_get_config_of(const struct device *dev, + struct gpio_nand_platdata *plat) { - struct nand_chip *this = mtd->priv; + u32 val; - readsb(this->IO_ADDR_R, buf, len); -} + if (!dev->of_node) + return -ENODEV; -static int gpio_nand_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - unsigned char read, *p = (unsigned char *) buf; - int i, err = 0; - - for (i = 0; i < len; i++) { - read = readb(this->IO_ADDR_R); - if (read != p[i]) { - pr_debug("%s: err at %d (read %04x vs %04x)\n", - __func__, i, read, p[i]); - err = -EFAULT; + if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { + if (val == 2) { + plat->options |= NAND_BUSWIDTH_16; + } else if (val != 1) { + dev_err(dev, "invalid bank-width %u\n", val); + return -EINVAL; } } - return err; -} -static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, - int len) -{ - struct nand_chip *this = mtd->priv; + plat->gpio_rdy = of_get_gpio(dev->of_node, 0); + plat->gpio_nce = of_get_gpio(dev->of_node, 1); + plat->gpio_ale = of_get_gpio(dev->of_node, 2); + plat->gpio_cle = of_get_gpio(dev->of_node, 3); + plat->gpio_nwp = of_get_gpio(dev->of_node, 4); - if (IS_ALIGNED((unsigned long)buf, 2)) { - writesw(this->IO_ADDR_W, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; + if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) + plat->chip_delay = val; - for (i = 0; i < len; i += 2, ptr++) - writew(*ptr, this->IO_ADDR_W); - } + return 0; } -static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) +static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev) { - struct nand_chip *this = mtd->priv; + struct resource *r; + u64 addr; - if (IS_ALIGNED((unsigned long)buf, 2)) { - readsw(this->IO_ADDR_R, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; + if (of_property_read_u64(pdev->dev.of_node, + "gpio-control-nand,io-sync-reg", &addr)) + return NULL; - for (i = 0; i < len; i += 2, ptr++) - *ptr = readw(this->IO_ADDR_R); - } -} + r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL); + if (!r) + return NULL; -static int gpio_nand_verifybuf16(struct mtd_info *mtd, const u_char *buf, - int len) + r->start = addr; + r->end = r->start + 0x3; + r->flags = IORESOURCE_MEM; + + return r; +} +#else /* CONFIG_OF */ +static inline int gpio_nand_get_config_of(const struct device *dev, + struct gpio_nand_platdata *plat) { - struct nand_chip *this = mtd->priv; - unsigned short read, *p = (unsigned short *) buf; - int i, err = 0; - len >>= 1; - - for (i = 0; i < len; i++) { - read = readw(this->IO_ADDR_R); - if (read != p[i]) { - pr_debug("%s: err at %d (read %04x vs %04x)\n", - __func__, i, read, p[i]); - err = -EFAULT; - } - } - return err; + return -ENOSYS; } - -static int gpio_nand_devready(struct mtd_info *mtd) +static inline struct resource * +gpio_nand_get_io_sync_of(struct platform_device *pdev) { - struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - return gpio_get_value(gpiomtd->plat.gpio_rdy); + return NULL; } +#endif /* CONFIG_OF */ -static int __devexit gpio_nand_remove(struct platform_device *dev) +static inline int gpio_nand_get_config(const struct device *dev, + struct gpio_nand_platdata *plat) { - struct gpiomtd *gpiomtd = platform_get_drvdata(dev); - struct resource *res; + int ret = gpio_nand_get_config_of(dev, plat); - nand_release(&gpiomtd->mtd_info); - - res = platform_get_resource(dev, IORESOURCE_MEM, 1); - iounmap(gpiomtd->io_sync); - if (res) - release_mem_region(res->start, resource_size(res)); + if (!ret) + return ret; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res->start, resource_size(res)); + if (dev_get_platdata(dev)) { + memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); + return 0; + } - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_set_value(gpiomtd->plat.gpio_nwp, 0); - gpio_set_value(gpiomtd->plat.gpio_nce, 1); + return -EINVAL; +} - gpio_free(gpiomtd->plat.gpio_cle); - gpio_free(gpiomtd->plat.gpio_ale); - gpio_free(gpiomtd->plat.gpio_nce); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); - gpio_free(gpiomtd->plat.gpio_rdy); +static inline struct resource * +gpio_nand_get_io_sync(struct platform_device *pdev) +{ + struct resource *r = gpio_nand_get_io_sync_of(pdev); - kfree(gpiomtd); + if (r) + return r; - return 0; + return platform_get_resource(pdev, IORESOURCE_MEM, 1); } -static void __iomem *request_and_remap(struct resource *res, size_t size, - const char *name, int *err) +static int gpio_nand_remove(struct platform_device *pdev) { - void __iomem *ptr; + struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); - if (!request_mem_region(res->start, resource_size(res), name)) { - *err = -EBUSY; - return NULL; - } + nand_release(&gpiomtd->mtd_info); - ptr = ioremap(res->start, size); - if (!ptr) { - release_mem_region(res->start, resource_size(res)); - *err = -ENOMEM; - } - return ptr; + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_set_value(gpiomtd->plat.gpio_nwp, 0); + gpio_set_value(gpiomtd->plat.gpio_nce, 1); + + return 0; } -static int __devinit gpio_nand_probe(struct platform_device *dev) +static int gpio_nand_probe(struct platform_device *pdev) { struct gpiomtd *gpiomtd; - struct nand_chip *this; - struct resource *res0, *res1; - int ret; - - if (!dev->dev.platform_data) - return -EINVAL; + struct nand_chip *chip; + struct resource *res; + struct mtd_part_parser_data ppdata = {}; + int ret = 0; - res0 = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res0) + if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev)) return -EINVAL; - gpiomtd = kzalloc(sizeof(*gpiomtd), GFP_KERNEL); - if (gpiomtd == NULL) { - dev_err(&dev->dev, "failed to create NAND MTD\n"); + gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); + if (!gpiomtd) return -ENOMEM; - } - this = &gpiomtd->nand_chip; - this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret); - if (!this->IO_ADDR_R) { - dev_err(&dev->dev, "unable to map NAND\n"); - goto err_map; - } + chip = &gpiomtd->nand_chip; - res1 = platform_get_resource(dev, IORESOURCE_MEM, 1); - if (res1) { - gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret); - if (!gpiomtd->io_sync) { - dev_err(&dev->dev, "unable to map sync NAND\n"); - goto err_sync; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(chip->IO_ADDR_R)) + return PTR_ERR(chip->IO_ADDR_R); + + res = gpio_nand_get_io_sync(pdev); + if (res) { + gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpiomtd->io_sync)) + return PTR_ERR(gpiomtd->io_sync); } - memcpy(&gpiomtd->plat, dev->dev.platform_data, sizeof(gpiomtd->plat)); + ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat); + if (ret) + return ret; - ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE"); if (ret) - goto err_nce; + return ret; gpio_direction_output(gpiomtd->plat.gpio_nce, 1); + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { - ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, + "NAND NWP"); if (ret) - goto err_nwp; - gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); + return ret; } - ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE"); + + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); if (ret) - goto err_ale; + return ret; gpio_direction_output(gpiomtd->plat.gpio_ale, 0); - ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE"); + + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); if (ret) - goto err_cle; + return ret; gpio_direction_output(gpiomtd->plat.gpio_cle, 0); - ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); - if (ret) - goto err_rdy; - gpio_direction_input(gpiomtd->plat.gpio_rdy); - - - this->IO_ADDR_W = this->IO_ADDR_R; - this->ecc.mode = NAND_ECC_SOFT; - this->options = gpiomtd->plat.options; - this->chip_delay = gpiomtd->plat.chip_delay; - - /* install our routines */ - this->cmd_ctrl = gpio_nand_cmd_ctrl; - this->dev_ready = gpio_nand_devready; - - if (this->options & NAND_BUSWIDTH_16) { - this->read_buf = gpio_nand_readbuf16; - this->write_buf = gpio_nand_writebuf16; - this->verify_buf = gpio_nand_verifybuf16; - } else { - this->read_buf = gpio_nand_readbuf; - this->write_buf = gpio_nand_writebuf; - this->verify_buf = gpio_nand_verifybuf; + + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, + "NAND RDY"); + if (ret) + return ret; + gpio_direction_input(gpiomtd->plat.gpio_rdy); + chip->dev_ready = gpio_nand_devready; } - /* set the mtd private data for the nand driver */ - gpiomtd->mtd_info.priv = this; - gpiomtd->mtd_info.owner = THIS_MODULE; + chip->IO_ADDR_W = chip->IO_ADDR_R; + chip->ecc.mode = NAND_ECC_SOFT; + chip->options = gpiomtd->plat.options; + chip->chip_delay = gpiomtd->plat.chip_delay; + chip->cmd_ctrl = gpio_nand_cmd_ctrl; + + gpiomtd->mtd_info.priv = chip; + gpiomtd->mtd_info.owner = THIS_MODULE; + + platform_set_drvdata(pdev, gpiomtd); + + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); if (nand_scan(&gpiomtd->mtd_info, 1)) { - dev_err(&dev->dev, "no nand chips found?\n"); ret = -ENXIO; goto err_wp; } @@ -316,34 +289,17 @@ static int __devinit gpio_nand_probe(struct platform_device *dev) gpiomtd->plat.adjust_parts(&gpiomtd->plat, gpiomtd->mtd_info.size); - add_mtd_partitions(&gpiomtd->mtd_info, gpiomtd->plat.parts, - gpiomtd->plat.num_parts); - platform_set_drvdata(dev, gpiomtd); - - return 0; + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata, + gpiomtd->plat.parts, + gpiomtd->plat.num_parts); + if (!ret) + return 0; err_wp: if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); - gpio_free(gpiomtd->plat.gpio_rdy); -err_rdy: - gpio_free(gpiomtd->plat.gpio_cle); -err_cle: - gpio_free(gpiomtd->plat.gpio_ale); -err_ale: - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); -err_nwp: - gpio_free(gpiomtd->plat.gpio_nce); -err_nce: - iounmap(gpiomtd->io_sync); - if (res1) - release_mem_region(res1->start, resource_size(res1)); -err_sync: - iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res0->start, resource_size(res0)); -err_map: - kfree(gpiomtd); + return ret; } @@ -352,23 +308,12 @@ static struct platform_driver gpio_nand_driver = { .remove = gpio_nand_remove, .driver = { .name = "gpio-nand", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpio_nand_id_table), }, }; -static int __init gpio_nand_init(void) -{ - printk(KERN_INFO "GPIO NAND driver, © 2004 Simtec Electronics\n"); - - return platform_driver_register(&gpio_nand_driver); -} - -static void __exit gpio_nand_exit(void) -{ - platform_driver_unregister(&gpio_nand_driver); -} - -module_init(gpio_nand_init); -module_exit(gpio_nand_exit); +module_platform_driver(gpio_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/gpmi-nand/Makefile new file mode 100644 index 00000000000..3a462487c35 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o +gpmi_nand-objs += gpmi-nand.o +gpmi_nand-objs += gpmi-lib.o diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h new file mode 100644 index 00000000000..05bb91f2f4c --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h @@ -0,0 +1,128 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NAND_BCH_REGS_H +#define __GPMI_NAND_BCH_REGS_H + +#define HW_BCH_CTRL 0x00000000 +#define HW_BCH_CTRL_SET 0x00000004 +#define HW_BCH_CTRL_CLR 0x00000008 +#define HW_BCH_CTRL_TOG 0x0000000c + +#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) +#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) + +#define HW_BCH_STATUS0 0x00000010 +#define HW_BCH_MODE 0x00000020 +#define HW_BCH_ENCODEPTR 0x00000030 +#define HW_BCH_DATAPTR 0x00000040 +#define HW_BCH_METAPTR 0x00000050 +#define HW_BCH_LAYOUTSELECT 0x00000070 + +#define HW_BCH_FLASH0LAYOUT0 0x00000080 + +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) + +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE) +#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_META_SIZE) + +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 +#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) +#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ + & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ + : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ + & BM_BCH_FLASH0LAYOUT0_ECC0) \ + ) + +#define MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14 10 +#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \ + (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) +#define BF_BCH_FLASH0LAYOUT0_GF(v, x) \ + ((GPMI_IS_MX6(x) && ((v) == 14)) \ + ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \ + & MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \ + : 0 \ + ) + +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + ) + +#define HW_BCH_FLASH0LAYOUT1 0x00000090 + +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ + (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) + +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 +#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) +#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ + & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ + : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ + & BM_BCH_FLASH0LAYOUT1_ECCN) \ + ) + +#define MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14 10 +#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \ + (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) +#define BF_BCH_FLASH0LAYOUT1_GF(v, x) \ + ((GPMI_IS_MX6(x) && ((v) == 14)) \ + ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \ + & MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \ + : 0 \ + ) + +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + ) + +#define HW_BCH_VERSION 0x00000160 +#endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c new file mode 100644 index 00000000000..87e658ce23e --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -0,0 +1,1355 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/slab.h> + +#include "gpmi-nand.h" +#include "gpmi-regs.h" +#include "bch-regs.h" + +static struct timing_threshod 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, +}; + +#define MXS_SET_ADDR 0x4 +#define MXS_CLR_ADDR 0x8 +/* + * Clear the bit and poll it cleared. This is usually called with + * a reset address and mask being either SFTRST(bit 31) or CLKGATE + * (bit 30). + */ +static int clear_poll_bit(void __iomem *addr, u32 mask) +{ + int timeout = 0x400; + + /* clear the bit */ + writel(mask, addr + MXS_CLR_ADDR); + + /* + * SFTRST needs 3 GPMI clocks to settle, the reference manual + * recommends to wait 1us. + */ + udelay(1); + + /* poll the bit becoming clear */ + while ((readl(addr) & mask) && --timeout) + /* nothing */; + + return !timeout; +} + +#define MODULE_CLKGATE (1 << 30) +#define MODULE_SFTRST (1 << 31) +/* + * The current mxs_reset_block() will do two things: + * [1] enable the module. + * [2] reset the module. + * + * In most of the cases, it's ok. + * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). + * If you try to soft reset the BCH block, it becomes unusable until + * the next hard reset. This case occurs in the NAND boot mode. When the board + * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. + * So If the driver tries to reset the BCH again, the BCH will not work anymore. + * You will see a DMA timeout in this case. The bug has been fixed + * in the following chips, such as MX28. + * + * To avoid this bug, just add a new parameter `just_enable` for + * the mxs_reset_block(), and rewrite it here. + */ +static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) +{ + int ret; + int timeout = 0x400; + + /* clear and poll SFTRST */ + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + if (unlikely(ret)) + goto error; + + /* clear CLKGATE */ + writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); + + if (!just_enable) { + /* set SFTRST to reset the block */ + writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); + udelay(1); + + /* poll CLKGATE becoming set */ + while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) + /* nothing */; + if (unlikely(!timeout)) + goto error; + } + + /* clear and poll SFTRST */ + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + if (unlikely(ret)) + goto error; + + /* clear and poll CLKGATE */ + ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); + if (unlikely(ret)) + goto error; + + return 0; + +error: + pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); + return -ETIMEDOUT; +} + +static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) +{ + struct clk *clk; + int ret; + int i; + + for (i = 0; i < GPMI_CLK_MAX; i++) { + clk = this->resources.clock[i]; + if (!clk) + break; + + if (v) { + ret = clk_prepare_enable(clk); + if (ret) + goto err_clk; + } else { + clk_disable_unprepare(clk); + } + } + return 0; + +err_clk: + for (; i > 0; i--) + clk_disable_unprepare(this->resources.clock[i - 1]); + return ret; +} + +#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) +#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) + +int gpmi_init(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + int ret; + + ret = gpmi_enable_clk(this); + if (ret) + goto err_out; + ret = gpmi_reset_block(r->gpmi_regs, false); + if (ret) + goto err_out; + + /* + * Reset BCH here, too. We got failures otherwise :( + * See later BCH reset for explanation of MX23 handling + */ + ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); + if (ret) + goto err_out; + + + /* Choose NAND mode. */ + writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Set the IRQ polarity. */ + writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, + r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable Write-Protection. */ + writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Select BCH ECC. */ + writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * Decouple the chip select from dma channel. We use dma0 for all + * the chips. + */ + writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + gpmi_disable_clk(this); + return 0; +err_out: + return ret; +} + +/* This function is very useful. It is called only when the bug occur. */ +void gpmi_dump_info(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct bch_geometry *geo = &this->bch_geometry; + u32 reg; + int i; + + dev_err(this->dev, "Show GPMI registers :\n"); + for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { + reg = readl(r->gpmi_regs + i * 0x10); + dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + + /* start to print out the BCH info */ + dev_err(this->dev, "Show BCH registers :\n"); + for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { + reg = readl(r->bch_regs + i * 0x10); + dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + dev_err(this->dev, "BCH Geometry :\n" + "GF length : %u\n" + "ECC Strength : %u\n" + "Page Size in Bytes : %u\n" + "Metadata Size in Bytes : %u\n" + "ECC Chunk Size in Bytes: %u\n" + "ECC Chunk Count : %u\n" + "Payload Size in Bytes : %u\n" + "Auxiliary Size in Bytes: %u\n" + "Auxiliary Status Offset: %u\n" + "Block Mark Byte Offset : %u\n" + "Block Mark Bit Offset : %u\n", + geo->gf_len, + geo->ecc_strength, + geo->page_size, + geo->metadata_size, + geo->ecc_chunk_size, + geo->ecc_chunk_count, + geo->payload_size, + geo->auxiliary_size, + geo->auxiliary_status_offset, + geo->block_mark_byte_offset, + geo->block_mark_bit_offset); +} + +/* Configures the geometry for BCH. */ +int bch_set_geometry(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct bch_geometry *bch_geo = &this->bch_geometry; + unsigned int block_count; + unsigned int block_size; + unsigned int metadata_size; + unsigned int ecc_strength; + unsigned int page_size; + unsigned int gf_len; + int ret; + + if (common_nfc_set_geometry(this)) + return !0; + + block_count = bch_geo->ecc_chunk_count - 1; + block_size = bch_geo->ecc_chunk_size; + metadata_size = bch_geo->metadata_size; + ecc_strength = bch_geo->ecc_strength >> 1; + page_size = bch_geo->page_size; + gf_len = bch_geo->gf_len; + + ret = gpmi_enable_clk(this); + if (ret) + goto err_out; + + /* + * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this + * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. + * On the other hand, the MX28 needs the reset, because one case has been + * seen where the BCH produced ECC errors constantly after 10000 + * consecutive reboots. The latter case has not been seen on the MX23 + * yet, still we don't know if it could happen there as well. + */ + ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); + if (ret) + goto err_out; + + /* Configure layout 0. */ + writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) + | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), + r->bch_regs + HW_BCH_FLASH0LAYOUT0); + + writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), + r->bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* Set *all* chip selects to use layout 0. */ + writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); + + /* Enable interrupts. */ + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + r->bch_regs + HW_BCH_CTRL_SET); + + gpmi_disable_clk(this); + return 0; +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_threshod *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. + * If you set 100MHz to gpmi nand controller, the GPMI-clock's period + * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. + * + * <2> Secondly, we should know what's the frequency on the nand chip pins. + * The frequency on the nand chip pins is derived from the GPMI-clock. + * We can get it from the following equation: + * + * F = G / (DS + DH) + * + * F : the frequency on the nand chip pins. + * G : the GPMI clock, such as 100MHz. + * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP + * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD + * + * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, + * the nand EDO(extended Data Out) timing could be applied. + * The GPMI implements a feedback read strobe to sample the read data. + * The feedback read strobe can be delayed to support the nand EDO timing + * where the read strobe may deasserts before the read data is valid, and + * read data is valid for some time after read strobe. + * + * The following figure illustrates some aspects of a NAND Flash read: + * + * |<---tREA---->| + * | | + * | | | + * |<--tRP-->| | + * | | | + * __ ___|__________________________________ + * RDN \________/ | + * | + * /---------\ + * Read Data --------------< >--------- + * \---------/ + * | | + * |<-D->| + * FeedbackRDN ________ ____________ + * \___________/ + * + * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. + * + * + * <4> Now, we begin to describe how to compute the right RDN_DELAY. + * + * 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 + * + * 4.2) From the aspect of the GPMI nand controller: + * Delay = RDN_DELAY * 0.125 * RP {2} + * + * RP : the DLL reference period. + * if (GPMI-clock-period > DLL_THRETHOLD) + * RP = GPMI-clock-period / 2; + * else + * RP = GPMI-clock-period; + * + * 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. + * + * 4.3) since {1} equals {2}, we get: + * + * (tREA + 4 - 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) +{ + 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; + + /* + * [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); + + /* [2] for GPMI_HW_GPMI_TIMING1 */ + hw->device_busy_timeout = 0x9000; + + /* [3] for GPMI_HW_GPMI_CTRL1 */ + hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + + /* + * Enlarge 10 times for the numerator and denominator in {3}. + * This make us to get more accurate result. + */ + 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; + } else { + hw->use_half_periods = 0; + rp = clk_period; + } + + /* + * 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 = &this->mtd; + 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 commond 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; + + return enable_edo_mode(this, mode); + } + return 0; +} + +/* Begin the I/O */ +void gpmi_begin(struct gpmi_nand_data *this) +{ + 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; + + 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); + + /* [3] The following code is to set the HW_GPMI_CTRL1. */ + + /* 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); + + /* If no sample delay is called for, return immediately. */ + if (!hw.sample_delay_factor) + return; + + /* 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); + + writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* At last, we enable the DLL. */ + writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * 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; + + if (!dll_wait_time_in_us) + dll_wait_time_in_us = 1; + + /* Wait for the DLL to settle. */ + udelay(dll_wait_time_in_us); + +err_out: + return; +} + +void gpmi_end(struct gpmi_nand_data *this) +{ + gpmi_disable_clk(this); +} + +/* Clears a BCH interrupt. */ +void gpmi_clear_bch(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); +} + +/* Returns the Ready/Busy status of the given chip. */ +int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) +{ + struct resources *r = &this->resources; + uint32_t mask = 0; + uint32_t reg = 0; + + if (GPMI_IS_MX23(this)) { + mask = MX23_BM_GPMI_DEBUG_READY0 << chip; + reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); + } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) { + /* + * In the imx6, all the ready/busy pins are bound + * together. So we only need to check chip 0. + */ + if (GPMI_IS_MX6(this)) + chip = 0; + + /* MX28 shares the same R/B register as MX6Q. */ + mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); + reg = readl(r->gpmi_regs + HW_GPMI_STAT); + } else + dev_err(this->dev, "unknow arch.\n"); + return reg & mask; +} + +static inline void set_dma_type(struct gpmi_nand_data *this, + enum dma_ops_type type) +{ + this->last_dma_type = this->dma_type; + this->dma_type = type; +} + +int gpmi_send_command(struct gpmi_nand_data *this) +{ + struct dma_chan *channel = get_dma_chan(this); + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl; + int chip = this->current_chip; + u32 pio[3]; + + /* [1] send out the PIO words */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) + | BM_GPMI_CTRL0_ADDRESS_INCREMENT + | BF_GPMI_CTRL0_XFER_COUNT(this->command_length); + pio[1] = pio[2] = 0; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ + sgl = &this->cmd_sgl; + + sg_init_one(sgl, this->cmd_buffer, this->command_length); + dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, + sgl, 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] submit the DMA */ + set_dma_type(this, DMA_FOR_COMMAND); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_send_data(struct gpmi_nand_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + uint32_t command_mode; + uint32_t address; + u32 pio[2]; + + /* [1] PIO */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + pio[1] = 0; + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] send DMA request */ + prepare_data_dma(this, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, + 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] submit the DMA */ + set_dma_type(this, DMA_FOR_WRITE_DATA); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_read_data(struct gpmi_nand_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + u32 pio[2]; + + /* [1] : send PIO */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) + | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + pio[1] = 0; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] : send DMA request */ + prepare_data_dma(this, DMA_FROM_DEVICE); + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, + 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] : submit the DMA */ + set_dma_type(this, DMA_FOR_READ_DATA); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_send_page(struct gpmi_nand_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct bch_geometry *geo = &this->bch_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + u32 pio[6]; + + /* A DMA descriptor that does an ECC page read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size; + pio[4] = payload; + pio[5] = auxiliary; + + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); + return start_dma_with_bch_irq(this, desc); +} + +int gpmi_read_page(struct gpmi_nand_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct bch_geometry *geo = &this->bch_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + u32 pio[6]; + + /* [1] Wait for the chip to report ready. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, 2, + DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] Enable the BCH block and read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE + | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); + + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size; + pio[4] = payload; + pio[5] = auxiliary; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] Disable the BCH block */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); + pio[1] = 0; + pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, 3, + DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [4] submit the DMA */ + set_dma_type(this, DMA_FOR_READ_ECC_PAGE); + return start_dma_with_bch_irq(this, desc); +} diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c new file mode 100644 index 00000000000..f638cd8077c --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -0,0 +1,1844 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mtd/partitions.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_mtd.h> +#include "gpmi-nand.h" +#include "bch-regs.h" + +/* Resource names for the GPMI NAND driver. */ +#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" +#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" +#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" + +/* add our owner bbt descriptor */ +static uint8_t scan_ff_pattern[] = { 0xff }; +static struct nand_bbt_descr gpmi_bbt_descr = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern +}; + +/* + * We may change the layout if we can get the ECC info from the datasheet, + * else we will use all the (page + OOB). + */ +static struct nand_ecclayout gpmi_hw_ecclayout = { + .eccbytes = 0, + .eccpos = { 0, }, + .oobfree = { {.offset = 0, .length = 0} } +}; + +static const struct gpmi_devdata gpmi_devdata_imx23 = { + .type = IS_MX23, + .bch_max_ecc_strength = 20, + .max_chain_delay = 16, +}; + +static const struct gpmi_devdata gpmi_devdata_imx28 = { + .type = IS_MX28, + .bch_max_ecc_strength = 20, + .max_chain_delay = 16, +}; + +static const struct gpmi_devdata gpmi_devdata_imx6q = { + .type = IS_MX6Q, + .bch_max_ecc_strength = 40, + .max_chain_delay = 12, +}; + +static const struct gpmi_devdata gpmi_devdata_imx6sx = { + .type = IS_MX6SX, + .bch_max_ecc_strength = 62, + .max_chain_delay = 12, +}; + +static irqreturn_t bch_irq(int irq, void *cookie) +{ + struct gpmi_nand_data *this = cookie; + + gpmi_clear_bch(this); + complete(&this->bch_done); + return IRQ_HANDLED; +} + +/* + * Calculate the ECC strength by hand: + * E : The ECC strength. + * G : the length of Galois Field. + * N : The chunk count of per page. + * O : the oobsize of the NAND chip. + * M : the metasize of per page. + * + * The formula is : + * E * G * N + * ------------ <= (O - M) + * 8 + * + * So, we get E by: + * (O - M) * 8 + * E <= ------------- + * G * N + */ +static inline int get_ecc_strength(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = &this->mtd; + int ecc_strength; + + ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) + / (geo->gf_len * geo->ecc_chunk_count); + + /* We need the minor even number. */ + return round_down(ecc_strength, 2); +} + +static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + + /* Do the sanity check. */ + if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) { + /* The mx23/mx28 only support the GF13. */ + if (geo->gf_len == 14) + return false; + } + return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; +} + +/* + * If we can get the ECC information from the nand chip, we do not + * need to calculate them ourselves. + * + * We may have available oob space in this case. + */ +static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = &this->mtd; + struct nand_chip *chip = mtd->priv; + struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree; + unsigned int block_mark_bit_offset; + + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return false; + + switch (chip->ecc_step_ds) { + case SZ_512: + geo->gf_len = 13; + break; + case SZ_1K: + geo->gf_len = 14; + break; + default: + dev_err(this->dev, + "unsupported nand chip. ecc bits : %d, ecc size : %d\n", + chip->ecc_strength_ds, chip->ecc_step_ds); + return false; + } + geo->ecc_chunk_size = chip->ecc_step_ds; + geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + if (!gpmi_check_ecc(this)) + return false; + + /* Keep the C >= O */ + if (geo->ecc_chunk_size < mtd->oobsize) { + dev_err(this->dev, + "unsupported nand chip. ecc size: %d, oob size : %d\n", + chip->ecc_step_ds, mtd->oobsize); + return false; + } + + /* The default value, see comment in the legacy_set_geometry(). */ + geo->metadata_size = 10; + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* + * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: + * + * | P | + * |<----------------------------------------------------->| + * | | + * | (Block Mark) | + * | P' | | | | + * |<-------------------------------------------->| D | | O' | + * | |<---->| |<--->| + * V V V V V + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * | M | data |E| data |E| data |E| data |E| | + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * ^ ^ + * | O | + * |<------------>| + * | | + * + * P : the page size for BCH module. + * E : The ECC strength. + * G : the length of Galois Field. + * N : The chunk count of per page. + * M : the metasize of per page. + * C : the ecc chunk size, aka the "data" above. + * P': the nand chip's page size. + * O : the nand chip's oob size. + * O': the free oob. + * + * The formula for P is : + * + * E * G * N + * P = ------------ + P' + M + * 8 + * + * The position of block mark moves forward in the ECC-based view + * of page, and the delta is: + * + * E * G * (N - 1) + * D = (---------------- + M) + * 8 + * + * Please see the comment in legacy_set_geometry(). + * With the condition C >= O , we still can get same result. + * So the bit position of the physical block mark within the ECC-based + * view of the page is : + * (P' - D) * 8 + */ + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; + + /* The available oob size we have. */ + if (geo->page_size < mtd->writesize + mtd->oobsize) { + of->offset = geo->page_size - mtd->writesize; + of->length = mtd->oobsize - of->offset; + } + + geo->payload_size = mtd->writesize; + + geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); + geo->auxiliary_size = ALIGN(geo->metadata_size, 4) + + ALIGN(geo->ecc_chunk_count, 4); + + if (!this->swap_block_mark) + return true; + + /* For bit swap. */ + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + return true; +} + +static int legacy_set_geometry(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = &this->mtd; + unsigned int metadata_size; + unsigned int status_size; + unsigned int block_mark_bit_offset; + + /* + * The size of the metadata can be changed, though we set it to 10 + * bytes now. But it can't be too large, because we have to save + * enough space for BCH. + */ + geo->metadata_size = 10; + + /* The default for the length of Galois Field. */ + geo->gf_len = 13; + + /* The default for chunk size. */ + geo->ecc_chunk_size = 512; + while (geo->ecc_chunk_size < mtd->oobsize) { + geo->ecc_chunk_size *= 2; /* keep C >= O */ + geo->gf_len = 14; + } + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* We use the same ECC strength for all chunks. */ + geo->ecc_strength = get_ecc_strength(this); + if (!gpmi_check_ecc(this)) { + dev_err(this->dev, + "We can not support this nand chip." + " Its required ecc strength(%d) is beyond our" + " capability(%d).\n", geo->ecc_strength, + this->devdata->bch_max_ecc_strength); + return -EINVAL; + } + + geo->page_size = mtd->writesize + mtd->oobsize; + geo->payload_size = mtd->writesize; + + /* + * The auxiliary buffer contains the metadata and the ECC status. The + * metadata is padded to the nearest 32-bit boundary. The ECC status + * contains one byte for every ECC chunk, and is also padded to the + * nearest 32-bit boundary. + */ + metadata_size = ALIGN(geo->metadata_size, 4); + status_size = ALIGN(geo->ecc_chunk_count, 4); + + geo->auxiliary_size = metadata_size + status_size; + geo->auxiliary_status_offset = metadata_size; + + if (!this->swap_block_mark) + return 0; + + /* + * We need to compute the byte and bit offsets of + * the physical block mark within the ECC-based view of the page. + * + * NAND chip with 2K page shows below: + * (Block Mark) + * | | + * | D | + * |<---->| + * V V + * +---+----------+-+----------+-+----------+-+----------+-+ + * | M | data |E| data |E| data |E| data |E| + * +---+----------+-+----------+-+----------+-+----------+-+ + * + * The position of block mark moves forward in the ECC-based view + * of page, and the delta is: + * + * E * G * (N - 1) + * D = (---------------- + M) + * 8 + * + * With the formula to compute the ECC strength, and the condition + * : C >= O (C is the ecc chunk size) + * + * It's easy to deduce to the following result: + * + * E * G (O - M) C - M C - M + * ----------- <= ------- <= -------- < --------- + * 8 N N (N - 1) + * + * So, we get: + * + * E * G * (N - 1) + * D = (---------------- + M) < C + * 8 + * + * The above inequality means the position of block mark + * within the ECC-based view of the page is still in the data chunk, + * and it's NOT in the ECC bits of the chunk. + * + * Use the following to compute the bit position of the + * physical block mark within the ECC-based view of the page: + * (page_size - D) * 8 + * + * --Huang Shijie + */ + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + return 0; +} + +int common_nfc_set_geometry(struct gpmi_nand_data *this) +{ + if (of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc") + && set_geometry_by_ecc_info(this)) + return 0; + return legacy_set_geometry(this); +} + +struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) +{ + /* We use the DMA channel 0 to access all the nand chips. */ + return this->dma_chans[0]; +} + +/* Can we use the upper's buffer directly for DMA? */ +void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) +{ + struct scatterlist *sgl = &this->data_sgl; + int ret; + + /* first try to map the upper buffer directly */ + if (virt_addr_valid(this->upper_buf) && + !object_is_on_stack(this->upper_buf)) { + sg_init_one(sgl, this->upper_buf, this->upper_len); + ret = dma_map_sg(this->dev, sgl, 1, dr); + if (ret == 0) + goto map_fail; + + this->direct_dma_map_ok = true; + return; + } + +map_fail: + /* We have to use our own DMA buffer. */ + sg_init_one(sgl, this->data_buffer_dma, this->upper_len); + + if (dr == DMA_TO_DEVICE) + memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len); + + dma_map_sg(this->dev, sgl, 1, dr); + + this->direct_dma_map_ok = false; +} + +/* This will be called after the DMA operation is finished. */ +static void dma_irq_callback(void *param) +{ + struct gpmi_nand_data *this = param; + struct completion *dma_c = &this->dma_done; + + switch (this->dma_type) { + case DMA_FOR_COMMAND: + dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); + break; + + case DMA_FOR_READ_DATA: + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); + if (this->direct_dma_map_ok == false) + memcpy(this->upper_buf, this->data_buffer_dma, + this->upper_len); + break; + + case DMA_FOR_WRITE_DATA: + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); + break; + + case DMA_FOR_READ_ECC_PAGE: + case DMA_FOR_WRITE_ECC_PAGE: + /* We have to wait the BCH interrupt to finish. */ + break; + + default: + dev_err(this->dev, "in wrong DMA operation.\n"); + } + + complete(dma_c); +} + +int start_dma_without_bch_irq(struct gpmi_nand_data *this, + struct dma_async_tx_descriptor *desc) +{ + struct completion *dma_c = &this->dma_done; + int err; + + init_completion(dma_c); + + desc->callback = dma_irq_callback; + desc->callback_param = this; + dmaengine_submit(desc); + dma_async_issue_pending(get_dma_chan(this)); + + /* Wait for the interrupt from the DMA block. */ + err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); + if (!err) { + dev_err(this->dev, "DMA timeout, last DMA :%d\n", + this->last_dma_type); + gpmi_dump_info(this); + return -ETIMEDOUT; + } + return 0; +} + +/* + * This function is used in BCH reading or BCH writing pages. + * It will wait for the BCH interrupt as long as ONE second. + * Actually, we must wait for two interrupts : + * [1] firstly the DMA interrupt and + * [2] secondly the BCH interrupt. + */ +int start_dma_with_bch_irq(struct gpmi_nand_data *this, + struct dma_async_tx_descriptor *desc) +{ + struct completion *bch_c = &this->bch_done; + int err; + + /* Prepare to receive an interrupt from the BCH block. */ + init_completion(bch_c); + + /* start the DMA */ + start_dma_without_bch_irq(this, desc); + + /* Wait for the interrupt from the BCH block. */ + err = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); + if (!err) { + dev_err(this->dev, "BCH timeout, last DMA :%d\n", + this->last_dma_type); + gpmi_dump_info(this); + return -ETIMEDOUT; + } + return 0; +} + +static int acquire_register_block(struct gpmi_nand_data *this, + const char *res_name) +{ + struct platform_device *pdev = this->pdev; + struct resources *res = &this->resources; + struct resource *r; + void __iomem *p; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); + p = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(p)) + return PTR_ERR(p); + + if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)) + res->gpmi_regs = p; + else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME)) + res->bch_regs = p; + else + dev_err(this->dev, "unknown resource name : %s\n", res_name); + + return 0; +} + +static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) +{ + struct platform_device *pdev = this->pdev; + const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME; + struct resource *r; + int err; + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); + if (!r) { + dev_err(this->dev, "Can't get resource for %s\n", res_name); + return -ENODEV; + } + + err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this); + if (err) + dev_err(this->dev, "error requesting BCH IRQ\n"); + + return err; +} + +static void release_dma_channels(struct gpmi_nand_data *this) +{ + unsigned int i; + for (i = 0; i < DMA_CHANS; i++) + if (this->dma_chans[i]) { + dma_release_channel(this->dma_chans[i]); + this->dma_chans[i] = NULL; + } +} + +static int acquire_dma_channels(struct gpmi_nand_data *this) +{ + struct platform_device *pdev = this->pdev; + struct dma_chan *dma_chan; + + /* request dma channel */ + dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx"); + if (!dma_chan) { + dev_err(this->dev, "Failed to request DMA channel.\n"); + goto acquire_err; + } + + this->dma_chans[0] = dma_chan; + return 0; + +acquire_err: + release_dma_channels(this); + return -EINVAL; +} + +static char *extra_clks_for_mx6q[GPMI_CLK_MAX] = { + "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", +}; + +static int gpmi_get_clks(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + char **extra_clks = NULL; + struct clk *clk; + int err, i; + + /* The main clock is stored in the first. */ + r->clock[0] = devm_clk_get(this->dev, "gpmi_io"); + if (IS_ERR(r->clock[0])) { + err = PTR_ERR(r->clock[0]); + goto err_clock; + } + + /* Get extra clocks */ + if (GPMI_IS_MX6(this)) + extra_clks = extra_clks_for_mx6q; + if (!extra_clks) + return 0; + + for (i = 1; i < GPMI_CLK_MAX; i++) { + if (extra_clks[i - 1] == NULL) + break; + + clk = devm_clk_get(this->dev, extra_clks[i - 1]); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + goto err_clock; + } + + r->clock[i] = clk; + } + + if (GPMI_IS_MX6(this)) + /* + * Set the default value for the gpmi clock. + * + * If you want to use the ONFI nand which is in the + * Synchronous Mode, you should change the clock as you need. + */ + clk_set_rate(r->clock[0], 22000000); + + return 0; + +err_clock: + dev_dbg(this->dev, "failed in finding the clocks.\n"); + return err; +} + +static int acquire_resources(struct gpmi_nand_data *this) +{ + int ret; + + ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); + if (ret) + goto exit_regs; + + ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME); + if (ret) + goto exit_regs; + + ret = acquire_bch_irq(this, bch_irq); + if (ret) + goto exit_regs; + + ret = acquire_dma_channels(this); + if (ret) + goto exit_regs; + + ret = gpmi_get_clks(this); + if (ret) + goto exit_clock; + return 0; + +exit_clock: + release_dma_channels(this); +exit_regs: + return ret; +} + +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, + void **use_virt, dma_addr_t *use_phys) +{ + struct device *dev = this->dev; + + if (virt_addr_valid(destination)) { + dma_addr_t dest_phys; + + dest_phys = dma_map_single(dev, destination, + length, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, dest_phys)) { + if (alt_size < length) { + dev_err(dev, "Alternate buffer is too small\n"); + return -ENOMEM; + } + goto map_failed; + } + *use_virt = destination; + *use_phys = dest_phys; + this->direct_dma_map_ok = true; + return 0; + } + +map_failed: + *use_virt = alt_virt; + *use_phys = alt_phys; + this->direct_dma_map_ok = false; + return 0; +} + +static inline void read_page_end(struct gpmi_nand_data *this, + void *destination, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + void *used_virt, dma_addr_t used_phys) +{ + if (this->direct_dma_map_ok) + dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE); +} + +static inline void read_page_swap_end(struct gpmi_nand_data *this, + void *destination, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + void *used_virt, dma_addr_t used_phys) +{ + if (!this->direct_dma_map_ok) + memcpy(destination, alt_virt, length); +} + +static int send_page_prepare(struct gpmi_nand_data *this, + const void *source, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + const void **use_virt, dma_addr_t *use_phys) +{ + struct device *dev = this->dev; + + if (virt_addr_valid(source)) { + dma_addr_t source_phys; + + source_phys = dma_map_single(dev, (void *)source, length, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, source_phys)) { + if (alt_size < length) { + dev_err(dev, "Alternate buffer is too small\n"); + return -ENOMEM; + } + goto map_failed; + } + *use_virt = source; + *use_phys = source_phys; + return 0; + } +map_failed: + /* + * Copy the content of the source buffer into the alternate + * buffer and set up the return values accordingly. + */ + memcpy(alt_virt, source, length); + + *use_virt = alt_virt; + *use_phys = alt_phys; + return 0; +} + +static void send_page_end(struct gpmi_nand_data *this, + const void *source, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + const void *used_virt, dma_addr_t used_phys) +{ + struct device *dev = this->dev; + if (used_virt == source) + dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE); +} + +static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) +{ + struct device *dev = this->dev; + + if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt)) + dma_free_coherent(dev, this->page_buffer_size, + this->page_buffer_virt, + this->page_buffer_phys); + kfree(this->cmd_buffer); + kfree(this->data_buffer_dma); + + this->cmd_buffer = NULL; + this->data_buffer_dma = NULL; + this->page_buffer_virt = NULL; + this->page_buffer_size = 0; +} + +/* Allocate the DMA buffers */ +static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct device *dev = this->dev; + struct mtd_info *mtd = &this->mtd; + + /* [1] Allocate a command buffer. PAGE_SIZE is enough. */ + this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL); + if (this->cmd_buffer == NULL) + goto error_alloc; + + /* + * [2] Allocate a read/write data buffer. + * The gpmi_alloc_dma_buffer can be called twice. + * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer + * is called before the nand_scan_ident; and we allocate a buffer + * of the real NAND page size when the gpmi_alloc_dma_buffer is + * called after the nand_scan_ident. + */ + this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE, + GFP_DMA | GFP_KERNEL); + if (this->data_buffer_dma == NULL) + goto error_alloc; + + /* + * [3] Allocate the page buffer. + * + * Both the payload buffer and the auxiliary buffer must appear on + * 32-bit boundaries. We presume the size of the payload buffer is a + * power of two and is much larger than four, which guarantees the + * auxiliary buffer will appear on a 32-bit boundary. + */ + this->page_buffer_size = geo->payload_size + geo->auxiliary_size; + this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size, + &this->page_buffer_phys, GFP_DMA); + if (!this->page_buffer_virt) + goto error_alloc; + + + /* Slice up the page buffer. */ + this->payload_virt = this->page_buffer_virt; + this->payload_phys = this->page_buffer_phys; + this->auxiliary_virt = this->payload_virt + geo->payload_size; + this->auxiliary_phys = this->payload_phys + geo->payload_size; + return 0; + +error_alloc: + gpmi_free_dma_buffer(this); + return -ENOMEM; +} + +static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + struct gpmi_nand_data *this = chip->priv; + int ret; + + /* + * Every operation begins with a command byte and a series of zero or + * more address bytes. These are distinguished by either the Address + * Latch Enable (ALE) or Command Latch Enable (CLE) signals being + * asserted. When MTD is ready to execute the command, it will deassert + * both latch enables. + * + * Rather than run a separate DMA operation for every single byte, we + * queue them up and run a single DMA operation for the entire series + * of command and data bytes. NAND_CMD_NONE means the END of the queue. + */ + if ((ctrl & (NAND_ALE | NAND_CLE))) { + if (data != NAND_CMD_NONE) + this->cmd_buffer[this->command_length++] = data; + return; + } + + if (!this->command_length) + return; + + ret = gpmi_send_command(this); + if (ret) + dev_err(this->dev, "Chip: %u, Error %d\n", + this->current_chip, ret); + + this->command_length = 0; +} + +static int gpmi_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct gpmi_nand_data *this = chip->priv; + + return gpmi_is_ready(this, this->current_chip); +} + +static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd->priv; + struct gpmi_nand_data *this = chip->priv; + + if ((this->current_chip < 0) && (chipnr >= 0)) + gpmi_begin(this); + else if ((this->current_chip >= 0) && (chipnr < 0)) + gpmi_end(this); + + this->current_chip = chipnr; +} + +static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct gpmi_nand_data *this = chip->priv; + + dev_dbg(this->dev, "len is %d\n", len); + this->upper_buf = buf; + this->upper_len = len; + + gpmi_read_data(this); +} + +static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct gpmi_nand_data *this = chip->priv; + + dev_dbg(this->dev, "len is %d\n", len); + this->upper_buf = (uint8_t *)buf; + this->upper_len = len; + + gpmi_send_data(this); +} + +static uint8_t gpmi_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct gpmi_nand_data *this = chip->priv; + uint8_t *buf = this->data_buffer_dma; + + gpmi_read_buf(mtd, buf, 1); + return buf[0]; +} + +/* + * Handles block mark swapping. + * It can be called in swapping the block mark, or swapping it back, + * because the the operations are the same. + */ +static void block_mark_swapping(struct gpmi_nand_data *this, + void *payload, void *auxiliary) +{ + struct bch_geometry *nfc_geo = &this->bch_geometry; + unsigned char *p; + unsigned char *a; + unsigned int bit; + unsigned char mask; + unsigned char from_data; + unsigned char from_oob; + + if (!this->swap_block_mark) + return; + + /* + * If control arrives here, we're swapping. Make some convenience + * variables. + */ + bit = nfc_geo->block_mark_bit_offset; + p = payload + nfc_geo->block_mark_byte_offset; + a = auxiliary; + + /* + * Get the byte from the data area that overlays the block mark. Since + * the ECC engine applies its own view to the bits in the page, the + * physical block mark won't (in general) appear on a byte boundary in + * the data. + */ + from_data = (p[0] >> bit) | (p[1] << (8 - bit)); + + /* Get the byte from the OOB. */ + from_oob = a[0]; + + /* Swap them. */ + a[0] = from_data; + + mask = (0x1 << bit) - 1; + p[0] = (p[0] & mask) | (from_oob << bit); + + mask = ~0 << bit; + p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); +} + +static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct gpmi_nand_data *this = chip->priv; + struct bch_geometry *nfc_geo = &this->bch_geometry; + void *payload_virt; + dma_addr_t payload_phys; + void *auxiliary_virt; + dma_addr_t auxiliary_phys; + unsigned int i; + unsigned char *status; + unsigned int max_bitflips = 0; + int ret; + + dev_dbg(this->dev, "page number is : %d\n", page); + ret = read_page_prepare(this, buf, nfc_geo->payload_size, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + &payload_virt, &payload_phys); + if (ret) { + dev_err(this->dev, "Inadequate DMA buffer\n"); + ret = -ENOMEM; + return ret; + } + auxiliary_virt = this->auxiliary_virt; + auxiliary_phys = this->auxiliary_phys; + + /* go! */ + ret = gpmi_read_page(this, payload_phys, auxiliary_phys); + read_page_end(this, buf, nfc_geo->payload_size, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + if (ret) { + dev_err(this->dev, "Error in ECC-based read: %d\n", ret); + return ret; + } + + /* handle the block mark swapping */ + block_mark_swapping(this, payload_virt, auxiliary_virt); + + /* Loop over status bytes, accumulating ECC status. */ + status = auxiliary_virt + nfc_geo->auxiliary_status_offset; + + for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { + if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) + continue; + + if (*status == STATUS_UNCORRECTABLE) { + mtd->ecc_stats.failed++; + continue; + } + mtd->ecc_stats.corrected += *status; + max_bitflips = max_t(unsigned int, max_bitflips, *status); + } + + if (oob_required) { + /* + * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() + * for details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the + * block mark to th caller's buffer. Note that, if block mark + * swapping was necessary, it has already been done, so we can + * rely on the first byte of the auxiliary buffer to contain + * the block mark. + */ + memset(chip->oob_poi, ~0, mtd->oobsize); + chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + } + + read_page_swap_end(this, buf, nfc_geo->payload_size, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + + return max_bitflips; +} + +/* Fake a virtual small page for the subpage read */ +static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offs, uint32_t len, uint8_t *buf, int page) +{ + struct gpmi_nand_data *this = chip->priv; + void __iomem *bch_regs = this->resources.bch_regs; + struct bch_geometry old_geo = this->bch_geometry; + struct bch_geometry *geo = &this->bch_geometry; + int size = chip->ecc.size; /* ECC chunk size */ + int meta, n, page_size; + u32 r1_old, r2_old, r1_new, r2_new; + unsigned int max_bitflips; + int first, last, marker_pos; + int ecc_parity_size; + int col = 0; + + /* The size of ECC parity */ + ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; + + /* Align it with the chunk size */ + first = offs / size; + last = (offs + len - 1) / size; + + /* + * Find the chunk which contains the Block Marker. If this chunk is + * in the range of [first, last], we have to read out the whole page. + * Why? since we had swapped the data at the position of Block Marker + * to the metadata which is bound with the chunk 0. + */ + marker_pos = geo->block_mark_byte_offset / size; + if (last >= marker_pos && first <= marker_pos) { + dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n", + page, first, last, marker_pos); + return gpmi_ecc_read_page(mtd, chip, buf, 0, page); + } + + meta = geo->metadata_size; + if (first) { + col = meta + (size + ecc_parity_size) * first; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1); + + meta = 0; + buf = buf + first * size; + } + + /* Save the old environment */ + r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); + r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* change the BCH registers and bch_geometry{} */ + n = last - first + 1; + page_size = meta + (size + ecc_parity_size) * n; + + r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS | + BM_BCH_FLASH0LAYOUT0_META_SIZE); + r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) + | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta); + writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0); + + r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE; + r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size); + writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1); + + geo->ecc_chunk_count = n; + geo->payload_size = n * size; + geo->page_size = page_size; + geo->auxiliary_status_offset = ALIGN(meta, 4); + + dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", + page, offs, len, col, first, n, page_size); + + /* Read the subpage now */ + this->swap_block_mark = false; + max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page); + + /* Restore */ + writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0); + writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1); + this->bch_geometry = old_geo; + this->swap_block_mark = true; + + return max_bitflips; +} + +static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct gpmi_nand_data *this = chip->priv; + struct bch_geometry *nfc_geo = &this->bch_geometry; + const void *payload_virt; + dma_addr_t payload_phys; + const void *auxiliary_virt; + dma_addr_t auxiliary_phys; + int ret; + + dev_dbg(this->dev, "ecc write page.\n"); + if (this->swap_block_mark) { + /* + * If control arrives here, we're doing block mark swapping. + * Since we can't modify the caller's buffers, we must copy them + * into our own. + */ + memcpy(this->payload_virt, buf, mtd->writesize); + payload_virt = this->payload_virt; + payload_phys = this->payload_phys; + + memcpy(this->auxiliary_virt, chip->oob_poi, + nfc_geo->auxiliary_size); + auxiliary_virt = this->auxiliary_virt; + auxiliary_phys = this->auxiliary_phys; + + /* Handle block mark swapping. */ + block_mark_swapping(this, + (void *) payload_virt, (void *) auxiliary_virt); + } else { + /* + * If control arrives here, we're not doing block mark swapping, + * so we can to try and use the caller's buffers. + */ + ret = send_page_prepare(this, + buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + &payload_virt, &payload_phys); + if (ret) { + dev_err(this->dev, "Inadequate payload DMA buffer\n"); + return 0; + } + + ret = send_page_prepare(this, + chip->oob_poi, mtd->oobsize, + this->auxiliary_virt, this->auxiliary_phys, + nfc_geo->auxiliary_size, + &auxiliary_virt, &auxiliary_phys); + if (ret) { + dev_err(this->dev, "Inadequate auxiliary DMA buffer\n"); + goto exit_auxiliary; + } + } + + /* Ask the NFC. */ + ret = gpmi_send_page(this, payload_phys, auxiliary_phys); + if (ret) + dev_err(this->dev, "Error in ECC-based write: %d\n", ret); + + if (!this->swap_block_mark) { + send_page_end(this, chip->oob_poi, mtd->oobsize, + this->auxiliary_virt, this->auxiliary_phys, + nfc_geo->auxiliary_size, + auxiliary_virt, auxiliary_phys); +exit_auxiliary: + send_page_end(this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + } + + return 0; +} + +/* + * There are several places in this driver where we have to handle the OOB and + * block marks. This is the function where things are the most complicated, so + * this is where we try to explain it all. All the other places refer back to + * here. + * + * These are the rules, in order of decreasing importance: + * + * 1) Nothing the caller does can be allowed to imperil the block mark. + * + * 2) In read operations, the first byte of the OOB we return must reflect the + * true state of the block mark, no matter where that block mark appears in + * the physical page. + * + * 3) ECC-based read operations return an OOB full of set bits (since we never + * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads + * return). + * + * 4) "Raw" read operations return a direct view of the physical bytes in the + * page, using the conventional definition of which bytes are data and which + * are OOB. This gives the caller a way to see the actual, physical bytes + * in the page, without the distortions applied by our ECC engine. + * + * + * What we do for this specific read operation depends on two questions: + * + * 1) Are we doing a "raw" read, or an ECC-based read? + * + * 2) Are we using block mark swapping or transcription? + * + * There are four cases, illustrated by the following Karnaugh map: + * + * | Raw | ECC-based | + * -------------+-------------------------+-------------------------+ + * | Read the conventional | | + * | OOB at the end of the | | + * Swapping | page and return it. It | | + * | contains exactly what | | + * | we want. | Read the block mark and | + * -------------+-------------------------+ return it in a buffer | + * | Read the conventional | full of set bits. | + * | OOB at the end of the | | + * | page and also the block | | + * Transcribing | mark in the metadata. | | + * | Copy the block mark | | + * | into the first byte of | | + * | the OOB. | | + * -------------+-------------------------+-------------------------+ + * + * Note that we break rule #4 in the Transcribing/Raw case because we're not + * giving an accurate view of the actual, physical bytes in the page (we're + * overwriting the block mark). That's OK because it's more important to follow + * rule #2. + * + * It turns out that knowing whether we want an "ECC-based" or "raw" read is not + * easy. When reading a page, for example, the NAND Flash MTD code calls our + * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an + * ECC-based or raw view of the page is implicit in which function it calls + * (there is a similar pair of ECC-based/raw functions for writing). + * + * FIXME: The following paragraph is incorrect, now that there exist + * ecc.read_oob_raw and ecc.write_oob_raw functions. + * + * Since MTD assumes the OOB is not covered by ECC, there is no pair of + * ECC-based/raw functions for reading or or writing the OOB. The fact that the + * caller wants an ECC-based or raw view of the page is not propagated down to + * this driver. + */ +static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct gpmi_nand_data *this = chip->priv; + + dev_dbg(this->dev, "page number is %d\n", page); + /* clear the OOB buffer */ + memset(chip->oob_poi, ~0, mtd->oobsize); + + /* Read out the conventional OOB. */ + chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* + * Now, we want to make sure the block mark is correct. In the + * Swapping/Raw case, we already have it. Otherwise, we need to + * explicitly read it. + */ + if (!this->swap_block_mark) { + /* Read the block mark into the first byte of the OOB buffer. */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + chip->oob_poi[0] = chip->read_byte(mtd); + } + + return 0; +} + +static int +gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) +{ + struct nand_oobfree *of = mtd->ecclayout->oobfree; + int status = 0; + + /* Do we have available oob area? */ + if (!of->length) + return -EPERM; + + if (!nand_is_slc(chip)) + return -EPERM; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of->offset, page); + chip->write_buf(mtd, chip->oob_poi + of->offset, of->length); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + struct gpmi_nand_data *this = chip->priv; + int ret = 0; + uint8_t *block_mark; + int column, page, status, chipnr; + + chipnr = (int)(ofs >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + column = this->swap_block_mark ? mtd->writesize : 0; + + /* Write the block mark. */ + block_mark = this->data_buffer_dma; + block_mark[0] = 0; /* bad block marker */ + + /* Shift to get page */ + page = (int)(ofs >> chip->page_shift); + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page); + chip->write_buf(mtd, block_mark, 1); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + ret = -EIO; + + chip->select_chip(mtd, -1); + + return ret; +} + +static int nand_boot_set_geometry(struct gpmi_nand_data *this) +{ + struct boot_rom_geometry *geometry = &this->rom_geometry; + + /* + * Set the boot block stride size. + * + * In principle, we should be reading this from the OTP bits, since + * that's where the ROM is going to get it. In fact, we don't have any + * way to read the OTP bits, so we go with the default and hope for the + * best. + */ + geometry->stride_size_in_pages = 64; + + /* + * Set the search area stride exponent. + * + * In principle, we should be reading this from the OTP bits, since + * that's where the ROM is going to get it. In fact, we don't have any + * way to read the OTP bits, so we go with the default and hope for the + * best. + */ + geometry->search_area_stride_exponent = 2; + return 0; +} + +static const char *fingerprint = "STMP"; +static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) +{ + struct boot_rom_geometry *rom_geo = &this->rom_geometry; + struct device *dev = this->dev; + struct mtd_info *mtd = &this->mtd; + struct nand_chip *chip = &this->nand; + unsigned int search_area_size_in_strides; + unsigned int stride; + unsigned int page; + uint8_t *buffer = chip->buffers->databuf; + int saved_chip_number; + int found_an_ncb_fingerprint = false; + + /* Compute the number of strides in a search area. */ + search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; + + saved_chip_number = this->current_chip; + chip->select_chip(mtd, 0); + + /* + * Loop through the first search area, looking for the NCB fingerprint. + */ + dev_dbg(dev, "Scanning for an NCB fingerprint...\n"); + + for (stride = 0; stride < search_area_size_in_strides; stride++) { + /* Compute the page addresses. */ + page = stride * rom_geo->stride_size_in_pages; + + dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page); + + /* + * Read the NCB fingerprint. The fingerprint is four bytes long + * and starts in the 12th byte of the page. + */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 12, page); + chip->read_buf(mtd, buffer, strlen(fingerprint)); + + /* Look for the fingerprint. */ + if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { + found_an_ncb_fingerprint = true; + break; + } + + } + + chip->select_chip(mtd, saved_chip_number); + + if (found_an_ncb_fingerprint) + dev_dbg(dev, "\tFound a fingerprint\n"); + else + dev_dbg(dev, "\tNo fingerprint found\n"); + return found_an_ncb_fingerprint; +} + +/* Writes a transcription stamp. */ +static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) +{ + struct device *dev = this->dev; + struct boot_rom_geometry *rom_geo = &this->rom_geometry; + struct mtd_info *mtd = &this->mtd; + struct nand_chip *chip = &this->nand; + unsigned int block_size_in_pages; + unsigned int search_area_size_in_strides; + unsigned int search_area_size_in_pages; + unsigned int search_area_size_in_blocks; + unsigned int block; + unsigned int stride; + unsigned int page; + uint8_t *buffer = chip->buffers->databuf; + int saved_chip_number; + int status; + + /* Compute the search area geometry. */ + block_size_in_pages = mtd->erasesize / mtd->writesize; + search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; + search_area_size_in_pages = search_area_size_in_strides * + rom_geo->stride_size_in_pages; + search_area_size_in_blocks = + (search_area_size_in_pages + (block_size_in_pages - 1)) / + block_size_in_pages; + + dev_dbg(dev, "Search Area Geometry :\n"); + dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks); + dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides); + dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages); + + /* Select chip 0. */ + saved_chip_number = this->current_chip; + chip->select_chip(mtd, 0); + + /* Loop over blocks in the first search area, erasing them. */ + dev_dbg(dev, "Erasing the search area...\n"); + + for (block = 0; block < search_area_size_in_blocks; block++) { + /* Compute the page address. */ + page = block * block_size_in_pages; + + /* Erase this block. */ + dev_dbg(dev, "\tErasing block 0x%x\n", block); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + /* Wait for the erase to finish. */ + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + dev_err(dev, "[%s] Erase failed.\n", __func__); + } + + /* Write the NCB fingerprint into the page buffer. */ + memset(buffer, ~0, mtd->writesize); + memcpy(buffer + 12, fingerprint, strlen(fingerprint)); + + /* Loop through the first search area, writing NCB fingerprints. */ + dev_dbg(dev, "Writing NCB fingerprints...\n"); + for (stride = 0; stride < search_area_size_in_strides; stride++) { + /* Compute the page addresses. */ + page = stride * rom_geo->stride_size_in_pages; + + /* Write the first page of the current stride. */ + dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + chip->ecc.write_page_raw(mtd, chip, buffer, 0); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* Wait for the write to finish. */ + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + dev_err(dev, "[%s] Write failed.\n", __func__); + } + + /* Deselect chip 0. */ + chip->select_chip(mtd, saved_chip_number); + return 0; +} + +static int mx23_boot_init(struct gpmi_nand_data *this) +{ + struct device *dev = this->dev; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = &this->mtd; + unsigned int block_count; + unsigned int block; + int chipnr; + int page; + loff_t byte; + uint8_t block_mark; + int ret = 0; + + /* + * If control arrives here, we can't use block mark swapping, which + * means we're forced to use transcription. First, scan for the + * transcription stamp. If we find it, then we don't have to do + * anything -- the block marks are already transcribed. + */ + if (mx23_check_transcription_stamp(this)) + return 0; + + /* + * If control arrives here, we couldn't find a transcription stamp, so + * so we presume the block marks are in the conventional location. + */ + dev_dbg(dev, "Transcribing bad block marks...\n"); + + /* Compute the number of blocks in the entire medium. */ + block_count = chip->chipsize >> chip->phys_erase_shift; + + /* + * Loop over all the blocks in the medium, transcribing block marks as + * we go. + */ + for (block = 0; block < block_count; block++) { + /* + * Compute the chip, page and byte addresses for this block's + * conventional mark. + */ + chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); + page = block << (chip->phys_erase_shift - chip->page_shift); + byte = block << chip->phys_erase_shift; + + /* Send the command to read the conventional block mark. */ + chip->select_chip(mtd, chipnr); + chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + block_mark = chip->read_byte(mtd); + chip->select_chip(mtd, -1); + + /* + * Check if the block is marked bad. If so, we need to mark it + * again, but this time the result will be a mark in the + * location where we transcribe block marks. + */ + if (block_mark != 0xff) { + dev_dbg(dev, "Transcribing mark in block %u\n", block); + ret = chip->block_markbad(mtd, byte); + if (ret) + dev_err(dev, "Failed to mark block bad with " + "ret %d\n", ret); + } + } + + /* Write the stamp that indicates we've transcribed the block marks. */ + mx23_write_transcription_stamp(this); + return 0; +} + +static int nand_boot_init(struct gpmi_nand_data *this) +{ + nand_boot_set_geometry(this); + + /* This is ROM arch-specific initilization before the BBT scanning. */ + if (GPMI_IS_MX23(this)) + return mx23_boot_init(this); + return 0; +} + +static int gpmi_set_geometry(struct gpmi_nand_data *this) +{ + int ret; + + /* Free the temporary DMA memory for reading ID. */ + gpmi_free_dma_buffer(this); + + /* Set up the NFC geometry which is used by BCH. */ + ret = bch_set_geometry(this); + if (ret) { + dev_err(this->dev, "Error setting BCH geometry : %d\n", ret); + return ret; + } + + /* Alloc the new DMA buffers according to the pagesize and oobsize */ + return gpmi_alloc_dma_buffer(this); +} + +static void gpmi_nand_exit(struct gpmi_nand_data *this) +{ + nand_release(&this->mtd); + gpmi_free_dma_buffer(this); +} + +static int gpmi_init_last(struct gpmi_nand_data *this) +{ + struct mtd_info *mtd = &this->mtd; + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct bch_geometry *bch_geo = &this->bch_geometry; + int ret; + + /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ + this->swap_block_mark = !GPMI_IS_MX23(this); + + /* Set up the medium geometry */ + ret = gpmi_set_geometry(this); + if (ret) + return ret; + + /* Init the nand_ecc_ctrl{} */ + ecc->read_page = gpmi_ecc_read_page; + ecc->write_page = gpmi_ecc_write_page; + ecc->read_oob = gpmi_ecc_read_oob; + ecc->write_oob = gpmi_ecc_write_oob; + ecc->mode = NAND_ECC_HW; + ecc->size = bch_geo->ecc_chunk_size; + ecc->strength = bch_geo->ecc_strength; + ecc->layout = &gpmi_hw_ecclayout; + + /* + * We only enable the subpage read when: + * (1) the chip is imx6, and + * (2) the size of the ECC parity is byte aligned. + */ + if (GPMI_IS_MX6(this) && + ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { + ecc->read_subpage = gpmi_ecc_read_subpage; + 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; +} + +static int gpmi_nand_init(struct gpmi_nand_data *this) +{ + struct mtd_info *mtd = &this->mtd; + struct nand_chip *chip = &this->nand; + struct mtd_part_parser_data ppdata = {}; + int ret; + + /* init current chip */ + this->current_chip = -1; + + /* init the MTD data structures */ + mtd->priv = chip; + mtd->name = "gpmi-nand"; + mtd->owner = THIS_MODULE; + + /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ + chip->priv = this; + chip->select_chip = gpmi_select_chip; + chip->cmd_ctrl = gpmi_cmd_ctrl; + chip->dev_ready = gpmi_dev_ready; + chip->read_byte = gpmi_read_byte; + chip->read_buf = gpmi_read_buf; + chip->write_buf = gpmi_write_buf; + chip->badblock_pattern = &gpmi_bbt_descr; + chip->block_markbad = gpmi_block_markbad; + chip->options |= NAND_NO_SUBPAGE_WRITE; + if (of_get_nand_on_flash_bbt(this->dev->of_node)) + chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + + /* + * Allocate a temporary DMA buffer for reading ID in the + * nand_scan_ident(). + */ + this->bch_geometry.payload_size = 1024; + this->bch_geometry.auxiliary_size = 128; + ret = gpmi_alloc_dma_buffer(this); + if (ret) + goto err_out; + + ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL); + if (ret) + goto err_out; + + ret = gpmi_init_last(this); + if (ret) + goto err_out; + + chip->options |= NAND_SKIP_BBTSCAN; + ret = nand_scan_tail(mtd); + if (ret) + goto err_out; + + ret = nand_boot_init(this); + if (ret) + goto err_out; + chip->scan_bbt(mtd); + + ppdata.of_node = this->pdev->dev.of_node; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + if (ret) + goto err_out; + return 0; + +err_out: + gpmi_nand_exit(this); + return ret; +} + +static const struct of_device_id gpmi_nand_id_table[] = { + { + .compatible = "fsl,imx23-gpmi-nand", + .data = (void *)&gpmi_devdata_imx23, + }, { + .compatible = "fsl,imx28-gpmi-nand", + .data = (void *)&gpmi_devdata_imx28, + }, { + .compatible = "fsl,imx6q-gpmi-nand", + .data = (void *)&gpmi_devdata_imx6q, + }, { + .compatible = "fsl,imx6sx-gpmi-nand", + .data = (void *)&gpmi_devdata_imx6sx, + }, {} +}; +MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); + +static int gpmi_nand_probe(struct platform_device *pdev) +{ + struct gpmi_nand_data *this; + const struct of_device_id *of_id; + int ret; + + this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); + if (!this) + return -ENOMEM; + + of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); + if (of_id) { + this->devdata = of_id->data; + } else { + dev_err(&pdev->dev, "Failed to find the right device id.\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, this); + this->pdev = pdev; + this->dev = &pdev->dev; + + ret = acquire_resources(this); + if (ret) + goto exit_acquire_resources; + + ret = init_hardware(this); + if (ret) + goto exit_nfc_init; + + ret = gpmi_nand_init(this); + if (ret) + goto exit_nfc_init; + + dev_info(this->dev, "driver registered.\n"); + + return 0; + +exit_nfc_init: + release_resources(this); +exit_acquire_resources: + dev_err(this->dev, "driver registration failed: %d\n", ret); + + return ret; +} + +static int gpmi_nand_remove(struct platform_device *pdev) +{ + struct gpmi_nand_data *this = platform_get_drvdata(pdev); + + gpmi_nand_exit(this); + release_resources(this); + return 0; +} + +static struct platform_driver gpmi_nand_driver = { + .driver = { + .name = "gpmi-nand", + .of_match_table = gpmi_nand_id_table, + }, + .probe = gpmi_nand_probe, + .remove = gpmi_nand_remove, +}; +module_platform_driver(gpmi_nand_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h new file mode 100644 index 00000000000..32c6ba49f98 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -0,0 +1,305 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * 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. + */ +#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H +#define __DRIVERS_MTD_NAND_GPMI_NAND_H + +#include <linux/mtd/nand.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> + +#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */ +struct resources { + void __iomem *gpmi_regs; + void __iomem *bch_regs; + unsigned int dma_low_channel; + unsigned int dma_high_channel; + struct clk *clock[GPMI_CLK_MAX]; +}; + +/** + * struct bch_geometry - BCH geometry description. + * @gf_len: The length of Galois Field. (e.g., 13 or 14) + * @ecc_strength: A number that describes the strength of the ECC + * algorithm. + * @page_size: The size, in bytes, of a physical page, including + * both data and OOB. + * @metadata_size: The size, in bytes, of the metadata. + * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note + * the first chunk in the page includes both data and + * metadata, so it's a bit larger than this value. + * @ecc_chunk_count: The number of ECC chunks in the page, + * @payload_size: The size, in bytes, of the payload buffer. + * @auxiliary_size: The size, in bytes, of the auxiliary buffer. + * @auxiliary_status_offset: The offset into the auxiliary buffer at which + * the ECC status appears. + * @block_mark_byte_offset: The byte offset in the ECC-based page view at + * which the underlying physical block mark appears. + * @block_mark_bit_offset: The bit offset into the ECC-based page view at + * which the underlying physical block mark appears. + */ +struct bch_geometry { + unsigned int gf_len; + unsigned int ecc_strength; + unsigned int page_size; + unsigned int metadata_size; + unsigned int ecc_chunk_size; + unsigned int ecc_chunk_count; + unsigned int payload_size; + unsigned int auxiliary_size; + unsigned int auxiliary_status_offset; + unsigned int block_mark_byte_offset; + unsigned int block_mark_bit_offset; +}; + +/** + * struct boot_rom_geometry - Boot ROM geometry description. + * @stride_size_in_pages: The size of a boot block stride, in pages. + * @search_area_stride_exponent: The logarithm to base 2 of the size of a + * search area in boot block strides. + */ +struct boot_rom_geometry { + unsigned int stride_size_in_pages; + unsigned int search_area_stride_exponent; +}; + +/* DMA operations types */ +enum dma_ops_type { + DMA_FOR_COMMAND = 1, + DMA_FOR_READ_DATA, + DMA_FOR_WRITE_DATA, + DMA_FOR_READ_ECC_PAGE, + 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, + IS_MX6Q, + IS_MX6SX +}; + +struct gpmi_devdata { + enum gpmi_type type; + int bch_max_ecc_strength; + int max_chain_delay; /* See the async EDO mode */ +}; + +struct gpmi_nand_data { + /* flags */ +#define GPMI_ASYNC_EDO_ENABLED (1 << 0) +#define GPMI_TIMING_INIT_OK (1 << 1) + int flags; + const struct gpmi_devdata *devdata; + + /* System Interface */ + struct device *dev; + struct platform_device *pdev; + + /* Resources */ + struct resources resources; + + /* Flash Hardware */ + struct nand_timing timing; + int timing_mode; + + /* BCH */ + struct bch_geometry bch_geometry; + struct completion bch_done; + + /* NAND Boot issue */ + bool swap_block_mark; + struct boot_rom_geometry rom_geometry; + + /* MTD / NAND */ + struct nand_chip nand; + struct mtd_info mtd; + + /* General-use Variables */ + int current_chip; + unsigned int command_length; + + /* passed from upper layer */ + uint8_t *upper_buf; + int upper_len; + + /* for DMA operations */ + bool direct_dma_map_ok; + + struct scatterlist cmd_sgl; + char *cmd_buffer; + + struct scatterlist data_sgl; + char *data_buffer_dma; + + void *page_buffer_virt; + dma_addr_t page_buffer_phys; + unsigned int page_buffer_size; + + void *payload_virt; + dma_addr_t payload_phys; + + void *auxiliary_virt; + dma_addr_t auxiliary_phys; + + /* DMA channels */ +#define DMA_CHANS 8 + struct dma_chan *dma_chans[DMA_CHANS]; + enum dma_ops_type last_dma_type; + enum dma_ops_type dma_type; + struct completion dma_done; + + /* private */ + 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_threshod - 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_threshod { + 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 */ +extern int common_nfc_set_geometry(struct gpmi_nand_data *); +extern struct dma_chan *get_dma_chan(struct gpmi_nand_data *); +extern void prepare_data_dma(struct gpmi_nand_data *, + enum dma_data_direction dr); +extern int start_dma_without_bch_irq(struct gpmi_nand_data *, + struct dma_async_tx_descriptor *); +extern int start_dma_with_bch_irq(struct gpmi_nand_data *, + struct dma_async_tx_descriptor *); + +/* GPMI-NAND helper function library */ +extern int gpmi_init(struct gpmi_nand_data *); +extern int gpmi_extra_init(struct gpmi_nand_data *); +extern void gpmi_clear_bch(struct gpmi_nand_data *); +extern void gpmi_dump_info(struct gpmi_nand_data *); +extern int bch_set_geometry(struct gpmi_nand_data *); +extern int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip); +extern int gpmi_send_command(struct gpmi_nand_data *); +extern void gpmi_begin(struct gpmi_nand_data *); +extern void gpmi_end(struct gpmi_nand_data *); +extern int gpmi_read_data(struct gpmi_nand_data *); +extern int gpmi_send_data(struct gpmi_nand_data *); +extern int gpmi_send_page(struct gpmi_nand_data *, + dma_addr_t payload, dma_addr_t auxiliary); +extern int gpmi_read_page(struct gpmi_nand_data *, + dma_addr_t payload, dma_addr_t auxiliary); + +/* BCH : Status Block Completion Codes */ +#define STATUS_GOOD 0x00 +#define STATUS_ERASED 0xff +#define STATUS_UNCORRECTABLE 0xfe + +/* Use the devdata to distinguish different Archs. */ +#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23) +#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28) +#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q) +#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX) + +#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x)) +#endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h new file mode 100644 index 00000000000..82114cdc833 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h @@ -0,0 +1,187 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NAND_GPMI_REGS_H +#define __GPMI_NAND_GPMI_REGS_H + +#define HW_GPMI_CTRL0 0x00000000 +#define HW_GPMI_CTRL0_SET 0x00000004 +#define HW_GPMI_CTRL0_CLR 0x00000008 +#define HW_GPMI_CTRL0_TOG 0x0000000c + +#define BP_GPMI_CTRL0_COMMAND_MODE 24 +#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE) +#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ + (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE) +#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 +#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 + +#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23) +#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 +#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 + +/* + * Difference in LOCK_CS between imx23 and imx28 : + * This bit may impact the _POWER_ consumption. So some chips + * do not set it. + */ +#define MX23_BP_GPMI_CTRL0_LOCK_CS 22 +#define MX28_BP_GPMI_CTRL0_LOCK_CS 27 +#define LOCK_CS_ENABLE 0x1 +#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0 + +/* Difference in CS between imx23 and imx28 */ +#define BP_GPMI_CTRL0_CS 20 +#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS) +#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS) +#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \ + (GPMI_IS_MX23((x)) \ + ? MX23_BM_GPMI_CTRL0_CS \ + : MX28_BM_GPMI_CTRL0_CS)) + +#define BP_GPMI_CTRL0_ADDRESS 17 +#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS) +#define BF_GPMI_CTRL0_ADDRESS(v) \ + (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS) +#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 +#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 +#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 + +#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 + +#define BP_GPMI_CTRL0_XFER_COUNT 0 +#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT) +#define BF_GPMI_CTRL0_XFER_COUNT(v) \ + (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT) + +#define HW_GPMI_COMPARE 0x00000010 + +#define HW_GPMI_ECCCTRL 0x00000020 +#define HW_GPMI_ECCCTRL_SET 0x00000024 +#define HW_GPMI_ECCCTRL_CLR 0x00000028 +#define HW_GPMI_ECCCTRL_TOG 0x0000002c + +#define BP_GPMI_ECCCTRL_ECC_CMD 13 +#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD) +#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ + (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD) +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 + +#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12) +#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 + +#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 +#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK) +#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ + (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK) +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF + +#define HW_GPMI_ECCCOUNT 0x00000030 +#define HW_GPMI_PAYLOAD 0x00000040 +#define HW_GPMI_AUXILIARY 0x00000050 +#define HW_GPMI_CTRL1 0x00000060 +#define HW_GPMI_CTRL1_SET 0x00000064 +#define HW_GPMI_CTRL1_CLR 0x00000068 +#define HW_GPMI_CTRL1_TOG 0x0000006c + +#define BP_GPMI_CTRL1_DECOUPLE_CS 24 +#define BM_GPMI_CTRL1_DECOUPLE_CS (1 << BP_GPMI_CTRL1_DECOUPLE_CS) + +#define BP_GPMI_CTRL1_WRN_DLY_SEL 22 +#define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL) +#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \ + (((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL) +#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0 +#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1 +#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2 +#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3 + +#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) + +#define BP_GPMI_CTRL1_DLL_ENABLE 17 +#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) + +#define BP_GPMI_CTRL1_HALF_PERIOD 16 +#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) + +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ + (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) + +#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 + +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 + +#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 + +#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) + +#define HW_GPMI_TIMING0 0x00000070 + +#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 +#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP) +#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP) + +#define BP_GPMI_TIMING0_DATA_HOLD 8 +#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD) +#define BF_GPMI_TIMING0_DATA_HOLD(v) \ + (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD) + +#define BP_GPMI_TIMING0_DATA_SETUP 0 +#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP) +#define BF_GPMI_TIMING0_DATA_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP) + +#define HW_GPMI_TIMING1 0x00000080 +#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16 +#define BM_GPMI_TIMING1_BUSY_TIMEOUT (0xffff << BP_GPMI_TIMING1_BUSY_TIMEOUT) +#define BF_GPMI_TIMING1_BUSY_TIMEOUT(v) \ + (((v) << BP_GPMI_TIMING1_BUSY_TIMEOUT) & BM_GPMI_TIMING1_BUSY_TIMEOUT) + +#define HW_GPMI_TIMING2 0x00000090 +#define HW_GPMI_DATA 0x000000a0 + +/* MX28 uses this to detect READY. */ +#define HW_GPMI_STAT 0x000000b0 +#define MX28_BP_GPMI_STAT_READY_BUSY 24 +#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY) +#define MX28_BF_GPMI_STAT_READY_BUSY(v) \ + (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) + +/* MX23 uses this to detect READY. */ +#define HW_GPMI_DEBUG 0x000000c0 +#define MX23_BP_GPMI_DEBUG_READY0 28 +#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) +#endif diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c deleted file mode 100644 index f8ce79b446e..00000000000 --- a/drivers/mtd/nand/h1910.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * drivers/mtd/nand/h1910.c - * - * Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com) - * - * Derived from drivers/mtd/nand/edb7312.c - * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is - * a 128Mibit (16MiB x 8 bits) NAND flash device. - */ - -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <mach/hardware.h> /* for CLPS7111_VIRT_BASE */ -#include <asm/sizes.h> -#include <mach/h1900-gpio.h> -#include <mach/ipaq.h> - -/* - * MTD structure for EDB7312 board - */ -static struct mtd_info *h1910_nand_mtd = NULL; - -/* - * Module stuff - */ - -#ifdef CONFIG_MTD_PARTITIONS -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info[] = { - {name:"h1910 NAND Flash", - offset:0, - size:16 * 1024 * 1024} -}; - -#define NUM_PARTITIONS 1 - -#endif - -/* - * hardware specific access to control-lines - * - * NAND_NCE: bit 0 - don't care - * NAND_CLE: bit 1 - address bit 2 - * NAND_ALE: bit 2 - address bit 3 - */ -static void h1910_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W | ((ctrl & 0x6) << 1)); -} - -/* - * read device ready pin - */ -#if 0 -static int h1910_device_ready(struct mtd_info *mtd) -{ - return (GPLR(55) & GPIO_bit(55)); -} -#endif - -/* - * Main initialization routine - */ -static int __init h1910_init(void) -{ - struct nand_chip *this; - const char *part_type = 0; - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - void __iomem *nandaddr; - - if (!machine_is_h1900()) - return -ENODEV; - - nandaddr = ioremap(0x08000000, 0x1000); - if (!nandaddr) { - printk("Failed to ioremap nand flash.\n"); - return -ENOMEM; - } - - /* Allocate memory for MTD device structure and private data */ - h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!h1910_nand_mtd) { - printk("Unable to allocate h1910 NAND MTD device structure.\n"); - iounmap((void *)nandaddr); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&h1910_nand_mtd[1]); - - /* Initialize structures */ - memset(h1910_nand_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - h1910_nand_mtd->priv = this; - h1910_nand_mtd->owner = THIS_MODULE; - - /* - * Enable VPEN - */ - GPSR(37) = GPIO_bit(37); - - /* insert callbacks */ - this->IO_ADDR_R = nandaddr; - this->IO_ADDR_W = nandaddr; - this->cmd_ctrl = h1910_hwcontrol; - this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */ - /* 15 us command delay time */ - this->chip_delay = 50; - this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; - - /* Scan to find existence of the device */ - if (nand_scan(h1910_nand_mtd, 1)) { - printk(KERN_NOTICE "No NAND device - returning -ENXIO\n"); - kfree(h1910_nand_mtd); - iounmap((void *)nandaddr); - return -ENXIO; - } -#ifdef CONFIG_MTD_CMDLINE_PARTS - mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, "h1910-nand"); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (mtd_parts_nb == 0) { - mtd_parts = partition_info; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - - /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb); - - /* Return happy */ - return 0; -} - -module_init(h1910_init); - -/* - * Clean up routine - */ -static void __exit h1910_cleanup(void) -{ - struct nand_chip *this = (struct nand_chip *)&h1910_nand_mtd[1]; - - /* Release resources, unregister device */ - nand_release(h1910_nand_mtd); - - /* Release io resource */ - iounmap((void *)this->IO_ADDR_W); - - /* Free the MTD device structure */ - kfree(h1910_nand_mtd); -} - -module_exit(h1910_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Joshua Wise <joshua at joshuawise dot com>"); -MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910"); diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index cea38a5d4ac..a2c804de156 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -52,9 +52,10 @@ #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) +#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa -#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 #define JZ_NAND_MEM_CMD_OFFSET 0x08000 +#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 struct jz_nand { struct mtd_info mtd; @@ -62,8 +63,11 @@ struct jz_nand { void __iomem *base; struct resource *mem; - void __iomem *bank_base; - struct resource *bank_mem; + unsigned char banks[JZ_NAND_NUM_BANKS]; + void __iomem *bank_base[JZ_NAND_NUM_BANKS]; + struct resource *bank_mem[JZ_NAND_NUM_BANKS]; + + int selected_bank; struct jz_nand_platform_data *pdata; bool is_reading; @@ -74,26 +78,50 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) return container_of(mtd, struct jz_nand, mtd); } +static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd->priv; + uint32_t ctrl; + int banknr; + + ctrl = readl(nand->base + JZ_REG_NAND_CTRL); + ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; + + if (chipnr == -1) { + banknr = -1; + } else { + banknr = nand->banks[chipnr] - 1; + chip->IO_ADDR_R = nand->bank_base[banknr]; + chip->IO_ADDR_W = nand->bank_base[banknr]; + } + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + + nand->selected_bank = banknr; +} + static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) { struct jz_nand *nand = mtd_to_jz_nand(mtd); struct nand_chip *chip = mtd->priv; uint32_t reg; + void __iomem *bank_base = nand->bank_base[nand->selected_bank]; + + BUG_ON(nand->selected_bank < 0); if (ctrl & NAND_CTRL_CHANGE) { BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); if (ctrl & NAND_ALE) - chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; + bank_base += JZ_NAND_MEM_ADDR_OFFSET; else if (ctrl & NAND_CLE) - chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; - else - chip->IO_ADDR_W = nand->bank_base; + bank_base += JZ_NAND_MEM_CMD_OFFSET; + chip->IO_ADDR_W = bank_base; reg = readl(nand->base + JZ_REG_NAND_CTRL); if (ctrl & NAND_NCE) - reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); + reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); else - reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); + reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); writel(reg, nand->base + JZ_REG_NAND_CTRL); } if (dat != NAND_CMD_NONE) @@ -251,12 +279,8 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, return 0; } -#ifdef CONFIG_MTD_CMDLINE_PARTS -static const char *part_probes[] = {"cmdline", NULL}; -#endif - static int jz_nand_ioremap_resource(struct platform_device *pdev, - const char *name, struct resource **res, void __iomem **base) + const char *name, struct resource **res, void *__iomem *base) { int ret; @@ -292,31 +316,112 @@ err: return ret; } -static int __devinit jz_nand_probe(struct platform_device *pdev) +static inline void jz_nand_iounmap_resource(struct resource *res, + void __iomem *base) +{ + iounmap(base); + release_mem_region(res->start, resource_size(res)); +} + +static int jz_nand_detect_bank(struct platform_device *pdev, + struct jz_nand *nand, unsigned char bank, + size_t chipnr, uint8_t *nand_maf_id, + uint8_t *nand_dev_id) +{ + int ret; + int gpio; + char gpio_name[9]; + char res_name[6]; + uint32_t ctrl; + struct mtd_info *mtd = &nand->mtd; + struct nand_chip *chip = &nand->chip; + + /* Request GPIO port. */ + gpio = JZ_GPIO_MEM_CS0 + bank - 1; + sprintf(gpio_name, "NAND CS%d", bank); + ret = gpio_request(gpio, gpio_name); + if (ret) { + dev_warn(&pdev->dev, + "Failed to request %s gpio %d: %d\n", + gpio_name, gpio, ret); + goto notfound_gpio; + } + + /* Request I/O resource. */ + sprintf(res_name, "bank%d", bank); + ret = jz_nand_ioremap_resource(pdev, res_name, + &nand->bank_mem[bank - 1], + &nand->bank_base[bank - 1]); + if (ret) + goto notfound_resource; + + /* Enable chip in bank. */ + jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); + ctrl = readl(nand->base + JZ_REG_NAND_CTRL); + ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + + if (chipnr == 0) { + /* Detect first chip. */ + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + goto notfound_id; + + /* Retrieve the IDs from the first chip. */ + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + *nand_maf_id = chip->read_byte(mtd); + *nand_dev_id = chip->read_byte(mtd); + } else { + /* Detect additional chip. */ + chip->select_chip(mtd, chipnr); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + if (*nand_maf_id != chip->read_byte(mtd) + || *nand_dev_id != chip->read_byte(mtd)) { + ret = -ENODEV; + goto notfound_id; + } + + /* Update size of the MTD. */ + chip->numchips++; + mtd->size += chip->chipsize; + } + + dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); + return 0; + +notfound_id: + dev_info(&pdev->dev, "No chip found on bank %i\n", bank); + ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); +notfound_resource: + gpio_free(gpio); +notfound_gpio: + return ret; +} + +static int jz_nand_probe(struct platform_device *pdev) { int ret; struct jz_nand *nand; struct nand_chip *chip; struct mtd_info *mtd; - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *partition_info; - int num_partitions = 0; -#endif + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + size_t chipnr, bank_idx; + uint8_t nand_maf_id = 0, nand_dev_id = 0; nand = kzalloc(sizeof(*nand), GFP_KERNEL); - if (!nand) { - dev_err(&pdev->dev, "Failed to allocate device structure.\n"); + if (!nand) return -ENOMEM; - } ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); if (ret) goto err_free; - ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, - &nand->bank_base); - if (ret) - goto err_iounmap_mmio; if (pdata && gpio_is_valid(pdata->busy_gpio)) { ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); @@ -324,7 +429,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to request busy gpio %d: %d\n", pdata->busy_gpio, ret); - goto err_iounmap_mem; + goto err_iounmap_mmio; } } @@ -340,28 +445,58 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; chip->ecc.size = 512; chip->ecc.bytes = 9; + chip->ecc.strength = 4; if (pdata) chip->ecc.layout = pdata->ecc_layout; chip->chip_delay = 50; chip->cmd_ctrl = jz_nand_cmd_ctrl; + chip->select_chip = jz_nand_select_chip; if (pdata && gpio_is_valid(pdata->busy_gpio)) chip->dev_ready = jz_nand_dev_ready; - chip->IO_ADDR_R = nand->bank_base; - chip->IO_ADDR_W = nand->bank_base; - nand->pdata = pdata; platform_set_drvdata(pdev, nand); - writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) { - dev_err(&pdev->dev, "Failed to scan nand\n"); - goto err_gpio_free; + /* We are going to autodetect NAND chips in the banks specified in the + * platform data. Although nand_scan_ident() can detect multiple chips, + * it requires those chips to be numbered consecuitively, which is not + * always the case for external memory banks. And a fixed chip-to-bank + * mapping is not practical either, since for example Dingoo units + * produced at different times have NAND chips in different banks. + */ + chipnr = 0; + for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { + unsigned char bank; + + /* If there is no platform data, look for NAND in bank 1, + * which is the most likely bank since it is the only one + * that can be booted from. + */ + bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; + if (bank == 0) + break; + if (bank > JZ_NAND_NUM_BANKS) { + dev_warn(&pdev->dev, + "Skipping non-existing bank: %d\n", bank); + continue; + } + /* The detection routine will directly or indirectly call + * jz_nand_select_chip(), so nand->banks has to contain the + * bank we're checking. + */ + nand->banks[chipnr] = bank; + if (jz_nand_detect_bank(pdev, nand, bank, chipnr, + &nand_maf_id, &nand_dev_id) == 0) + chipnr++; + else + nand->banks[chipnr] = 0; + } + if (chipnr == 0) { + dev_err(&pdev->dev, "No NAND chips found\n"); + goto err_gpio_busy; } if (pdata && pdata->ident_callback) { @@ -371,25 +506,13 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) ret = nand_scan_tail(mtd); if (ret) { - dev_err(&pdev->dev, "Failed to scan nand\n"); - goto err_gpio_free; - } - -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS - num_partitions = parse_mtd_partitions(mtd, part_probes, - &partition_info, 0); -#endif - if (num_partitions <= 0 && pdata) { - num_partitions = pdata->num_partitions; - partition_info = pdata->partitions; + dev_err(&pdev->dev, "Failed to scan NAND\n"); + goto err_unclaim_banks; } - if (num_partitions > 0) - ret = add_mtd_partitions(mtd, partition_info, num_partitions); - else -#endif - ret = add_mtd_device(mtd); + ret = mtd_device_parse_register(mtd, NULL, NULL, + pdata ? pdata->partitions : NULL, + pdata ? pdata->num_partitions : 0); if (ret) { dev_err(&pdev->dev, "Failed to add mtd device\n"); @@ -401,34 +524,49 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) return 0; err_nand_release: - nand_release(&nand->mtd); -err_gpio_free: - platform_set_drvdata(pdev, NULL); - gpio_free(pdata->busy_gpio); -err_iounmap_mem: - iounmap(nand->bank_base); + nand_release(mtd); +err_unclaim_banks: + while (chipnr--) { + unsigned char bank = nand->banks[chipnr]; + gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); + } + writel(0, nand->base + JZ_REG_NAND_CTRL); +err_gpio_busy: + if (pdata && gpio_is_valid(pdata->busy_gpio)) + gpio_free(pdata->busy_gpio); err_iounmap_mmio: - iounmap(nand->base); + jz_nand_iounmap_resource(nand->mem, nand->base); err_free: kfree(nand); return ret; } -static int __devexit jz_nand_remove(struct platform_device *pdev) +static int jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + size_t i; nand_release(&nand->mtd); /* Deassert and disable all chips */ writel(0, nand->base + JZ_REG_NAND_CTRL); - iounmap(nand->bank_base); - release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); - iounmap(nand->base); - release_mem_region(nand->mem->start, resource_size(nand->mem)); + for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { + unsigned char bank = nand->banks[i]; + if (bank != 0) { + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); + gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); + } + } + if (pdata && gpio_is_valid(pdata->busy_gpio)) + gpio_free(pdata->busy_gpio); + + jz_nand_iounmap_resource(nand->mem, nand->base); - platform_set_drvdata(pdev, NULL); kfree(nand); return 0; @@ -436,24 +574,14 @@ static int __devexit jz_nand_remove(struct platform_device *pdev) static struct platform_driver jz_nand_driver = { .probe = jz_nand_probe, - .remove = __devexit_p(jz_nand_remove), + .remove = jz_nand_remove, .driver = { .name = "jz4740-nand", .owner = THIS_MODULE, }, }; -static int __init jz_nand_init(void) -{ - return platform_driver_register(&jz_nand_driver); -} -module_init(jz_nand_init); - -static void __exit jz_nand_exit(void) -{ - platform_driver_unregister(&jz_nand_driver); -} -module_exit(jz_nand_exit); +module_platform_driver(jz_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c new file mode 100644 index 00000000000..687478c9f09 --- /dev/null +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -0,0 +1,896 @@ +/* + * Driver for NAND MLC Controller in LPC32xx + * + * Author: Roland Stigge <stigge@antcom.de> + * + * Copyright © 2011 WORK Microwave GmbH + * Copyright © 2011, 2012 Roland Stigge + * + * 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. + * + * + * NAND Flash Controller Operation: + * - Read: Auto Decode + * - Write: Auto Encode + * - Tested Page Sizes: 2048, 4096 + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_mtd.h> +#include <linux/of_gpio.h> +#include <linux/mtd/lpc32xx_mlc.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/mtd/nand_ecc.h> + +#define DRV_NAME "lpc32xx_mlc" + +/********************************************************************** +* MLC NAND controller register offsets +**********************************************************************/ + +#define MLC_BUFF(x) (x + 0x00000) +#define MLC_DATA(x) (x + 0x08000) +#define MLC_CMD(x) (x + 0x10000) +#define MLC_ADDR(x) (x + 0x10004) +#define MLC_ECC_ENC_REG(x) (x + 0x10008) +#define MLC_ECC_DEC_REG(x) (x + 0x1000C) +#define MLC_ECC_AUTO_ENC_REG(x) (x + 0x10010) +#define MLC_ECC_AUTO_DEC_REG(x) (x + 0x10014) +#define MLC_RPR(x) (x + 0x10018) +#define MLC_WPR(x) (x + 0x1001C) +#define MLC_RUBP(x) (x + 0x10020) +#define MLC_ROBP(x) (x + 0x10024) +#define MLC_SW_WP_ADD_LOW(x) (x + 0x10028) +#define MLC_SW_WP_ADD_HIG(x) (x + 0x1002C) +#define MLC_ICR(x) (x + 0x10030) +#define MLC_TIME_REG(x) (x + 0x10034) +#define MLC_IRQ_MR(x) (x + 0x10038) +#define MLC_IRQ_SR(x) (x + 0x1003C) +#define MLC_LOCK_PR(x) (x + 0x10044) +#define MLC_ISR(x) (x + 0x10048) +#define MLC_CEH(x) (x + 0x1004C) + +/********************************************************************** +* MLC_CMD bit definitions +**********************************************************************/ +#define MLCCMD_RESET 0xFF + +/********************************************************************** +* MLC_ICR bit definitions +**********************************************************************/ +#define MLCICR_WPROT (1 << 3) +#define MLCICR_LARGEBLOCK (1 << 2) +#define MLCICR_LONGADDR (1 << 1) +#define MLCICR_16BIT (1 << 0) /* unsupported by LPC32x0! */ + +/********************************************************************** +* MLC_TIME_REG bit definitions +**********************************************************************/ +#define MLCTIMEREG_TCEA_DELAY(n) (((n) & 0x03) << 24) +#define MLCTIMEREG_BUSY_DELAY(n) (((n) & 0x1F) << 19) +#define MLCTIMEREG_NAND_TA(n) (((n) & 0x07) << 16) +#define MLCTIMEREG_RD_HIGH(n) (((n) & 0x0F) << 12) +#define MLCTIMEREG_RD_LOW(n) (((n) & 0x0F) << 8) +#define MLCTIMEREG_WR_HIGH(n) (((n) & 0x0F) << 4) +#define MLCTIMEREG_WR_LOW(n) (((n) & 0x0F) << 0) + +/********************************************************************** +* MLC_IRQ_MR and MLC_IRQ_SR bit definitions +**********************************************************************/ +#define MLCIRQ_NAND_READY (1 << 5) +#define MLCIRQ_CONTROLLER_READY (1 << 4) +#define MLCIRQ_DECODE_FAILURE (1 << 3) +#define MLCIRQ_DECODE_ERROR (1 << 2) +#define MLCIRQ_ECC_READY (1 << 1) +#define MLCIRQ_WRPROT_FAULT (1 << 0) + +/********************************************************************** +* MLC_LOCK_PR bit definitions +**********************************************************************/ +#define MLCLOCKPR_MAGIC 0xA25E + +/********************************************************************** +* MLC_ISR bit definitions +**********************************************************************/ +#define MLCISR_DECODER_FAILURE (1 << 6) +#define MLCISR_ERRORS ((1 << 4) | (1 << 5)) +#define MLCISR_ERRORS_DETECTED (1 << 3) +#define MLCISR_ECC_READY (1 << 2) +#define MLCISR_CONTROLLER_READY (1 << 1) +#define MLCISR_NAND_READY (1 << 0) + +/********************************************************************** +* MLC_CEH bit definitions +**********************************************************************/ +#define MLCCEH_NORMAL (1 << 0) + +struct lpc32xx_nand_cfg_mlc { + uint32_t tcea_delay; + uint32_t busy_delay; + uint32_t nand_ta; + uint32_t rd_high; + uint32_t rd_low; + uint32_t wr_high; + uint32_t wr_low; + int wp_gpio; + struct mtd_partition *parts; + unsigned num_parts; +}; + +static struct nand_ecclayout lpc32xx_nand_oob = { + .eccbytes = 40, + .eccpos = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, + .oobfree = { + { .offset = 0, + .length = 6, }, + { .offset = 16, + .length = 6, }, + { .offset = 32, + .length = 6, }, + { .offset = 48, + .length = 6, }, + }, +}; + +static struct nand_bbt_descr lpc32xx_nand_bbt = { + .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | + NAND_BBT_WRITE, + .pages = { 524224, 0, 0, 0, 0, 0, 0, 0 }, +}; + +static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = { + .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | + NAND_BBT_WRITE, + .pages = { 524160, 0, 0, 0, 0, 0, 0, 0 }, +}; + +struct lpc32xx_nand_host { + struct nand_chip nand_chip; + struct lpc32xx_mlc_platform_data *pdata; + struct clk *clk; + struct mtd_info mtd; + void __iomem *io_base; + int irq; + struct lpc32xx_nand_cfg_mlc *ncfg; + struct completion comp_nand; + struct completion comp_controller; + uint32_t llptr; + /* + * Physical addresses of ECC buffer, DMA data buffers, OOB data buffer + */ + dma_addr_t oob_buf_phy; + /* + * Virtual addresses of ECC buffer, DMA data buffers, OOB data buffer + */ + uint8_t *oob_buf; + /* Physical address of DMA base address */ + dma_addr_t io_base_phy; + + struct completion comp_dma; + struct dma_chan *dma_chan; + struct dma_slave_config dma_slave_config; + struct scatterlist sgl; + uint8_t *dma_buf; + uint8_t *dummy_buf; + int mlcsubpages; /* number of 512bytes-subpages */ +}; + +/* + * Activate/Deactivate DMA Operation: + * + * Using the PL080 DMA Controller for transferring the 512 byte subpages + * instead of doing readl() / writel() in a loop slows it down significantly. + * Measurements via getnstimeofday() upon 512 byte subpage reads reveal: + * + * - readl() of 128 x 32 bits in a loop: ~20us + * - DMA read of 512 bytes (32 bit, 4...128 words bursts): ~60us + * - DMA read of 512 bytes (32 bit, no bursts): ~100us + * + * This applies to the transfer itself. In the DMA case: only the + * wait_for_completion() (DMA setup _not_ included). + * + * Note that the 512 bytes subpage transfer is done directly from/to a + * FIFO/buffer inside the NAND controller. Most of the time (~400-800us for a + * 2048 bytes page) is spent waiting for the NAND IRQ, anyway. (The NAND + * controller transferring data between its internal buffer to/from the NAND + * chip.) + * + * Therefore, using the PL080 DMA is disabled by default, for now. + * + */ +static int use_dma; + +static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) +{ + uint32_t clkrate, tmp; + + /* Reset MLC controller */ + writel(MLCCMD_RESET, MLC_CMD(host->io_base)); + udelay(1000); + + /* Get base clock for MLC block */ + clkrate = clk_get_rate(host->clk); + if (clkrate == 0) + clkrate = 104000000; + + /* Unlock MLC_ICR + * (among others, will be locked again automatically) */ + writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); + + /* Configure MLC Controller: Large Block, 5 Byte Address */ + tmp = MLCICR_LARGEBLOCK | MLCICR_LONGADDR; + writel(tmp, MLC_ICR(host->io_base)); + + /* Unlock MLC_TIME_REG + * (among others, will be locked again automatically) */ + writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); + + /* Compute clock setup values, see LPC and NAND manual */ + tmp = 0; + tmp |= MLCTIMEREG_TCEA_DELAY(clkrate / host->ncfg->tcea_delay + 1); + tmp |= MLCTIMEREG_BUSY_DELAY(clkrate / host->ncfg->busy_delay + 1); + tmp |= MLCTIMEREG_NAND_TA(clkrate / host->ncfg->nand_ta + 1); + tmp |= MLCTIMEREG_RD_HIGH(clkrate / host->ncfg->rd_high + 1); + tmp |= MLCTIMEREG_RD_LOW(clkrate / host->ncfg->rd_low); + tmp |= MLCTIMEREG_WR_HIGH(clkrate / host->ncfg->wr_high + 1); + tmp |= MLCTIMEREG_WR_LOW(clkrate / host->ncfg->wr_low); + writel(tmp, MLC_TIME_REG(host->io_base)); + + /* Enable IRQ for CONTROLLER_READY and NAND_READY */ + writeb(MLCIRQ_CONTROLLER_READY | MLCIRQ_NAND_READY, + MLC_IRQ_MR(host->io_base)); + + /* Normal nCE operation: nCE controlled by controller */ + writel(MLCCEH_NORMAL, MLC_CEH(host->io_base)); +} + +/* + * Hardware specific access to control lines + */ +static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd->priv; + struct lpc32xx_nand_host *host = nand_chip->priv; + + if (cmd != NAND_CMD_NONE) { + if (ctrl & NAND_CLE) + writel(cmd, MLC_CMD(host->io_base)); + else + writel(cmd, MLC_ADDR(host->io_base)); + } +} + +/* + * Read Device Ready (NAND device _and_ controller ready) + */ +static int lpc32xx_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct lpc32xx_nand_host *host = nand_chip->priv; + + if ((readb(MLC_ISR(host->io_base)) & + (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) == + (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) + return 1; + + return 0; +} + +static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host) +{ + uint8_t sr; + + /* Clear interrupt flag by reading status */ + sr = readb(MLC_IRQ_SR(host->io_base)); + if (sr & MLCIRQ_NAND_READY) + complete(&host->comp_nand); + if (sr & MLCIRQ_CONTROLLER_READY) + complete(&host->comp_controller); + + return IRQ_HANDLED; +} + +static int lpc32xx_waitfunc_nand(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct lpc32xx_nand_host *host = chip->priv; + + if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY) + goto exit; + + wait_for_completion(&host->comp_nand); + + while (!(readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)) { + /* Seems to be delayed sometimes by controller */ + dev_dbg(&mtd->dev, "Warning: NAND not ready.\n"); + cpu_relax(); + } + +exit: + return NAND_STATUS_READY; +} + +static int lpc32xx_waitfunc_controller(struct mtd_info *mtd, + struct nand_chip *chip) +{ + struct lpc32xx_nand_host *host = chip->priv; + + if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY) + goto exit; + + wait_for_completion(&host->comp_controller); + + while (!(readb(MLC_ISR(host->io_base)) & + MLCISR_CONTROLLER_READY)) { + dev_dbg(&mtd->dev, "Warning: Controller not ready.\n"); + cpu_relax(); + } + +exit: + return NAND_STATUS_READY; +} + +static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + lpc32xx_waitfunc_nand(mtd, chip); + lpc32xx_waitfunc_controller(mtd, chip); + + return NAND_STATUS_READY; +} + +/* + * Enable NAND write protect + */ +static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 0); +} + +/* + * Disable NAND write protect + */ +static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 1); +} + +static void lpc32xx_dma_complete_func(void *completion) +{ + complete(completion); +} + +static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, + enum dma_transfer_direction dir) +{ + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + struct dma_async_tx_descriptor *desc; + int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int res; + + sg_init_one(&host->sgl, mem, len); + + res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + if (res != 1) { + dev_err(mtd->dev.parent, "Failed to map sg list\n"); + return -ENXIO; + } + desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, + flags); + if (!desc) { + dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); + goto out1; + } + + init_completion(&host->comp_dma); + desc->callback = lpc32xx_dma_complete_func; + desc->callback_param = &host->comp_dma; + + dmaengine_submit(desc); + dma_async_issue_pending(host->dma_chan); + + wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000)); + + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + return 0; +out1: + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + return -ENXIO; +} + +static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct lpc32xx_nand_host *host = chip->priv; + int i, j; + uint8_t *oobbuf = chip->oob_poi; + uint32_t mlc_isr; + int res; + uint8_t *dma_buf; + bool dma_mapped; + + if ((void *)buf <= high_memory) { + dma_buf = buf; + dma_mapped = true; + } else { + dma_buf = host->dma_buf; + dma_mapped = false; + } + + /* Writing Command and Address */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + /* For all sub-pages */ + for (i = 0; i < host->mlcsubpages; i++) { + /* Start Auto Decode Command */ + writeb(0x00, MLC_ECC_AUTO_DEC_REG(host->io_base)); + + /* Wait for Controller Ready */ + lpc32xx_waitfunc_controller(mtd, chip); + + /* Check ECC Error status */ + mlc_isr = readl(MLC_ISR(host->io_base)); + if (mlc_isr & MLCISR_DECODER_FAILURE) { + mtd->ecc_stats.failed++; + dev_warn(&mtd->dev, "%s: DECODER_FAILURE\n", __func__); + } else if (mlc_isr & MLCISR_ERRORS_DETECTED) { + mtd->ecc_stats.corrected += ((mlc_isr >> 4) & 0x3) + 1; + } + + /* Read 512 + 16 Bytes */ + if (use_dma) { + res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, + DMA_DEV_TO_MEM); + if (res) + return res; + } else { + for (j = 0; j < (512 >> 2); j++) { + *((uint32_t *)(buf)) = + readl(MLC_BUFF(host->io_base)); + buf += 4; + } + } + for (j = 0; j < (16 >> 2); j++) { + *((uint32_t *)(oobbuf)) = + readl(MLC_BUFF(host->io_base)); + oobbuf += 4; + } + } + + if (use_dma && !dma_mapped) + memcpy(buf, dma_buf, mtd->writesize); + + return 0; +} + +static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct lpc32xx_nand_host *host = chip->priv; + const uint8_t *oobbuf = chip->oob_poi; + uint8_t *dma_buf = (uint8_t *)buf; + int res; + int i, j; + + if (use_dma && (void *)buf >= high_memory) { + dma_buf = host->dma_buf; + memcpy(dma_buf, buf, mtd->writesize); + } + + for (i = 0; i < host->mlcsubpages; i++) { + /* Start Encode */ + writeb(0x00, MLC_ECC_ENC_REG(host->io_base)); + + /* Write 512 + 6 Bytes to Buffer */ + if (use_dma) { + res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, + DMA_MEM_TO_DEV); + if (res) + return res; + } else { + for (j = 0; j < (512 >> 2); j++) { + writel(*((uint32_t *)(buf)), + MLC_BUFF(host->io_base)); + buf += 4; + } + } + writel(*((uint32_t *)(oobbuf)), MLC_BUFF(host->io_base)); + oobbuf += 4; + writew(*((uint16_t *)(oobbuf)), MLC_BUFF(host->io_base)); + oobbuf += 12; + + /* Auto Encode w/ Bit 8 = 0 (see LPC MLC Controller manual) */ + writeb(0x00, MLC_ECC_AUTO_ENC_REG(host->io_base)); + + /* Wait for Controller Ready */ + lpc32xx_waitfunc_controller(mtd, chip); + } + return 0; +} + +static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct lpc32xx_nand_host *host = chip->priv; + + /* Read whole page - necessary with MLC controller! */ + lpc32xx_read_page(mtd, chip, host->dummy_buf, 1, page); + + return 0; +} + +static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + /* None, write_oob conflicts with the automatic LPC MLC ECC decoder! */ + return 0; +} + +/* Prepares MLC for transfers with H/W ECC enabled: always enabled anyway */ +static void lpc32xx_ecc_enable(struct mtd_info *mtd, int mode) +{ + /* Always enabled! */ +} + +static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host) +{ + struct mtd_info *mtd = &host->mtd; + dma_cap_mask_t mask; + + if (!host->pdata || !host->pdata->dma_filter) { + dev_err(mtd->dev.parent, "no DMA platform data\n"); + return -ENOENT; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, + "nand-mlc"); + if (!host->dma_chan) { + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return -EBUSY; + } + + /* + * Set direction to a sensible value even if the dmaengine driver + * should ignore it. With the default (DMA_MEM_TO_MEM), the amba-pl08x + * driver criticizes it as "alien transfer direction". + */ + host->dma_slave_config.direction = DMA_DEV_TO_MEM; + host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.src_maxburst = 128; + host->dma_slave_config.dst_maxburst = 128; + /* DMA controller does flow control: */ + host->dma_slave_config.device_fc = false; + host->dma_slave_config.src_addr = MLC_BUFF(host->io_base_phy); + host->dma_slave_config.dst_addr = MLC_BUFF(host->io_base_phy); + if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { + dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); + goto out1; + } + + return 0; +out1: + dma_release_channel(host->dma_chan); + return -ENXIO; +} + +static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev) +{ + struct lpc32xx_nand_cfg_mlc *ncfg; + struct device_node *np = dev->of_node; + + ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); + if (!ncfg) + return NULL; + + of_property_read_u32(np, "nxp,tcea-delay", &ncfg->tcea_delay); + of_property_read_u32(np, "nxp,busy-delay", &ncfg->busy_delay); + of_property_read_u32(np, "nxp,nand-ta", &ncfg->nand_ta); + of_property_read_u32(np, "nxp,rd-high", &ncfg->rd_high); + of_property_read_u32(np, "nxp,rd-low", &ncfg->rd_low); + of_property_read_u32(np, "nxp,wr-high", &ncfg->wr_high); + of_property_read_u32(np, "nxp,wr-low", &ncfg->wr_low); + + if (!ncfg->tcea_delay || !ncfg->busy_delay || !ncfg->nand_ta || + !ncfg->rd_high || !ncfg->rd_low || !ncfg->wr_high || + !ncfg->wr_low) { + dev_err(dev, "chip parameters not specified correctly\n"); + return NULL; + } + + ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); + + return ncfg; +} + +/* + * Probe for NAND controller + */ +static int lpc32xx_nand_probe(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + struct resource *rc; + int res; + struct mtd_part_parser_data ppdata = {}; + + /* Allocate memory for the device structure (and zero it) */ + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->io_base = devm_ioremap_resource(&pdev->dev, rc); + if (IS_ERR(host->io_base)) + return PTR_ERR(host->io_base); + + host->io_base_phy = rc->start; + + mtd = &host->mtd; + nand_chip = &host->nand_chip; + if (pdev->dev.of_node) + host->ncfg = lpc32xx_parse_dt(&pdev->dev); + if (!host->ncfg) { + dev_err(&pdev->dev, + "Missing or bad NAND config from device tree\n"); + return -ENOENT; + } + if (host->ncfg->wp_gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(host->ncfg->wp_gpio) && + gpio_request(host->ncfg->wp_gpio, "NAND WP")) { + dev_err(&pdev->dev, "GPIO not available\n"); + return -EBUSY; + } + lpc32xx_wp_disable(host); + + host->pdata = dev_get_platdata(&pdev->dev); + + nand_chip->priv = host; /* link the private data structures */ + mtd->priv = nand_chip; + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + + /* Get NAND clock */ + host->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "Clock initialization failure\n"); + res = -ENOENT; + goto err_exit1; + } + clk_enable(host->clk); + + nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; + nand_chip->dev_ready = lpc32xx_nand_device_ready; + nand_chip->chip_delay = 25; /* us */ + nand_chip->IO_ADDR_R = MLC_DATA(host->io_base); + nand_chip->IO_ADDR_W = MLC_DATA(host->io_base); + + /* Init NAND controller */ + lpc32xx_nand_setup(host); + + platform_set_drvdata(pdev, host); + + /* Initialize function pointers */ + nand_chip->ecc.hwctl = lpc32xx_ecc_enable; + nand_chip->ecc.read_page_raw = lpc32xx_read_page; + nand_chip->ecc.read_page = lpc32xx_read_page; + nand_chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel; + nand_chip->ecc.write_page = lpc32xx_write_page_lowlevel; + nand_chip->ecc.write_oob = lpc32xx_write_oob; + nand_chip->ecc.read_oob = lpc32xx_read_oob; + nand_chip->ecc.strength = 4; + nand_chip->waitfunc = lpc32xx_waitfunc; + + nand_chip->options = NAND_NO_SUBPAGE_WRITE; + nand_chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + nand_chip->bbt_td = &lpc32xx_nand_bbt; + nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror; + + /* bitflip_threshold's default is defined as ecc_strength anyway. + * Unfortunately, it is set only later at add_mtd_device(). Meanwhile + * being 0, it causes bad block table scanning errors in + * nand_scan_tail(), so preparing it here. */ + mtd->bitflip_threshold = nand_chip->ecc.strength; + + if (use_dma) { + res = lpc32xx_dma_setup(host); + if (res) { + res = -EIO; + goto err_exit2; + } + } + + /* + * Scan to find existance of the device and + * Get the type of NAND device SMALL block or LARGE block + */ + if (nand_scan_ident(mtd, 1, NULL)) { + res = -ENXIO; + goto err_exit3; + } + + host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); + if (!host->dma_buf) { + res = -ENOMEM; + goto err_exit3; + } + + host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); + if (!host->dummy_buf) { + res = -ENOMEM; + goto err_exit3; + } + + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = mtd->writesize; + nand_chip->ecc.layout = &lpc32xx_nand_oob; + host->mlcsubpages = mtd->writesize / 512; + + /* initially clear interrupt status */ + readb(MLC_IRQ_SR(host->io_base)); + + init_completion(&host->comp_nand); + init_completion(&host->comp_controller); + + host->irq = platform_get_irq(pdev, 0); + if ((host->irq < 0) || (host->irq >= NR_IRQS)) { + dev_err(&pdev->dev, "failed to get platform irq\n"); + res = -EINVAL; + goto err_exit3; + } + + if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq, + IRQF_TRIGGER_HIGH, DRV_NAME, host)) { + dev_err(&pdev->dev, "Error requesting NAND IRQ\n"); + res = -ENXIO; + goto err_exit3; + } + + /* + * Fills out all the uninitialized function pointers with the defaults + * And scans for a bad block table if appropriate. + */ + if (nand_scan_tail(mtd)) { + res = -ENXIO; + goto err_exit4; + } + + mtd->name = DRV_NAME; + + ppdata.of_node = pdev->dev.of_node; + res = mtd_device_parse_register(mtd, NULL, &ppdata, host->ncfg->parts, + host->ncfg->num_parts); + if (!res) + return res; + + nand_release(mtd); + +err_exit4: + free_irq(host->irq, host); +err_exit3: + if (use_dma) + dma_release_channel(host->dma_chan); +err_exit2: + clk_disable(host->clk); + clk_put(host->clk); +err_exit1: + lpc32xx_wp_enable(host); + gpio_free(host->ncfg->wp_gpio); + + return res; +} + +/* + * Remove NAND device + */ +static int lpc32xx_nand_remove(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = &host->mtd; + + nand_release(mtd); + free_irq(host->irq, host); + if (use_dma) + dma_release_channel(host->dma_chan); + + clk_disable(host->clk); + clk_put(host->clk); + + lpc32xx_wp_enable(host); + gpio_free(host->ncfg->wp_gpio); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_nand_resume(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + + /* Re-enable NAND clock */ + clk_enable(host->clk); + + /* Fresh init of NAND controller */ + lpc32xx_nand_setup(host); + + /* Disable write protect */ + lpc32xx_wp_disable(host); + + return 0; +} + +static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + + /* Enable write protect for safety */ + lpc32xx_wp_enable(host); + + /* Disable clock */ + clk_disable(host->clk); + return 0; +} + +#else +#define lpc32xx_nand_resume NULL +#define lpc32xx_nand_suspend NULL +#endif + +static const struct of_device_id lpc32xx_nand_match[] = { + { .compatible = "nxp,lpc3220-mlc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); + +static struct platform_driver lpc32xx_nand_driver = { + .probe = lpc32xx_nand_probe, + .remove = lpc32xx_nand_remove, + .resume = lpc32xx_nand_resume, + .suspend = lpc32xx_nand_suspend, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = lpc32xx_nand_match, + }, +}; + +module_platform_driver(lpc32xx_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX MLC controller"); diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c new file mode 100644 index 00000000000..53a6742e3da --- /dev/null +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -0,0 +1,1018 @@ +/* + * NXP LPC32XX NAND SLC driver + * + * Authors: + * Kevin Wells <kevin.wells@nxp.com> + * Roland Stigge <stigge@antcom.de> + * + * Copyright © 2011 NXP Semiconductors + * Copyright © 2012 Roland Stigge + * + * 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. + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_mtd.h> +#include <linux/of_gpio.h> +#include <linux/mtd/lpc32xx_slc.h> + +#define LPC32XX_MODNAME "lpc32xx-nand" + +/********************************************************************** +* SLC NAND controller register offsets +**********************************************************************/ + +#define SLC_DATA(x) (x + 0x000) +#define SLC_ADDR(x) (x + 0x004) +#define SLC_CMD(x) (x + 0x008) +#define SLC_STOP(x) (x + 0x00C) +#define SLC_CTRL(x) (x + 0x010) +#define SLC_CFG(x) (x + 0x014) +#define SLC_STAT(x) (x + 0x018) +#define SLC_INT_STAT(x) (x + 0x01C) +#define SLC_IEN(x) (x + 0x020) +#define SLC_ISR(x) (x + 0x024) +#define SLC_ICR(x) (x + 0x028) +#define SLC_TAC(x) (x + 0x02C) +#define SLC_TC(x) (x + 0x030) +#define SLC_ECC(x) (x + 0x034) +#define SLC_DMA_DATA(x) (x + 0x038) + +/********************************************************************** +* slc_ctrl register definitions +**********************************************************************/ +#define SLCCTRL_SW_RESET (1 << 2) /* Reset the NAND controller bit */ +#define SLCCTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */ +#define SLCCTRL_DMA_START (1 << 0) /* Start DMA channel bit */ + +/********************************************************************** +* slc_cfg register definitions +**********************************************************************/ +#define SLCCFG_CE_LOW (1 << 5) /* Force CE low bit */ +#define SLCCFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */ +#define SLCCFG_ECC_EN (1 << 3) /* ECC enable bit */ +#define SLCCFG_DMA_BURST (1 << 2) /* DMA burst bit */ +#define SLCCFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */ +#define SLCCFG_WIDTH (1 << 0) /* External device width, 0=8bit */ + +/********************************************************************** +* slc_stat register definitions +**********************************************************************/ +#define SLCSTAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */ +#define SLCSTAT_SLC_FIFO (1 << 1) /* SLC FIFO has data bit */ +#define SLCSTAT_NAND_READY (1 << 0) /* NAND device is ready bit */ + +/********************************************************************** +* slc_int_stat, slc_ien, slc_isr, and slc_icr register definitions +**********************************************************************/ +#define SLCSTAT_INT_TC (1 << 1) /* Transfer count bit */ +#define SLCSTAT_INT_RDY_EN (1 << 0) /* Ready interrupt bit */ + +/********************************************************************** +* slc_tac register definitions +**********************************************************************/ +/* Clock setting for RDY write sample wait time in 2*n clocks */ +#define SLCTAC_WDR(n) (((n) & 0xF) << 28) +/* Write pulse width in clock cycles, 1 to 16 clocks */ +#define SLCTAC_WWIDTH(n) (((n) & 0xF) << 24) +/* Write hold time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_WHOLD(n) (((n) & 0xF) << 20) +/* Write setup time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_WSETUP(n) (((n) & 0xF) << 16) +/* Clock setting for RDY read sample wait time in 2*n clocks */ +#define SLCTAC_RDR(n) (((n) & 0xF) << 12) +/* Read pulse width in clock cycles, 1 to 16 clocks */ +#define SLCTAC_RWIDTH(n) (((n) & 0xF) << 8) +/* Read hold time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_RHOLD(n) (((n) & 0xF) << 4) +/* Read setup time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_RSETUP(n) (((n) & 0xF) << 0) + +/********************************************************************** +* slc_ecc register definitions +**********************************************************************/ +/* ECC line party fetch macro */ +#define SLCECC_TO_LINEPAR(n) (((n) >> 6) & 0x7FFF) +#define SLCECC_TO_COLPAR(n) ((n) & 0x3F) + +/* + * DMA requires storage space for the DMA local buffer and the hardware ECC + * storage area. The DMA local buffer is only used if DMA mapping fails + * during runtime. + */ +#define LPC32XX_DMA_DATA_SIZE 4096 +#define LPC32XX_ECC_SAVE_SIZE ((4096 / 256) * 4) + +/* Number of bytes used for ECC stored in NAND per 256 bytes */ +#define LPC32XX_SLC_DEV_ECC_BYTES 3 + +/* + * If the NAND base clock frequency can't be fetched, this frequency will be + * used instead as the base. This rate is used to setup the timing registers + * used for NAND accesses. + */ +#define LPC32XX_DEF_BUS_RATE 133250000 + +/* Milliseconds for DMA FIFO timeout (unlikely anyway) */ +#define LPC32XX_DMA_TIMEOUT 100 + +/* + * NAND ECC Layout for small page NAND devices + * Note: For large and huge page devices, the default layouts are used + */ +static struct nand_ecclayout lpc32xx_nand_oob_16 = { + .eccbytes = 6, + .eccpos = {10, 11, 12, 13, 14, 15}, + .oobfree = { + { .offset = 0, .length = 4 }, + { .offset = 6, .length = 4 }, + }, +}; + +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +/* + * Small page FLASH BBT descriptors, marker at offset 0, version at offset 6 + * Note: Large page devices used the default layout + */ +static struct nand_bbt_descr bbt_smallpage_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_smallpage_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/* + * NAND platform configuration structure + */ +struct lpc32xx_nand_cfg_slc { + uint32_t wdr_clks; + uint32_t wwidth; + uint32_t whold; + uint32_t wsetup; + uint32_t rdr_clks; + uint32_t rwidth; + uint32_t rhold; + uint32_t rsetup; + bool use_bbt; + int wp_gpio; + struct mtd_partition *parts; + unsigned num_parts; +}; + +struct lpc32xx_nand_host { + struct nand_chip nand_chip; + struct lpc32xx_slc_platform_data *pdata; + struct clk *clk; + struct mtd_info mtd; + void __iomem *io_base; + struct lpc32xx_nand_cfg_slc *ncfg; + + struct completion comp; + struct dma_chan *dma_chan; + uint32_t dma_buf_len; + struct dma_slave_config dma_slave_config; + struct scatterlist sgl; + + /* + * DMA and CPU addresses of ECC work area and data buffer + */ + uint32_t *ecc_buf; + uint8_t *data_buf; + dma_addr_t io_base_dma; +}; + +static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) +{ + uint32_t clkrate, tmp; + + /* Reset SLC controller */ + writel(SLCCTRL_SW_RESET, SLC_CTRL(host->io_base)); + udelay(1000); + + /* Basic setup */ + writel(0, SLC_CFG(host->io_base)); + writel(0, SLC_IEN(host->io_base)); + writel((SLCSTAT_INT_TC | SLCSTAT_INT_RDY_EN), + SLC_ICR(host->io_base)); + + /* Get base clock for SLC block */ + clkrate = clk_get_rate(host->clk); + if (clkrate == 0) + clkrate = LPC32XX_DEF_BUS_RATE; + + /* Compute clock setup values */ + tmp = SLCTAC_WDR(host->ncfg->wdr_clks) | + SLCTAC_WWIDTH(1 + (clkrate / host->ncfg->wwidth)) | + SLCTAC_WHOLD(1 + (clkrate / host->ncfg->whold)) | + SLCTAC_WSETUP(1 + (clkrate / host->ncfg->wsetup)) | + SLCTAC_RDR(host->ncfg->rdr_clks) | + SLCTAC_RWIDTH(1 + (clkrate / host->ncfg->rwidth)) | + SLCTAC_RHOLD(1 + (clkrate / host->ncfg->rhold)) | + SLCTAC_RSETUP(1 + (clkrate / host->ncfg->rsetup)); + writel(tmp, SLC_TAC(host->io_base)); +} + +/* + * Hardware specific access to control lines + */ +static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + uint32_t tmp; + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + + /* Does CE state need to be changed? */ + tmp = readl(SLC_CFG(host->io_base)); + if (ctrl & NAND_NCE) + tmp |= SLCCFG_CE_LOW; + else + tmp &= ~SLCCFG_CE_LOW; + writel(tmp, SLC_CFG(host->io_base)); + + if (cmd != NAND_CMD_NONE) { + if (ctrl & NAND_CLE) + writel(cmd, SLC_CMD(host->io_base)); + else + writel(cmd, SLC_ADDR(host->io_base)); + } +} + +/* + * Read the Device Ready pin + */ +static int lpc32xx_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + int rdy = 0; + + if ((readl(SLC_STAT(host->io_base)) & SLCSTAT_NAND_READY) != 0) + rdy = 1; + + return rdy; +} + +/* + * Enable NAND write protect + */ +static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 0); +} + +/* + * Disable NAND write protect + */ +static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 1); +} + +/* + * Prepares SLC for transfers with H/W ECC enabled + */ +static void lpc32xx_nand_ecc_enable(struct mtd_info *mtd, int mode) +{ + /* Hardware ECC is enabled automatically in hardware as needed */ +} + +/* + * Calculates the ECC for the data + */ +static int lpc32xx_nand_ecc_calculate(struct mtd_info *mtd, + const unsigned char *buf, + unsigned char *code) +{ + /* + * ECC is calculated automatically in hardware during syndrome read + * and write operations, so it doesn't need to be calculated here. + */ + return 0; +} + +/* + * Read a single byte from NAND device + */ +static uint8_t lpc32xx_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + + return (uint8_t)readl(SLC_DATA(host->io_base)); +} + +/* + * Simple device read without ECC + */ +static void lpc32xx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + + /* Direct device read with no ECC */ + while (len-- > 0) + *buf++ = (uint8_t)readl(SLC_DATA(host->io_base)); +} + +/* + * Simple device write without ECC + */ +static void lpc32xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + + /* Direct device write with no ECC */ + while (len-- > 0) + writel((uint32_t)*buf++, SLC_DATA(host->io_base)); +} + +/* + * Read the OOB data from the device without ECC using FIFO method + */ +static int lpc32xx_nand_read_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/* + * Write the OOB data to the device without ECC using FIFO method + */ +static int lpc32xx_nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* + * Fills in the ECC fields in the OOB buffer with the hardware generated ECC + */ +static void lpc32xx_slc_ecc_copy(uint8_t *spare, const uint32_t *ecc, int count) +{ + int i; + + for (i = 0; i < (count * 3); i += 3) { + uint32_t ce = ecc[i / 3]; + ce = ~(ce << 2) & 0xFFFFFF; + spare[i + 2] = (uint8_t)(ce & 0xFF); + ce >>= 8; + spare[i + 1] = (uint8_t)(ce & 0xFF); + ce >>= 8; + spare[i] = (uint8_t)(ce & 0xFF); + } +} + +static void lpc32xx_dma_complete_func(void *completion) +{ + complete(completion); +} + +static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, + void *mem, int len, enum dma_transfer_direction dir) +{ + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + struct dma_async_tx_descriptor *desc; + int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int res; + + host->dma_slave_config.direction = dir; + host->dma_slave_config.src_addr = dma; + host->dma_slave_config.dst_addr = dma; + host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.src_maxburst = 4; + host->dma_slave_config.dst_maxburst = 4; + /* DMA controller does flow control: */ + host->dma_slave_config.device_fc = false; + if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { + dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); + return -ENXIO; + } + + sg_init_one(&host->sgl, mem, len); + + res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + if (res != 1) { + dev_err(mtd->dev.parent, "Failed to map sg list\n"); + return -ENXIO; + } + desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, + flags); + if (!desc) { + dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); + goto out1; + } + + init_completion(&host->comp); + desc->callback = lpc32xx_dma_complete_func; + desc->callback_param = &host->comp; + + dmaengine_submit(desc); + dma_async_issue_pending(host->dma_chan); + + wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); + + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + + return 0; +out1: + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + return -ENXIO; +} + +/* + * DMA read/write transfers with ECC support + */ +static int lpc32xx_xfer(struct mtd_info *mtd, uint8_t *buf, int eccsubpages, + int read) +{ + struct nand_chip *chip = mtd->priv; + struct lpc32xx_nand_host *host = chip->priv; + int i, status = 0; + unsigned long timeout; + int res; + enum dma_transfer_direction dir = + read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + uint8_t *dma_buf; + bool dma_mapped; + + if ((void *)buf <= high_memory) { + dma_buf = buf; + dma_mapped = true; + } else { + dma_buf = host->data_buf; + dma_mapped = false; + if (!read) + memcpy(host->data_buf, buf, mtd->writesize); + } + + if (read) { + writel(readl(SLC_CFG(host->io_base)) | + SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | + SLCCFG_DMA_BURST, SLC_CFG(host->io_base)); + } else { + writel((readl(SLC_CFG(host->io_base)) | + SLCCFG_ECC_EN | SLCCFG_DMA_ECC | SLCCFG_DMA_BURST) & + ~SLCCFG_DMA_DIR, + SLC_CFG(host->io_base)); + } + + /* Clear initial ECC */ + writel(SLCCTRL_ECC_CLEAR, SLC_CTRL(host->io_base)); + + /* Transfer size is data area only */ + writel(mtd->writesize, SLC_TC(host->io_base)); + + /* Start transfer in the NAND controller */ + writel(readl(SLC_CTRL(host->io_base)) | SLCCTRL_DMA_START, + SLC_CTRL(host->io_base)); + + for (i = 0; i < chip->ecc.steps; i++) { + /* Data */ + res = lpc32xx_xmit_dma(mtd, SLC_DMA_DATA(host->io_base_dma), + dma_buf + i * chip->ecc.size, + mtd->writesize / chip->ecc.steps, dir); + if (res) + return res; + + /* Always _read_ ECC */ + if (i == chip->ecc.steps - 1) + break; + if (!read) /* ECC availability delayed on write */ + udelay(10); + res = lpc32xx_xmit_dma(mtd, SLC_ECC(host->io_base_dma), + &host->ecc_buf[i], 4, DMA_DEV_TO_MEM); + if (res) + return res; + } + + /* + * According to NXP, the DMA can be finished here, but the NAND + * controller may still have buffered data. After porting to using the + * dmaengine DMA driver (amba-pl080), the condition (DMA_FIFO empty) + * appears to be always true, according to tests. Keeping the check for + * safety reasons for now. + */ + if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) { + dev_warn(mtd->dev.parent, "FIFO not empty!\n"); + timeout = jiffies + msecs_to_jiffies(LPC32XX_DMA_TIMEOUT); + while ((readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) && + time_before(jiffies, timeout)) + cpu_relax(); + if (!time_before(jiffies, timeout)) { + dev_err(mtd->dev.parent, "FIFO held data too long\n"); + status = -EIO; + } + } + + /* Read last calculated ECC value */ + if (!read) + udelay(10); + host->ecc_buf[chip->ecc.steps - 1] = + readl(SLC_ECC(host->io_base)); + + /* Flush DMA */ + dmaengine_terminate_all(host->dma_chan); + + if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO || + readl(SLC_TC(host->io_base))) { + /* Something is left in the FIFO, something is wrong */ + dev_err(mtd->dev.parent, "DMA FIFO failure\n"); + status = -EIO; + } + + /* Stop DMA & HW ECC */ + writel(readl(SLC_CTRL(host->io_base)) & ~SLCCTRL_DMA_START, + SLC_CTRL(host->io_base)); + writel(readl(SLC_CFG(host->io_base)) & + ~(SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | + SLCCFG_DMA_BURST), SLC_CFG(host->io_base)); + + if (!dma_mapped && read) + memcpy(buf, host->data_buf, mtd->writesize); + + return status; +} + +/* + * Read the data and OOB data from the device, use ECC correction with the + * data, disable ECC for the OOB data + */ +static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct lpc32xx_nand_host *host = chip->priv; + int stat, i, status; + uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE]; + + /* Issue read command */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + /* Read data and oob, calculate ECC */ + status = lpc32xx_xfer(mtd, buf, chip->ecc.steps, 1); + + /* Get OOB data */ + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Convert to stored ECC format */ + lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps); + + /* Pointer to ECC data retrieved from NAND spare area */ + oobecc = chip->oob_poi + chip->ecc.layout->eccpos[0]; + + for (i = 0; i < chip->ecc.steps; i++) { + stat = chip->ecc.correct(mtd, buf, oobecc, + &tmpecc[i * chip->ecc.bytes]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + buf += chip->ecc.size; + oobecc += chip->ecc.bytes; + } + + return status; +} + +/* + * Read the data and OOB data from the device, no ECC correction with the + * data or OOB data + */ +static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) +{ + /* Issue read command */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + /* Raw reads can just use the FIFO interface */ + chip->read_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/* + * Write the data and OOB data to the device, use ECC with the data, + * disable ECC for the OOB data + */ +static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct lpc32xx_nand_host *host = chip->priv; + uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0]; + int error; + + /* Write data, calculate ECC on outbound data */ + error = lpc32xx_xfer(mtd, (uint8_t *)buf, chip->ecc.steps, 0); + if (error) + return error; + + /* + * The calculated ECC needs some manual work done to it before + * committing it to NAND. Process the calculated ECC and place + * the resultant values directly into the OOB buffer. */ + lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps); + + /* Write ECC data to device */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/* + * Write the data and OOB data to the device, no ECC correction with the + * data or OOB data + */ +static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required) +{ + /* Raw writes can just use the FIFO interface */ + chip->write_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) +{ + struct mtd_info *mtd = &host->mtd; + dma_cap_mask_t mask; + + if (!host->pdata || !host->pdata->dma_filter) { + dev_err(mtd->dev.parent, "no DMA platform data\n"); + return -ENOENT; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, + "nand-slc"); + if (!host->dma_chan) { + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return -EBUSY; + } + + return 0; +} + +static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) +{ + struct lpc32xx_nand_cfg_slc *ncfg; + struct device_node *np = dev->of_node; + + ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); + if (!ncfg) + return NULL; + + of_property_read_u32(np, "nxp,wdr-clks", &ncfg->wdr_clks); + of_property_read_u32(np, "nxp,wwidth", &ncfg->wwidth); + of_property_read_u32(np, "nxp,whold", &ncfg->whold); + of_property_read_u32(np, "nxp,wsetup", &ncfg->wsetup); + of_property_read_u32(np, "nxp,rdr-clks", &ncfg->rdr_clks); + of_property_read_u32(np, "nxp,rwidth", &ncfg->rwidth); + of_property_read_u32(np, "nxp,rhold", &ncfg->rhold); + of_property_read_u32(np, "nxp,rsetup", &ncfg->rsetup); + + if (!ncfg->wdr_clks || !ncfg->wwidth || !ncfg->whold || + !ncfg->wsetup || !ncfg->rdr_clks || !ncfg->rwidth || + !ncfg->rhold || !ncfg->rsetup) { + dev_err(dev, "chip parameters not specified correctly\n"); + return NULL; + } + + ncfg->use_bbt = of_get_nand_on_flash_bbt(np); + ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); + + return ncfg; +} + +/* + * Probe for NAND controller + */ +static int lpc32xx_nand_probe(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *chip; + struct resource *rc; + struct mtd_part_parser_data ppdata = {}; + int res; + + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (rc == NULL) { + dev_err(&pdev->dev, "No memory resource found for device\n"); + return -EBUSY; + } + + /* Allocate memory for the device structure (and zero it) */ + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + host->io_base_dma = rc->start; + + host->io_base = devm_ioremap_resource(&pdev->dev, rc); + if (IS_ERR(host->io_base)) + return PTR_ERR(host->io_base); + + if (pdev->dev.of_node) + host->ncfg = lpc32xx_parse_dt(&pdev->dev); + if (!host->ncfg) { + dev_err(&pdev->dev, + "Missing or bad NAND config from device tree\n"); + return -ENOENT; + } + if (host->ncfg->wp_gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev, + host->ncfg->wp_gpio, "NAND WP")) { + dev_err(&pdev->dev, "GPIO not available\n"); + return -EBUSY; + } + lpc32xx_wp_disable(host); + + host->pdata = dev_get_platdata(&pdev->dev); + + mtd = &host->mtd; + chip = &host->nand_chip; + chip->priv = host; + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + + /* Get NAND clock */ + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "Clock failure\n"); + res = -ENOENT; + goto err_exit1; + } + clk_enable(host->clk); + + /* Set NAND IO addresses and command/ready functions */ + chip->IO_ADDR_R = SLC_DATA(host->io_base); + chip->IO_ADDR_W = SLC_DATA(host->io_base); + chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; + chip->dev_ready = lpc32xx_nand_device_ready; + chip->chip_delay = 20; /* 20us command delay time */ + + /* Init NAND controller */ + lpc32xx_nand_setup(host); + + platform_set_drvdata(pdev, host); + + /* NAND callbacks for LPC32xx SLC hardware */ + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->read_byte = lpc32xx_nand_read_byte; + chip->read_buf = lpc32xx_nand_read_buf; + chip->write_buf = lpc32xx_nand_write_buf; + chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; + chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; + chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; + chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; + chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; + chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; + chip->ecc.calculate = lpc32xx_nand_ecc_calculate; + chip->ecc.correct = nand_correct_data; + chip->ecc.strength = 1; + chip->ecc.hwctl = lpc32xx_nand_ecc_enable; + + /* bitflip_threshold's default is defined as ecc_strength anyway. + * Unfortunately, it is set only later at add_mtd_device(). Meanwhile + * being 0, it causes bad block table scanning errors in + * nand_scan_tail(), so preparing it here already. */ + mtd->bitflip_threshold = chip->ecc.strength; + + /* + * Allocate a large enough buffer for a single huge page plus + * extra space for the spare area and ECC storage area + */ + host->dma_buf_len = LPC32XX_DMA_DATA_SIZE + LPC32XX_ECC_SAVE_SIZE; + host->data_buf = devm_kzalloc(&pdev->dev, host->dma_buf_len, + GFP_KERNEL); + if (host->data_buf == NULL) { + res = -ENOMEM; + goto err_exit2; + } + + res = lpc32xx_nand_dma_setup(host); + if (res) { + res = -EIO; + goto err_exit2; + } + + /* Find NAND device */ + if (nand_scan_ident(mtd, 1, NULL)) { + res = -ENXIO; + goto err_exit3; + } + + /* OOB and ECC CPU and DMA work areas */ + host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); + + /* + * Small page FLASH has a unique OOB layout, but large and huge + * page FLASH use the standard layout. Small page FLASH uses a + * custom BBT marker layout. + */ + if (mtd->writesize <= 512) + chip->ecc.layout = &lpc32xx_nand_oob_16; + + /* These sizes remain the same regardless of page size */ + chip->ecc.size = 256; + chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; + chip->ecc.prepad = chip->ecc.postpad = 0; + + /* Avoid extra scan if using BBT, setup BBT support */ + if (host->ncfg->use_bbt) { + chip->bbt_options |= NAND_BBT_USE_FLASH; + + /* + * Use a custom BBT marker setup for small page FLASH that + * won't interfere with the ECC layout. Large and huge page + * FLASH use the standard layout. + */ + if (mtd->writesize <= 512) { + chip->bbt_td = &bbt_smallpage_main_descr; + chip->bbt_md = &bbt_smallpage_mirror_descr; + } + } + + /* + * Fills out all the uninitialized function pointers with the defaults + */ + if (nand_scan_tail(mtd)) { + res = -ENXIO; + goto err_exit3; + } + + mtd->name = "nxp_lpc3220_slc"; + ppdata.of_node = pdev->dev.of_node; + res = mtd_device_parse_register(mtd, NULL, &ppdata, host->ncfg->parts, + host->ncfg->num_parts); + if (!res) + return res; + + nand_release(mtd); + +err_exit3: + dma_release_channel(host->dma_chan); +err_exit2: + clk_disable(host->clk); +err_exit1: + lpc32xx_wp_enable(host); + + return res; +} + +/* + * Remove NAND device. + */ +static int lpc32xx_nand_remove(struct platform_device *pdev) +{ + uint32_t tmp; + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = &host->mtd; + + nand_release(mtd); + dma_release_channel(host->dma_chan); + + /* Force CE high */ + tmp = readl(SLC_CTRL(host->io_base)); + tmp &= ~SLCCFG_CE_LOW; + writel(tmp, SLC_CTRL(host->io_base)); + + clk_disable(host->clk); + lpc32xx_wp_enable(host); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_nand_resume(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + + /* Re-enable NAND clock */ + clk_enable(host->clk); + + /* Fresh init of NAND controller */ + lpc32xx_nand_setup(host); + + /* Disable write protect */ + lpc32xx_wp_disable(host); + + return 0; +} + +static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) +{ + uint32_t tmp; + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + + /* Force CE high */ + tmp = readl(SLC_CTRL(host->io_base)); + tmp &= ~SLCCFG_CE_LOW; + writel(tmp, SLC_CTRL(host->io_base)); + + /* Enable write protect for safety */ + lpc32xx_wp_enable(host); + + /* Disable clock */ + clk_disable(host->clk); + + return 0; +} + +#else +#define lpc32xx_nand_resume NULL +#define lpc32xx_nand_suspend NULL +#endif + +static const struct of_device_id lpc32xx_nand_match[] = { + { .compatible = "nxp,lpc3220-slc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); + +static struct platform_driver lpc32xx_nand_driver = { + .probe = lpc32xx_nand_probe, + .remove = lpc32xx_nand_remove, + .resume = lpc32xx_nand_resume, + .suspend = lpc32xx_nand_suspend, + .driver = { + .name = LPC32XX_MODNAME, + .owner = THIS_MODULE, + .of_match_table = lpc32xx_nand_match, + }, +}; + +module_platform_driver(lpc32xx_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX SLC controller"); diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 469e649c911..e78841a2dcc 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -29,13 +29,15 @@ #include <linux/clk.h> #include <linux/gfp.h> #include <linux/delay.h> -#include <linux/init.h> +#include <linux/err.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <asm/mpc5121.h> @@ -130,10 +132,6 @@ struct mpc5121_nfc_prv { static void mpc5121_nfc_done(struct mtd_info *mtd); -#ifdef CONFIG_MTD_PARTITIONS -static const char *mpc5121_nfc_pprobes[] = { "cmdlinepart", NULL }; -#endif - /* Read NFC register */ static inline u16 nfc_read(struct mtd_info *mtd, uint reg) { @@ -509,27 +507,6 @@ static void mpc5121_nfc_write_buf(struct mtd_info *mtd, mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); } -/* Compare buffer with NAND flash */ -static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, - const u_char *buf, int len) -{ - u_char tmp[256]; - uint bsize; - - while (len) { - bsize = min(len, 256); - mpc5121_nfc_read_buf(mtd, tmp, bsize); - - if (memcmp(buf, tmp, bsize)) - return 1; - - buf += bsize; - len -= bsize; - } - - return 0; -} - /* Read byte from NFC buffers */ static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) { @@ -641,32 +618,28 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; struct mpc5121_nfc_prv *prv = chip->priv; - if (prv->clk) { - clk_disable(prv->clk); - clk_put(prv->clk); - } + if (prv->clk) + clk_disable_unprepare(prv->clk); if (prv->csreg) iounmap(prv->csreg); } -static int __devinit mpc5121_nfc_probe(struct platform_device *op, - const struct of_device_id *match) +static int mpc5121_nfc_probe(struct platform_device *op) { struct device_node *rootnode, *dn = op->dev.of_node; + struct clk *clk; struct device *dev = &op->dev; struct mpc5121_nfc_prv *prv; struct resource res; struct mtd_info *mtd; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *parts; -#endif struct nand_chip *chip; unsigned long regs_paddr, regs_size; const __be32 *chips_no; int resettime = 0; int retval = 0; int rev, len; + struct mtd_part_parser_data ppdata; /* * Check SoC revision. This driver supports only NFC @@ -679,10 +652,8 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op, } prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); - if (!prv) { - dev_err(dev, "Memory exhausted!\n"); + if (!prv) return -ENOMEM; - } mtd = &prv->mtd; chip = &prv->chip; @@ -717,7 +688,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op, } regs_paddr = res.start; - regs_size = res.end - res.start + 1; + regs_size = resource_size(&res); if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { dev_err(dev, "Error requesting memory region!\n"); @@ -731,15 +702,15 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op, } mtd->name = "MPC5121 NAND"; + ppdata.of_node = dn; chip->dev_ready = mpc5121_nfc_dev_ready; chip->cmdfunc = mpc5121_nfc_command; chip->read_byte = mpc5121_nfc_read_byte; chip->read_word = mpc5121_nfc_read_word; chip->read_buf = mpc5121_nfc_read_buf; chip->write_buf = mpc5121_nfc_write_buf; - chip->verify_buf = mpc5121_nfc_verify_buf; chip->select_chip = mpc5121_nfc_select_chip; - chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; + chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; /* Support external chip-select logic on ADS5121 board */ @@ -757,14 +728,18 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op, of_node_put(rootnode); /* Enable NFC clock */ - prv->clk = clk_get(dev, "nfc_clk"); - if (!prv->clk) { + clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(clk)) { dev_err(dev, "Unable to acquire NFC clock!\n"); - retval = -ENODEV; + retval = PTR_ERR(clk); goto error; } - - clk_enable(prv->clk); + retval = clk_prepare_enable(clk); + if (retval) { + dev_err(dev, "Unable to enable NFC clock!\n"); + goto error; + } + prv->clk = clk; /* Reset NAND Flash controller */ nfc_set(mtd, NFC_CONFIG1, NFC_RESET); @@ -808,7 +783,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op, /* Detect NAND chips */ if (nand_scan(mtd, be32_to_cpup(chips_no))) { dev_err(dev, "NAND Flash not found !\n"); - devm_free_irq(dev, prv->irq, mtd); retval = -ENXIO; goto error; } @@ -833,7 +807,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op, default: dev_err(dev, "Unsupported NAND flash!\n"); - devm_free_irq(dev, prv->irq, mtd); retval = -ENXIO; goto error; } @@ -841,28 +814,9 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op, dev_set_drvdata(dev, mtd); /* Register device in MTD */ -#ifdef CONFIG_MTD_PARTITIONS - retval = parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &parts, 0); -#ifdef CONFIG_MTD_OF_PARTS - if (retval == 0) - retval = of_mtd_parse_partitions(dev, dn, &parts); -#endif - if (retval < 0) { - dev_err(dev, "Error parsing MTD partitions!\n"); - devm_free_irq(dev, prv->irq, mtd); - retval = -EINVAL; - goto error; - } - - if (retval > 0) - retval = add_mtd_partitions(mtd, parts, retval); - else -#endif - retval = add_mtd_device(mtd); - + retval = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (retval) { dev_err(dev, "Error adding MTD device!\n"); - devm_free_irq(dev, prv->irq, mtd); goto error; } @@ -872,28 +826,25 @@ error: return retval; } -static int __devexit mpc5121_nfc_remove(struct platform_device *op) +static int mpc5121_nfc_remove(struct platform_device *op) { struct device *dev = &op->dev; struct mtd_info *mtd = dev_get_drvdata(dev); - struct nand_chip *chip = mtd->priv; - struct mpc5121_nfc_prv *prv = chip->priv; nand_release(mtd); - devm_free_irq(dev, prv->irq, mtd); mpc5121_nfc_free(dev, mtd); return 0; } -static struct of_device_id mpc5121_nfc_match[] __devinitdata = { +static struct of_device_id mpc5121_nfc_match[] = { { .compatible = "fsl,mpc5121-nfc", }, {}, }; -static struct of_platform_driver mpc5121_nfc_driver = { +static struct platform_driver mpc5121_nfc_driver = { .probe = mpc5121_nfc_probe, - .remove = __devexit_p(mpc5121_nfc_remove), + .remove = mpc5121_nfc_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, @@ -901,19 +852,7 @@ static struct of_platform_driver mpc5121_nfc_driver = { }, }; -static int __init mpc5121_nfc_init(void) -{ - return of_register_platform_driver(&mpc5121_nfc_driver); -} - -module_init(mpc5121_nfc_init); - -static void __exit mpc5121_nfc_cleanup(void) -{ - of_unregister_platform_driver(&mpc5121_nfc_driver); -} - -module_exit(mpc5121_nfc_cleanup); +module_platform_driver(mpc5121_nfc_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("MPC5121 NAND MTD driver"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ef932ba55a0..dba262bf766 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -32,18 +32,15 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_mtd.h> #include <asm/mach/flash.h> -#include <mach/mxc_nand.h> -#include <mach/hardware.h> +#include <linux/platform_data/mtd-mxc_nand.h> #define DRIVER_NAME "mxc_nand" -#define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) -#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21()) -#define nfc_is_v3_2() cpu_is_mx51() -#define nfc_is_v3() nfc_is_v3_2() - /* Addresses for NFC registers */ #define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) #define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) @@ -56,8 +53,14 @@ #define NFC_V1_V2_WRPROT (host->regs + 0x12) #define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) #define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) -#define NFC_V21_UNLOCKSTART_BLKADDR (host->regs + 0x20) -#define NFC_V21_UNLOCKEND_BLKADDR (host->regs + 0x22) +#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) +#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) +#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) +#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) +#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) +#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) +#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) +#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) #define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) #define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) #define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) @@ -114,7 +117,7 @@ #define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) #define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) #define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) -#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) +#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift) #define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) #define NFC_V3_CONFIG2_INT_MSK (1 << 15) #define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) @@ -134,14 +137,48 @@ #define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) +struct mxc_nand_host; + +struct mxc_nand_devtype_data { + void (*preset)(struct mtd_info *); + 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); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); + void (*irq_control)(struct mxc_nand_host *, int); + u32 (*get_ecc_status)(struct mxc_nand_host *); + struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k; + 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); + + /* + * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked + * (CONFIG1:INT_MSK is set). To handle this the driver uses + * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK + */ + int irqpending_quirk; + int needs_ip; + + size_t regs_offset; + size_t spare0_offset; + size_t axi_offset; + + int spare_len; + int eccbytes; + int eccsize; + int ppb_shift; +}; + struct mxc_nand_host { struct mtd_info mtd; struct nand_chip nand; - struct mtd_partition *parts; struct device *dev; - void *spare0; - void *main_area0; + void __iomem *spare0; + void __iomem *main_area0; void __iomem *base; void __iomem *regs; @@ -152,21 +189,15 @@ struct mxc_nand_host { int clk_act; int irq; int eccsize; + int active_cs; struct completion op_completion; uint8_t *data_buf; unsigned int buf_start; - int spare_len; - - void (*preset)(struct mtd_info *); - 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); - void (*send_read_id)(struct mxc_nand_host *); - uint16_t (*get_dev_status)(struct mxc_nand_host *); - int (*check_int)(struct mxc_nand_host *); - void (*irq_control)(struct mxc_nand_host *, int); + + const struct mxc_nand_devtype_data *devtype_data; + struct mxc_nand_platform_data pdata; }; /* OOB placement block for use with hardware ecc generation */ @@ -211,22 +242,52 @@ static struct nand_ecclayout nandv2_hw_eccoob_largepage = { } }; -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; -#endif +/* OOB description for 4096 byte pages with 128 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_4k = { + .eccbytes = 8 * 9, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63, + 71, 72, 73, 74, 75, 76, 77, 78, 79, + 87, 88, 89, 90, 91, 92, 93, 94, 95, + 103, 104, 105, 106, 107, 108, 109, 110, 111, + 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + {.offset = 2, .length = 4}, + {.offset = 16, .length = 7}, + {.offset = 32, .length = 7}, + {.offset = 48, .length = 7}, + {.offset = 64, .length = 7}, + {.offset = 80, .length = 7}, + {.offset = 96, .length = 7}, + {.offset = 112, .length = 7}, + } +}; -static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) -{ - struct mxc_nand_host *host = dev_id; +static const char * const part_probes[] = { + "cmdlinepart", "RedBoot", "ofpart", NULL }; - if (!host->check_int(host)) - return IRQ_NONE; +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u32 *t = trg; + const __iomem u32 *s = src; - host->irq_control(host, 0); + for (i = 0; i < (size >> 2); i++) + *t++ = __raw_readl(s++); +} - complete(&host->op_completion); +static void memcpy32_toio(void __iomem *trg, const void *src, int size) +{ + int i; + u32 __iomem *t = trg; + const u32 *s = src; - return IRQ_HANDLED; + for (i = 0; i < (size >> 2); i++) + __raw_writel(*s++, t++); } static int check_int_v3(struct mxc_nand_host *host) @@ -251,26 +312,12 @@ static int check_int_v1_v2(struct mxc_nand_host *host) if (!(tmp & NFC_V1_V2_CONFIG2_INT)) return 0; - if (!cpu_is_mx21()) + if (!host->devtype_data->irqpending_quirk) writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); return 1; } -/* - * It has been observed that the i.MX21 cannot read the CONFIG2:INT bit - * if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the - * driver can enable/disable the irq line rather than simply masking the - * interrupts. - */ -static void irq_control_mx21(struct mxc_nand_host *host, int activate) -{ - if (activate) - enable_irq(host->irq); - else - disable_irq_nosync(host->irq); -} - static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) { uint16_t tmp; @@ -299,6 +346,47 @@ static void irq_control_v3(struct mxc_nand_host *host, int activate) writel(tmp, NFC_V3_CONFIG2); } +static void irq_control(struct mxc_nand_host *host, int activate) +{ + if (host->devtype_data->irqpending_quirk) { + if (activate) + enable_irq(host->irq); + else + disable_irq_nosync(host->irq); + } else { + host->devtype_data->irq_control(host, activate); + } +} + +static u32 get_ecc_status_v1(struct mxc_nand_host *host) +{ + return readw(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v2(struct mxc_nand_host *host) +{ + return readl(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v3(struct mxc_nand_host *host) +{ + return readl(NFC_V3_ECC_STATUS_RESULT); +} + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + struct mxc_nand_host *host = dev_id; + + if (!host->devtype_data->check_int(host)) + return IRQ_NONE; + + irq_control(host, 0); + + complete(&host->op_completion); + + return IRQ_HANDLED; +} + /* This function polls the NANDFC to wait for the basic operation to * complete by checking the INT bit of config2 register. */ @@ -307,21 +395,20 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) int max_retries = 8000; if (useirq) { - if (!host->check_int(host)) { - INIT_COMPLETION(host->op_completion); - host->irq_control(host, 1); + if (!host->devtype_data->check_int(host)) { + reinit_completion(&host->op_completion); + irq_control(host, 1); wait_for_completion(&host->op_completion); } } else { while (max_retries-- > 0) { - if (host->check_int(host)) + if (host->devtype_data->check_int(host)) break; udelay(1); } if (max_retries < 0) - DEBUG(MTD_DEBUG_LEVEL0, "%s: INT not set\n", - __func__); + pr_debug("%s: INT not set\n", __func__); } } @@ -341,12 +428,12 @@ static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) * waits for completion. */ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) { - DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); + pr_debug("send_cmd(host, 0x%x, %d)\n", cmd, useirq); writew(cmd, NFC_V1_V2_FLASH_CMD); writew(NFC_CMD, NFC_V1_V2_CONFIG2); - if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { + if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { int max_retries = 100; /* Reset completion is indicated by NFC_CONFIG2 */ /* being set to 0 */ @@ -357,8 +444,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) udelay(1); } if (max_retries < 0) - DEBUG(MTD_DEBUG_LEVEL0, "%s: RESET failed\n", - __func__); + pr_debug("%s: RESET failed\n", __func__); } else { /* Wait for operation to complete */ wait_op_done(host, useirq); @@ -381,7 +467,7 @@ static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) * a NAND command. */ static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) { - DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast); + pr_debug("send_addr(host, 0x%x %d)\n", addr, islast); writew(addr, NFC_V1_V2_FLASH_ADDR); writew(NFC_ADDR, NFC_V1_V2_CONFIG2); @@ -406,13 +492,27 @@ static void send_page_v3(struct mtd_info *mtd, unsigned int ops) wait_op_done(host, false); } -static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) +static void send_page_v2(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* NANDFC buffer 0 is used for page read/write */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); +} + +static void send_page_v1(struct mtd_info *mtd, unsigned int ops) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (nfc_is_v1() && mtd->writesize > 512) + if (mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -420,7 +520,7 @@ static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) for (i = 0; i < bufs; i++) { /* NANDFC buffer 0 is used for page read/write */ - writew(i, NFC_V1_V2_BUF_ADDR); + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); writew(ops, NFC_V1_V2_CONFIG2); @@ -431,12 +531,23 @@ static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) static void send_read_id_v3(struct mxc_nand_host *host) { + struct nand_chip *this = &host->nand; + /* Read ID into main buffer */ writel(NFC_ID, NFC_V3_LAUNCH); wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy32_fromio(host->data_buf, host->main_area0, 16); + + if (this->options & NAND_BUSWIDTH_16) { + /* compress the ID info */ + host->data_buf[1] = host->data_buf[2]; + host->data_buf[2] = host->data_buf[4]; + host->data_buf[3] = host->data_buf[6]; + host->data_buf[4] = host->data_buf[8]; + host->data_buf[5] = host->data_buf[10]; + } } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -445,14 +556,14 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host) struct nand_chip *this = &host->nand; /* NANDFC buffer 0 is used for device ID output */ - writew(0x0, NFC_V1_V2_BUF_ADDR); + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); writew(NFC_ID, NFC_V1_V2_CONFIG2); /* Wait for operation to complete */ wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy32_fromio(host->data_buf, host->main_area0, 16); if (this->options & NAND_BUSWIDTH_16) { /* compress the ID info */ @@ -480,7 +591,7 @@ static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) uint32_t store; uint16_t ret; - writew(0x0, NFC_V1_V2_BUF_ADDR); + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); /* * The device status is stored in main_area0. To @@ -528,11 +639,10 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, * additional correction. 2-Bit errors cannot be corrected by * HW ECC, so we need to return failure */ - uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT); + uint16_t ecc_status = get_ecc_status_v1(host); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { - DEBUG(MTD_DEBUG_LEVEL0, - "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); return -1; } @@ -554,10 +664,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, no_subpages = mtd->writesize >> 9; - if (nfc_is_v21()) - ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); - else - ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + ecc_stat = host->devtype_data->get_ecc_status(host); do { err = ecc_stat & ecc_bit_mask; @@ -570,7 +677,6 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, ecc_stat >>= 4; } while (--no_subpages); - mtd->ecc_stats.corrected += ret; pr_debug("%d Symbol Correctable RS-ECC Error\n", ret); return ret; @@ -590,7 +696,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) /* Check for status request */ if (host->status_request) - return host->get_dev_status(host) & 0xFF; + return host->devtype_data->get_dev_status(host) & 0xFF; ret = *(uint8_t *)(host->data_buf + host->buf_start); host->buf_start++; @@ -641,45 +747,56 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) n = min(n, len); - memcpy(buf, host->data_buf + col, len); - - host->buf_start += len; -} + memcpy(buf, host->data_buf + col, n); -/* Used by the upper layer to verify the data in NAND Flash - * with the data in the buf. */ -static int mxc_nand_verify_buf(struct mtd_info *mtd, - const u_char *buf, int len) -{ - return -EFAULT; + host->buf_start += n; } /* This function is used by upper layer for select and * deselect of the NAND chip */ -static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - switch (chip) { - case -1: + if (chip == -1) { /* Disable the NFC clock */ if (host->clk_act) { - clk_disable(host->clk); + clk_disable_unprepare(host->clk); host->clk_act = 0; } - break; - case 0: + return; + } + + if (!host->clk_act) { /* Enable the NFC clock */ - if (!host->clk_act) { - clk_enable(host->clk); - host->clk_act = 1; + clk_prepare_enable(host->clk); + host->clk_act = 1; + } +} + +static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable_unprepare(host->clk); + host->clk_act = 0; } - break; + return; + } - default: - break; + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_prepare_enable(host->clk); + host->clk_act = 1; } + + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); } /* @@ -692,23 +809,23 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) u16 i, j; u16 n = mtd->writesize >> 9; u8 *d = host->data_buf + mtd->writesize; - u8 *s = host->spare0; - u16 t = host->spare_len; + u8 __iomem *s = host->spare0; + u16 t = host->devtype_data->spare_len; j = (mtd->oobsize / n >> 1) << 1; if (bfrom) { for (i = 0; i < n - 1; i++) - memcpy(d + i * j, s + i * t, j); + memcpy32_fromio(d + i * j, s + i * t, j); /* the last section */ - memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); + memcpy32_fromio(d + i * j, s + i * t, mtd->oobsize - i * j); } else { for (i = 0; i < n - 1; i++) - memcpy(&s[i * t], &d[i * j], j); + memcpy32_toio(&s[i * t], &d[i * j], j); /* the last section */ - memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); + memcpy32_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j); } } @@ -722,38 +839,47 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) /* * MXC NANDFC can only perform full page+spare or * spare-only read/write. When the upper layers - * layers perform a read/write buf operation, - * we will used the saved column address to index into - * the full page. + * perform a read/write buf operation, the saved column + * address is used to index into the full page. */ - host->send_addr(host, 0, page_addr == -1); + host->devtype_data->send_addr(host, 0, page_addr == -1); if (mtd->writesize > 512) /* another col addr cycle for 2k page */ - host->send_addr(host, 0, false); + host->devtype_data->send_addr(host, 0, false); } /* Write out page address, if necessary */ if (page_addr != -1) { /* paddr_0 - p_addr_7 */ - host->send_addr(host, (page_addr & 0xff), false); + host->devtype_data->send_addr(host, (page_addr & 0xff), false); if (mtd->writesize > 512) { if (mtd->size >= 0x10000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + 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->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } else { /* One more address cycle for higher density devices */ if (mtd->size >= 0x4000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + 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->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } } } @@ -775,7 +901,7 @@ static int get_eccsize(struct mtd_info *mtd) return 8; } -static void preset_v1_v2(struct mtd_info *mtd) +static void preset_v1(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -784,13 +910,40 @@ static void preset_v1_v2(struct mtd_info *mtd) if (nand_chip->ecc.mode == NAND_ECC_HW) config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - if (nfc_is_v21()) - config1 |= NFC_V2_CONFIG1_FP_INT; + if (!host->devtype_data->irqpending_quirk) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + host->eccsize = 1; - if (!cpu_is_mx21()) + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static void preset_v2(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t config1 = 0; + + if (nand_chip->ecc.mode == NAND_ECC_HW) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + config1 |= NFC_V2_CONFIG1_FP_INT; + + if (!host->devtype_data->irqpending_quirk) config1 |= NFC_V1_V2_CONFIG1_INT_MSK; - if (nfc_is_v21() && mtd->writesize) { + if (mtd->writesize) { uint16_t pages_per_block = mtd->erasesize / mtd->writesize; host->eccsize = get_eccsize(mtd); @@ -809,14 +962,14 @@ static void preset_v1_v2(struct mtd_info *mtd) writew(0x2, NFC_V1_V2_CONFIG); /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR); - } else if (nfc_is_v1()) { - writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); - writew(0x4000, NFC_V1_UNLOCKEND_BLKADDR); - } else - BUG(); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); /* Unlock Block Command for given address range */ writew(0x4, NFC_V1_V2_WRPROT); @@ -867,7 +1020,9 @@ static void preset_v3(struct mtd_info *mtd) } if (mtd->writesize) { - config2 |= NFC_V3_CONFIG2_PPB(ffs(mtd->erasesize / mtd->writesize) - 6); + config2 |= NFC_V3_CONFIG2_PPB( + ffs(mtd->erasesize / mtd->writesize) - 6, + host->devtype_data->ppb_shift); host->eccsize = get_eccsize(mtd); if (host->eccsize == 8) config2 |= NFC_V3_CONFIG2_ECC_MODE_8; @@ -897,8 +1052,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", command, column, page_addr); /* Reset command state information */ @@ -907,15 +1061,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { case NAND_CMD_RESET: - host->preset(mtd); - host->send_cmd(host, command, false); + host->devtype_data->preset(mtd); + host->devtype_data->send_cmd(host, command, false); break; case NAND_CMD_STATUS: host->buf_start = 0; host->status_request = true; - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -928,15 +1082,17 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, command = NAND_CMD_READ0; /* only READ0 is valid */ - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); if (mtd->writesize > 512) - host->send_cmd(host, NAND_CMD_READSTART, true); + host->devtype_data->send_cmd(host, + NAND_CMD_READSTART, true); - host->send_page(mtd, NFC_OUTPUT); + host->devtype_data->send_page(mtd, NFC_OUTPUT); - memcpy(host->data_buf, host->main_area0, mtd->writesize); + memcpy32_fromio(host->data_buf, host->main_area0, + mtd->writesize); copy_spare(mtd, true); break; @@ -947,28 +1103,28 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->buf_start = column; - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: - memcpy(host->main_area0, host->data_buf, mtd->writesize); + memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - host->send_page(mtd, NFC_INPUT); - host->send_cmd(host, command, true); + host->devtype_data->send_page(mtd, NFC_INPUT); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_READID: - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); - host->send_read_id(host); + host->devtype_data->send_read_id(host); host->buf_start = column; break; case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -1002,23 +1158,256 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern, }; -static int __init mxcnd_probe(struct platform_device *pdev) +/* v1 + irqpending_quirk: i.MX21 */ +static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { + .preset = preset_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 1, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ +static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { + .preset = preset_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .axi_offset = 0, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v21: i.MX25, i.MX35 */ +static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { + .preset = preset_v2, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v2, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v2, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_4k, + .select_chip = mxc_nand_select_chip_v2, + .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0x1e00, + .spare0_offset = 0x1000, + .axi_offset = 0, + .spare_len = 64, + .eccbytes = 9, + .eccsize = 0, +}; + +/* v3.2a: i.MX51 */ +static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { + .preset = preset_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, + .ppb_shift = 7, +}; + +/* v3.2b: i.MX53 */ +static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { + .preset = preset_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, + .ppb_shift = 8, +}; + +static inline int is_imx21_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx21_nand_devtype_data; +} + +static inline int is_imx27_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx27_nand_devtype_data; +} + +static inline int is_imx25_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx25_nand_devtype_data; +} + +static inline int is_imx51_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx51_nand_devtype_data; +} + +static inline int is_imx53_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx53_nand_devtype_data; +} + +static struct platform_device_id mxcnd_devtype[] = { + { + .name = "imx21-nand", + .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data, + }, { + .name = "imx27-nand", + .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data, + }, { + .name = "imx25-nand", + .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data, + }, { + .name = "imx51-nand", + .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data, + }, { + .name = "imx53-nand", + .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mxcnd_devtype); + +#ifdef CONFIG_OF_MTD +static const struct of_device_id mxcnd_dt_ids[] = { + { + .compatible = "fsl,imx21-nand", + .data = &imx21_nand_devtype_data, + }, { + .compatible = "fsl,imx27-nand", + .data = &imx27_nand_devtype_data, + }, { + .compatible = "fsl,imx25-nand", + .data = &imx25_nand_devtype_data, + }, { + .compatible = "fsl,imx51-nand", + .data = &imx51_nand_devtype_data, + }, { + .compatible = "fsl,imx53-nand", + .data = &imx53_nand_devtype_data, + }, + { /* sentinel */ } +}; + +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + struct device_node *np = host->dev->of_node; + struct mxc_nand_platform_data *pdata = &host->pdata; + const struct of_device_id *of_id = + of_match_device(mxcnd_dt_ids, host->dev); + int buswidth; + + if (!np) + return 1; + + if (of_get_nand_ecc_mode(np) >= 0) + pdata->hw_ecc = 1; + + pdata->flash_bbt = of_get_nand_on_flash_bbt(np); + + buswidth = of_get_nand_bus_width(np); + if (buswidth < 0) + return buswidth; + + pdata->width = buswidth / 8; + + host->devtype_data = of_id->data; + + return 0; +} +#else +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + return 1; +} +#endif + +static int mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; struct mtd_info *mtd; - struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host; struct resource *res; - int err = 0, __maybe_unused nr_parts = 0; - struct nand_ecclayout *oob_smallpage, *oob_largepage; + int err = 0; /* Allocate memory for MTD device structure and private data */ - host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + - NAND_MAX_OOBSIZE, GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host), + GFP_KERNEL); if (!host) return -ENOMEM; - host->data_buf = (uint8_t *)(host + 1); + /* allocate a temporary buffer for the nand_scan_ident() */ + host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL); + if (!host->data_buf) + return -ENOMEM; host->dev = &pdev->dev; /* structures must be linked */ @@ -1035,157 +1424,142 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->priv = host; this->dev_ready = mxc_nand_dev_ready; this->cmdfunc = mxc_nand_command; - this->select_chip = mxc_nand_select_chip; this->read_byte = mxc_nand_read_byte; this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; this->read_buf = mxc_nand_read_buf; - this->verify_buf = mxc_nand_verify_buf; - host->clk = clk_get(&pdev->dev, "nfc"); - if (IS_ERR(host->clk)) { - err = PTR_ERR(host->clk); - goto eclk; + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + err = mxcnd_probe_dt(host); + if (err > 0) { + struct mxc_nand_platform_data *pdata = + dev_get_platdata(&pdev->dev); + if (pdata) { + host->pdata = *pdata; + host->devtype_data = (struct mxc_nand_devtype_data *) + pdev->id_entry->driver_data; + } else { + err = -ENODEV; + } } + if (err < 0) + return err; - clk_enable(host->clk); - host->clk_act = 1; + if (host->devtype_data->needs_ip) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->regs_ip = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->regs_ip)) + return PTR_ERR(host->regs_ip); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -ENODEV; - goto eres; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); } - host->base = ioremap(res->start, resource_size(res)); - if (!host->base) { - err = -ENOMEM; - goto eres; - } + host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->main_area0 = host->base; - if (nfc_is_v1() || nfc_is_v21()) { - host->preset = preset_v1_v2; - host->send_cmd = send_cmd_v1_v2; - host->send_addr = send_addr_v1_v2; - host->send_page = send_page_v1_v2; - host->send_read_id = send_read_id_v1_v2; - host->get_dev_status = get_dev_status_v1_v2; - host->check_int = check_int_v1_v2; - if (cpu_is_mx21()) - host->irq_control = irq_control_mx21; - else - host->irq_control = irq_control_v1_v2; - } + if (host->devtype_data->regs_offset) + host->regs = host->base + host->devtype_data->regs_offset; + host->spare0 = host->base + host->devtype_data->spare0_offset; + if (host->devtype_data->axi_offset) + host->regs_axi = host->base + host->devtype_data->axi_offset; - if (nfc_is_v21()) { - host->regs = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { - host->regs = host->base + 0xe00; - host->spare0 = host->base + 0x800; - host->spare_len = 16; - oob_smallpage = &nandv1_hw_eccoob_smallpage; - oob_largepage = &nandv1_hw_eccoob_largepage; - this->ecc.bytes = 3; - host->eccsize = 1; - } else if (nfc_is_v3_2()) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - err = -ENODEV; - goto eirq; - } - host->regs_ip = ioremap(res->start, resource_size(res)); - if (!host->regs_ip) { - err = -ENOMEM; - goto eirq; - } - host->regs_axi = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - host->preset = preset_v3; - host->send_cmd = send_cmd_v3; - host->send_addr = send_addr_v3; - host->send_page = send_page_v3; - host->send_read_id = send_read_id_v3; - host->check_int = check_int_v3; - host->get_dev_status = get_dev_status_v3; - host->irq_control = irq_control_v3; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - } else - BUG(); + this->ecc.bytes = host->devtype_data->eccbytes; + host->eccsize = host->devtype_data->eccsize; + this->select_chip = host->devtype_data->select_chip; this->ecc.size = 512; - this->ecc.layout = oob_smallpage; + this->ecc.layout = host->devtype_data->ecclayout_512; - if (pdata->hw_ecc) { + if (host->pdata.hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; - if (nfc_is_v1()) - this->ecc.correct = mxc_nand_correct_data_v1; - else - this->ecc.correct = mxc_nand_correct_data_v2_v3; + this->ecc.correct = host->devtype_data->correct_data; this->ecc.mode = NAND_ECC_HW; } else { this->ecc.mode = NAND_ECC_SOFT; } - /* NAND bus width determines access funtions used by upper layer */ - if (pdata->width == 2) + /* NAND bus width determines access functions used by upper layer */ + if (host->pdata.width == 2) this->options |= NAND_BUSWIDTH_16; - if (pdata->flash_bbt) { + if (host->pdata.flash_bbt) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; /* update flash based bbt */ - this->options |= NAND_USE_FLASH_BBT; + this->bbt_options |= NAND_BBT_USE_FLASH; } init_completion(&host->op_completion); host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) + return host->irq; /* - * mask the interrupt. For i.MX21 explicitely call - * irq_control_v1_v2 to use the mask bit. We can't call - * disable_irq_nosync() for an interrupt we do not own yet. + * Use host->devtype_data->irq_control() here instead of irq_control() + * because we must not disable_irq_nosync without having requested the + * irq. */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 0); - else - host->irq_control(host, 0); + host->devtype_data->irq_control(host, 0); - err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); + err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq, + 0, DRIVER_NAME, host); if (err) - goto eirq; + return err; - host->irq_control(host, 0); + err = clk_prepare_enable(host->clk); + if (err) + return err; + host->clk_act = 1; /* - * Now that the interrupt is disabled make sure the interrupt - * mask bit is cleared on i.MX21. Otherwise we can't read - * the interrupt status bit on this machine. + * Now that we "own" the interrupt make sure the interrupt mask bit is + * cleared on i.MX21. Otherwise we can't read the interrupt status bit + * on this machine. */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 1); + if (host->devtype_data->irqpending_quirk) { + disable_irq_nosync(host->irq); + host->devtype_data->irq_control(host, 1); + } /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1, NULL)) { + if (nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL)) { err = -ENXIO; goto escan; } + /* allocate the right size buffer now */ + devm_kfree(&pdev->dev, (void *)host->data_buf); + host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize, + GFP_KERNEL); + if (!host->data_buf) { + err = -ENOMEM; + goto escan; + } + /* Call preset again, with correct writesize this time */ - host->preset(mtd); + host->devtype_data->preset(mtd); if (mtd->writesize == 2048) - this->ecc.layout = oob_largepage; + this->ecc.layout = host->devtype_data->ecclayout_2k; + else if (mtd->writesize == 4096) + this->ecc.layout = host->devtype_data->ecclayout_4k; + + if (this->ecc.mode == NAND_ECC_HW) { + if (is_imx21_nfc(host) || is_imx27_nfc(host)) + this->ecc.strength = 1; + else + this->ecc.strength = (host->eccsize == 4) ? 4 : 8; + } /* second phase scan */ if (nand_scan_tail(mtd)) { @@ -1194,52 +1568,31 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* Register the partitions */ -#ifdef CONFIG_MTD_PARTITIONS - nr_parts = - parse_mtd_partitions(mtd, part_probes, &host->parts, 0); - if (nr_parts > 0) - add_mtd_partitions(mtd, host->parts, nr_parts); - else if (pdata->parts) - add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); - else -#endif - { - pr_info("Registering %s as whole device\n", mtd->name); - add_mtd_device(mtd); - } + mtd_device_parse_register(mtd, part_probes, + &(struct mtd_part_parser_data){ + .of_node = pdev->dev.of_node, + }, + host->pdata.parts, + host->pdata.nr_parts); platform_set_drvdata(pdev, host); return 0; escan: - free_irq(host->irq, host); -eirq: - if (host->regs_ip) - iounmap(host->regs_ip); - iounmap(host->base); -eres: - clk_put(host->clk); -eclk: - kfree(host); + if (host->clk_act) + clk_disable_unprepare(host->clk); return err; } -static int __devexit mxcnd_remove(struct platform_device *pdev) +static int mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); - clk_put(host->clk); - - platform_set_drvdata(pdev, NULL); - nand_release(&host->mtd); - free_irq(host->irq, host); - if (host->regs_ip) - iounmap(host->regs_ip); - iounmap(host->base); - kfree(host); + if (host->clk_act) + clk_disable_unprepare(host->clk); return 0; } @@ -1247,23 +1600,14 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mxcnd_dt_ids), }, - .remove = __devexit_p(mxcnd_remove), + .id_table = mxcnd_devtype, + .probe = mxcnd_probe, + .remove = mxcnd_remove, }; - -static int __init mxc_nd_init(void) -{ - return platform_driver_probe(&mxcnd_driver, mxcnd_probe); -} - -static void __exit mxc_nd_cleanup(void) -{ - /* Unregister the device structure */ - platform_driver_unregister(&mxcnd_driver); -} - -module_init(mxc_nd_init); -module_exit(mxc_nd_cleanup); +module_platform_driver(mxcnd_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("MXC NAND MTD driver"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index a9c6ce74576..4f3e80c68a2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4,7 +4,6 @@ * Overview: * This is the generic MTD driver for NAND flash devices. It should be * capable of working with almost all NAND chips currently available. - * Basic support for AG-AND chips is provided. * * Additional technical information is available on * http://www.linux-mtd.infradead.org/doc/nand.html @@ -21,9 +20,7 @@ * TODO: * Enable cached programming for 2k page size chips * Check, if mtd->ecctype should be set to MTD_ECC_HW - * if we have HW ecc support. - * The AG-AND chips have nice features for speed improvement, - * which are not supported yet. Read / program 4 pages in one go. + * if we have HW ECC support. * BBT table is not serialized, has to be fixed * * This program is free software; you can redistribute it and/or modify @@ -32,24 +29,25 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/err.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/mm.h> #include <linux/types.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_bch.h> #include <linux/interrupt.h> #include <linux/bitops.h> #include <linux/leds.h> #include <linux/io.h> - -#ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> -#endif /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { @@ -95,8 +93,7 @@ static struct nand_ecclayout nand_oob_128 = { .length = 78} } }; -static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, - int new_state); +static int nand_get_device(struct mtd_info *mtd, int new_state); static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); @@ -114,22 +111,14 @@ static int check_offs_len(struct mtd_info *mtd, int ret = 0; /* Start address must align on block boundary */ - if (ofs & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: unaligned address\n", __func__); ret = -EINVAL; } /* Length must align on block boundary */ - if (len & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", - __func__); - ret = -EINVAL; - } - - /* Do not allow past end of device */ - if (ofs + len > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", - __func__); + if (len & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: length not block aligned\n", __func__); ret = -EINVAL; } @@ -138,17 +127,14 @@ static int check_offs_len(struct mtd_info *mtd, /** * nand_release_device - [GENERIC] release chip - * @mtd: MTD device structure + * @mtd: MTD device structure * - * Deselect, release chip lock and wake up anyone waiting on the device + * Release chip lock and wake up anyone waiting on the device. */ static void nand_release_device(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - /* De-select the NAND device */ - chip->select_chip(mtd, -1); - /* Release the controller and the chip */ spin_lock(&chip->controller->lock); chip->controller->active = NULL; @@ -159,9 +145,9 @@ static void nand_release_device(struct mtd_info *mtd) /** * nand_read_byte - [DEFAULT] read one byte from the chip - * @mtd: MTD device structure + * @mtd: MTD device structure * - * Default read function for 8bit buswith + * Default read function for 8bit buswidth */ static uint8_t nand_read_byte(struct mtd_info *mtd) { @@ -170,11 +156,12 @@ static uint8_t nand_read_byte(struct mtd_info *mtd) } /** - * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip - * @mtd: MTD device structure + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth with endianness conversion. * - * Default read function for 16bit buswith with - * endianess conversion */ static uint8_t nand_read_byte16(struct mtd_info *mtd) { @@ -184,10 +171,9 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd) /** * nand_read_word - [DEFAULT] read one word from the chip - * @mtd: MTD device structure + * @mtd: MTD device structure * - * Default read function for 16bit buswith without - * endianess conversion + * Default read function for 16bit buswidth without endianness conversion. */ static u16 nand_read_word(struct mtd_info *mtd) { @@ -197,8 +183,8 @@ static u16 nand_read_word(struct mtd_info *mtd) /** * nand_select_chip - [DEFAULT] control CE line - * @mtd: MTD device structure - * @chipnr: chipnumber to select, -1 for deselect + * @mtd: MTD device structure + * @chipnr: chipnumber to select, -1 for deselect * * Default select function for 1 chip devices. */ @@ -219,134 +205,127 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) } /** - * nand_write_buf - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write + * nand_write_byte - [DEFAULT] write single byte to chip + * @mtd: MTD device structure + * @byte: value to write * - * Default write function for 8bit buswith + * Default function to write a byte to I/O[7:0] */ -static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - writeb(buf[i], chip->IO_ADDR_W); + chip->write_buf(mtd, &byte, 1); } /** - * nand_read_buf - [DEFAULT] read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read + * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16 + * @mtd: MTD device structure + * @byte: value to write * - * Default read function for 8bit buswith + * Default function to write a byte to I/O[7:0] on a 16-bit wide chip. */ -static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) { - int i; struct nand_chip *chip = mtd->priv; + uint16_t word = byte; - for (i = 0; i < len; i++) - buf[i] = readb(chip->IO_ADDR_R); + /* + * It's not entirely clear what should happen to I/O[15:8] when writing + * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads: + * + * When the host supports a 16-bit bus width, only data is + * transferred at the 16-bit width. All address and command line + * transfers shall use only the lower 8-bits of the data bus. During + * command transfers, the host may place any value on the upper + * 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 + * 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. + */ + chip->write_buf(mtd, (uint8_t *)&word, 2); } /** - * nand_verify_buf - [DEFAULT] Verify chip data against buffer - * @mtd: MTD device structure - * @buf: buffer containing the data to compare - * @len: number of bytes to compare + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write * - * Default verify function for 8bit buswith + * Default write function for 8bit buswidth. */ -static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - if (buf[i] != readb(chip->IO_ADDR_R)) - return -EFAULT; - return 0; + iowrite8_rep(chip->IO_ADDR_W, buf, len); } /** - * nand_write_buf16 - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read * - * Default write function for 16bit buswith + * Default read function for 8bit buswidth. */ -static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; - u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - writew(p[i], chip->IO_ADDR_W); + ioread8_rep(chip->IO_ADDR_R, buf, len); } /** - * nand_read_buf16 - [DEFAULT] read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write * - * Default read function for 16bit buswith + * Default write function for 16bit buswidth. */ -static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - for (i = 0; i < len; i++) - p[i] = readw(chip->IO_ADDR_R); + iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); } /** - * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer - * @mtd: MTD device structure - * @buf: buffer containing the data to compare - * @len: number of bytes to compare + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read * - * Default verify function for 16bit buswith + * Default read function for 16bit buswidth. */ -static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - if (p[i] != readw(chip->IO_ADDR_R)) - return -EFAULT; - return 0; + ioread16_rep(chip->IO_ADDR_R, p, len >> 1); } /** * nand_block_bad - [DEFAULT] Read bad block marker from the chip - * @mtd: MTD device structure - * @ofs: offset from device start - * @getchip: 0, if the chip is already selected + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected * * Check, if the block is bad. */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { - int page, chipnr, res = 0; + int page, chipnr, res = 0, i = 0; struct nand_chip *chip = mtd->priv; u16 bad; - if (chip->options & NAND_BBT_SCANLASTPAGE) + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) ofs += mtd->erasesize - mtd->writesize; page = (int)(ofs >> chip->page_shift) & chip->pagemask; @@ -354,89 +333,131 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) if (getchip) { chipnr = (int)(ofs >> chip->chip_shift); - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); /* Select the NAND device */ chip->select_chip(mtd, chipnr); } - if (chip->options & NAND_BUSWIDTH_16) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, - page); - bad = cpu_to_le16(chip->read_word(mtd)); - if (chip->badblockpos & 0x1) - bad >>= 8; - else - bad &= 0xFF; - } else { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); - bad = chip->read_byte(mtd); - } + do { + if (chip->options & NAND_BUSWIDTH_16) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, + chip->badblockpos & 0xFE, page); + bad = cpu_to_le16(chip->read_word(mtd)); + if (chip->badblockpos & 0x1) + bad >>= 8; + else + bad &= 0xFF; + } else { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, + page); + bad = chip->read_byte(mtd); + } - if (likely(chip->badblockbits == 8)) - res = bad != 0xFF; - else - res = hweight8(bad) < chip->badblockbits; + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + ofs += mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + i++; + } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); - if (getchip) + if (getchip) { + chip->select_chip(mtd, -1); nand_release_device(mtd); + } return res; } /** - * nand_default_block_markbad - [DEFAULT] mark a block bad - * @mtd: MTD device structure - * @ofs: offset from device start + * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker + * @mtd: MTD device structure + * @ofs: offset from device start * - * This is the default implementation, which can be overridden by - * a hardware specific driver. -*/ + * This is the default implementation, which can be overridden by a hardware + * specific driver. It provides the details for writing a bad block marker to a + * block. + */ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; uint8_t buf[2] = { 0, 0 }; - int block, ret, i = 0; + int ret = 0, res, i = 0; - if (chip->options & NAND_BBT_SCANLASTPAGE) + ops.datbuf = NULL; + ops.oobbuf = buf; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } + ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, ofs, &ops); + if (!ret) + ret = res; - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - if (chip->bbt) - chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + i++; + ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); - /* Do we have a flash based bad block table ? */ - if (chip->options & NAND_USE_FLASH_BBT) - ret = nand_update_bbt(mtd, ofs); - else { - nand_get_device(chip, mtd, FL_WRITING); + return ret; +} - /* Write to first two pages and to byte 1 and 6 if necessary. - * If we write to more than one location, the first error - * encountered quits the procedure. We write two bytes per - * location, so we dont have to mess with 16 bit access. - */ - do { - chip->ops.len = chip->ops.ooblen = 2; - chip->ops.datbuf = NULL; - chip->ops.oobbuf = buf; - chip->ops.ooboffs = chip->badblockpos & ~0x01; - - ret = nand_do_write_oob(mtd, ofs, &chip->ops); - - if (!ret && (chip->options & NAND_BBT_SCANBYTE1AND6)) { - chip->ops.ooboffs = NAND_SMALL_BADBLOCK_POS - & ~0x01; - ret = nand_do_write_oob(mtd, ofs, &chip->ops); - } - i++; - ofs += mtd->writesize; - } while (!ret && (chip->options & NAND_BBT_SCAN2NDPAGE) && - i < 2); +/** + * nand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This function performs the generic NAND bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). We only allow the hardware driver to + * specify how to write bad block markers to OOB (chip->block_markbad). + * + * We try operations in the following order: + * (1) erase the affected block, to allow OOB marker to be written cleanly + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + * Note that we retain the first error encountered in (2) or (3), finish the + * procedures, and dump the error in the end. +*/ +static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + int res, ret = 0; + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { + struct erase_info einfo; + + /* 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); + + /* Write bad block marker to OOB */ + nand_get_device(mtd, FL_WRITING); + ret = chip->block_markbad(mtd, ofs); nand_release_device(mtd); } + + /* Mark block bad in BBT */ + if (chip->bbt) { + res = nand_markbad_bbt(mtd, ofs); + if (!ret) + ret = res; + } + if (!ret) mtd->ecc_stats.badblocks++; @@ -445,16 +466,16 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /** * nand_check_wp - [GENERIC] check if the chip is write protected - * @mtd: MTD device structure - * Check, if the device is write protected + * @mtd: MTD device structure * - * The function expects, that the device is already selected + * Check, if the device is write protected. The function expects, that the + * device is already selected. */ static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - /* broken xD cards report WP despite being writable */ + /* Broken xD cards report WP despite being writable */ if (chip->options & NAND_BROKEN_XD) return 0; @@ -465,10 +486,10 @@ static int nand_check_wp(struct mtd_info *mtd) /** * nand_block_checkbad - [GENERIC] Check if a block is marked bad - * @mtd: MTD device structure - * @ofs: offset from device start - * @getchip: 0, if the chip is already selected - * @allowbbt: 1, if its allowed to access the bbt area + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @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. @@ -487,8 +508,8 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, /** * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. - * @mtd: MTD device structure - * @timeo: Timeout + * @mtd: MTD device structure + * @timeo: Timeout * * Helper function for nand_wait_ready used when needing to wait in interrupt * context. @@ -507,21 +528,18 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) } } -/* - * Wait for the ready pin, after a command - * The timeout is catched later. - */ +/* Wait for the ready pin, after a command. The timeout is caught later. */ void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - unsigned long timeo = jiffies + 2; + unsigned long timeo = jiffies + msecs_to_jiffies(20); /* 400ms timeout */ if (in_interrupt() || oops_in_progress) return panic_nand_wait_ready(mtd, 400); led_trigger_event(nand_led_trigger, LED_FULL); - /* wait until command is processed or timeout occures */ + /* Wait until command is processed or timeout occurs */ do { if (chip->dev_ready(mtd)) break; @@ -533,13 +551,13 @@ EXPORT_SYMBOL_GPL(nand_wait_ready); /** * nand_command - [DEFAULT] Send command to NAND device - * @mtd: MTD device structure - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none * - * Send command to NAND device. This function is used for small page - * devices (256/512 Bytes per page) + * Send command to NAND device. This function is used for small page devices + * (512 Bytes per page). */ static void nand_command(struct mtd_info *mtd, unsigned int command, int column, int page_addr) @@ -547,9 +565,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, register struct nand_chip *chip = mtd->priv; int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; - /* - * Write out the command to the device. - */ + /* Write out the command to the device */ if (command == NAND_CMD_SEQIN) { int readcmd; @@ -569,14 +585,13 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, } chip->cmd_ctrl(mtd, command, ctrl); - /* - * Address cycle, when necessary - */ + /* Address cycle, when necessary */ ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); ctrl &= ~NAND_CTRL_CHANGE; @@ -592,8 +607,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* - * program and erase have their own busy handlers - * status and sequential in needs no delay + * Program and erase have their own busy handlers status and sequential + * in needs no delay */ switch (command) { @@ -627,8 +642,10 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, return; } } - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ ndelay(100); nand_wait_ready(mtd); @@ -636,14 +653,14 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, /** * nand_command_lp - [DEFAULT] Send command to NAND large page device - * @mtd: MTD device structure - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none * * Send command to NAND device. This is the version for the new large page - * devices We dont have the separate regions as we have in the small page - * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. + * devices. We don't have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. */ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, int column, int page_addr) @@ -657,8 +674,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, } /* Command latch cycle */ - chip->cmd_ctrl(mtd, command & 0xff, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); if (column != -1 || page_addr != -1) { int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; @@ -666,7 +682,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); ctrl &= ~NAND_CTRL_CHANGE; @@ -685,8 +702,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* - * program and erase have their own busy handlers - * status, sequential in, and deplete1 need no delay + * Program and erase have their own busy handlers status, sequential + * in, and deplete1 need no delay. */ switch (command) { @@ -697,18 +714,6 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_SEQIN: case NAND_CMD_RNDIN: case NAND_CMD_STATUS: - case NAND_CMD_DEPLETE1: - return; - - /* - * read error status commands require only a short delay - */ - case NAND_CMD_STATUS_ERROR: - case NAND_CMD_STATUS_ERROR0: - case NAND_CMD_STATUS_ERROR1: - case NAND_CMD_STATUS_ERROR2: - case NAND_CMD_STATUS_ERROR3: - udelay(chip->chip_delay); return; case NAND_CMD_RESET: @@ -741,7 +746,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, default: /* * If we don't have access to the busy pin, we apply the given - * command delay + * command delay. */ if (!chip->dev_ready) { udelay(chip->chip_delay); @@ -749,8 +754,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, } } - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ ndelay(100); nand_wait_ready(mtd); @@ -758,31 +765,31 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, /** * panic_nand_get_device - [GENERIC] Get chip for selected access - * @chip: the nand chip descriptor - * @mtd: MTD device structure - * @new_state: the state which is requested + * @chip: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested * * Used when in panic, no locks are taken. */ static void panic_nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) { - /* Hardware controller shared among independend devices */ + /* Hardware controller shared among independent devices */ chip->controller->active = chip; chip->state = new_state; } /** * nand_get_device - [GENERIC] Get chip for selected access - * @chip: the nand chip descriptor - * @mtd: MTD device structure - * @new_state: the state which is requested + * @mtd: MTD device structure + * @new_state: the state which is requested * * Get the device and lock it for exclusive access */ static int -nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) +nand_get_device(struct mtd_info *mtd, int new_state) { + struct nand_chip *chip = mtd->priv; spinlock_t *lock = &chip->controller->lock; wait_queue_head_t *wq = &chip->controller->wq; DECLARE_WAITQUEUE(wait, current); @@ -814,10 +821,10 @@ retry: } /** - * panic_nand_wait - [GENERIC] wait until the command is done - * @mtd: MTD device structure - * @chip: NAND chip structure - * @timeo: Timeout + * panic_nand_wait - [GENERIC] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * @timeo: timeout * * Wait for command done. This is a helper function for nand_wait used when * we are in interrupt context. May happen when in panic and trying to write @@ -840,39 +847,34 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_wait - [DEFAULT] wait until the command is done - * @mtd: MTD device structure - * @chip: NAND chip structure + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure * - * Wait for command done. This applies to erase and program only - * Erase can take up to 400ms and program up to 20ms according to - * general NAND and SmartMedia specs + * Wait for command done. This applies to erase and program only. Erase can + * take up to 400ms and program up to 20ms according to general NAND and + * SmartMedia specs. */ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { - unsigned long timeo = jiffies; int status, state = chip->state; - - if (state == FL_ERASING) - timeo += (HZ * 400) / 1000; - else - timeo += (HZ * 20) / 1000; + unsigned long timeo = (state == FL_ERASING ? 400 : 20); led_trigger_event(nand_led_trigger, LED_FULL); - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ + /* + * Apply this short delay always to ensure that we do wait tWB in any + * case on any machine. + */ ndelay(100); - if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) - chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); - else - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); if (in_interrupt() || oops_in_progress) panic_nand_wait(mtd, chip, timeo); else { + timeo = jiffies + msecs_to_jiffies(timeo); while (time_before(jiffies, timeo)) { if (chip->dev_ready) { if (chip->dev_ready(mtd)) @@ -887,21 +889,22 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) led_trigger_event(nand_led_trigger, LED_OFF); status = (int)chip->read_byte(mtd); + /* This can happen if in case of timeout or buggy dev_ready */ + WARN_ON(!(status & NAND_STATUS_READY)); return status; } /** * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks - * * @mtd: mtd info * @ofs: offset to start unlock from * @len: length to unlock - * @invert: when = 0, unlock the range of blocks within the lower and - * upper boundary address - * when = 1, unlock the range of blocks outside the boundaries - * of the lower and upper boundary address + * @invert: when = 0, unlock the range of blocks within the lower and + * upper boundary address + * when = 1, unlock the range of blocks outside the boundaries + * of the lower and upper boundary address * - * return - unlock status + * Returs unlock status. */ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len, int invert) @@ -921,10 +924,9 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, /* Call wait ready function */ status = chip->waitfunc(mtd, chip); - udelay(1000); /* See if device thinks it succeeded */ - if (status & 0x01) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + if (status & NAND_STATUS_FAIL) { + pr_debug("%s: error status = 0x%08x\n", __func__, status); ret = -EIO; } @@ -934,12 +936,11 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, /** * nand_unlock - [REPLACEABLE] unlocks specified locked blocks - * * @mtd: mtd info * @ofs: offset to start unlock from * @len: length to unlock * - * return - unlock status + * Returns unlock status. */ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { @@ -947,7 +948,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) int chipnr; struct nand_chip *chip = mtd->priv; - DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)ofs, len); if (check_offs_len(mtd, ofs, len)) @@ -957,7 +958,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ofs + len == mtd->size) len -= mtd->erasesize; - nand_get_device(chip, mtd, FL_UNLOCKING); + nand_get_device(mtd, FL_UNLOCKING); /* Shift to get chip number */ chipnr = ofs >> chip->chip_shift; @@ -966,7 +967,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + pr_debug("%s: device is write protected!\n", __func__); ret = -EIO; goto out; @@ -975,9 +976,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ret = __nand_unlock(mtd, ofs, len, 0); out: - /* de-select the NAND device */ chip->select_chip(mtd, -1); - nand_release_device(mtd); return ret; @@ -986,18 +985,16 @@ EXPORT_SYMBOL(nand_unlock); /** * nand_lock - [REPLACEABLE] locks all blocks present in the device - * * @mtd: mtd info * @ofs: offset to start unlock from * @len: length to unlock * - * return - lock status - * - * This feature is not supported in many NAND parts. 'Micron' NAND parts - * do have this feature, but it allows only to lock all blocks, not for - * specified range for block. + * This feature is not supported in many NAND parts. 'Micron' NAND parts do + * have this feature, but it allows only to lock all blocks, not for specified + * range for block. Implementing 'lock' feature by making use of 'unlock', for + * now. * - * Implementing 'lock' feature by making use of 'unlock', for now. + * Returns lock status. */ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { @@ -1005,13 +1002,13 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) int chipnr, status, page; struct nand_chip *chip = mtd->priv; - DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)ofs, len); if (check_offs_len(mtd, ofs, len)) ret = -EINVAL; - nand_get_device(chip, mtd, FL_LOCKING); + nand_get_device(mtd, FL_LOCKING); /* Shift to get chip number */ chipnr = ofs >> chip->chip_shift; @@ -1020,7 +1017,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + pr_debug("%s: device is write protected!\n", __func__); status = MTD_ERASE_FAILED; ret = -EIO; @@ -1033,10 +1030,9 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) /* Call wait ready function */ status = chip->waitfunc(mtd, chip); - udelay(1000); /* See if device thinks it succeeded */ - if (status & 0x01) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + if (status & NAND_STATUS_FAIL) { + pr_debug("%s: error status = 0x%08x\n", __func__, status); ret = -EIO; goto out; @@ -1045,9 +1041,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ret = __nand_unlock(mtd, ofs, len, 0x1); out: - /* de-select the NAND device */ chip->select_chip(mtd, -1); - nand_release_device(mtd); return ret; @@ -1055,34 +1049,37 @@ out: EXPORT_SYMBOL(nand_lock); /** - * nand_read_page_raw - [Intern] read raw page data without ecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @page: page number to read + * nand_read_page_raw - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read * - * Not for syndrome calculating ecc controllers, which use a special oob layout + * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } /** - * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @page: page number to read + * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read * * We need a special oob layout and handling even when OOB isn't used. */ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1115,14 +1112,15 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, } /** - * nand_read_page_swecc - [REPLACABLE] software ecc based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @page: page number to read + * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1131,8 +1129,9 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; - chip->ecc.read_page_raw(mtd, chip, buf, page); + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -1147,24 +1146,28 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** - * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @data_offs: offset of requested data within the page - * @readlen: data length - * @bufpoi: buffer to store read data + * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data + * @page: page number to read */ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, + int page) { int start_step, end_step, num_steps; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -1172,14 +1175,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; - int index = 0; + int index; + unsigned int max_bitflips = 0; - /* Column address wihin the page aligned to ECC size (256bytes). */ + /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; end_step = (data_offs + readlen - 1) / chip->ecc.size; num_steps = end_step - start_step + 1; + index = start_step * chip->ecc.bytes; - /* Data size aligned to ECC ecc.size*/ + /* Data size aligned to ECC ecc.size */ datafrag_len = num_steps * chip->ecc.size; eccfrag_len = num_steps * chip->ecc.bytes; @@ -1191,16 +1196,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, p = bufpoi + data_col_addr; chip->read_buf(mtd, p, datafrag_len); - /* Calculate ECC */ + /* Calculate ECC */ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); - /* The performance is faster if to position offsets - according to ecc.pos. Let make sure here that - there are no gaps in ecc positions */ + /* + * The performance is faster if we position offsets according to + * ecc.pos. Let's make sure that there are no gaps in ECC positions. + */ for (i = 0; i < eccfrag_len - 1; i++) { - if (eccpos[i + start_step * chip->ecc.bytes] + 1 != - eccpos[i + start_step * chip->ecc.bytes + 1]) { + if (eccpos[i + index] + 1 != eccpos[i + index + 1]) { gaps = 1; break; } @@ -1209,10 +1214,10 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); } else { - /* send the command to read the particular ecc bytes */ - /* take care about buswidth alignment in read_buf */ - index = start_step * chip->ecc.bytes; - + /* + * Send the command to read the particular ECC bytes take care + * about buswidth alignment in read_buf. + */ aligned_pos = eccpos[index] & ~(busw - 1); aligned_len = eccfrag_len; if (eccpos[index] & (busw - 1)) @@ -1234,25 +1239,28 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** - * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @page: page number to read + * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read * - * Not for syndrome calculating ecc controllers which need a special oob layout + * Not for syndrome calculating ECC controllers which need a special oob layout. */ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1261,6 +1269,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -1279,30 +1288,32 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** - * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @page: page number to read + * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read * - * Hardware ECC for large page chips, require OOB to be read first. - * For this ECC mode, the write_page method is re-used from ECC_HW. - * These methods read/write ECC from the OOB area, unlike the - * ECC_HW_SYNDROME support with multiple ECC steps, follows the - * "infix ECC" scheme and reads/writes ECC from the data area, by - * overwriting the NAND manufacturer bad block markings. + * Hardware ECC for large page chips, require OOB to be read first. For this + * ECC mode, the write_page method is re-used from ECC_HW. These methods + * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with + * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from + * the data area, by overwriting the NAND manufacturer bad block markings. */ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1311,6 +1322,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; + unsigned int max_bitflips = 0; /* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); @@ -1328,32 +1340,36 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** - * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @page: page number to read + * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. */ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -1370,10 +1386,12 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } oob += eccbytes; @@ -1388,33 +1406,33 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (i) chip->read_buf(mtd, oob, i); - return 0; + return max_bitflips; } /** - * nand_transfer_oob - [Internal] Transfer oob to client buffer - * @chip: nand chip structure - * @oob: oob destination address - * @ops: oob ops structure - * @len: size of oob to transfer + * nand_transfer_oob - [INTERN] Transfer oob to client buffer + * @chip: nand chip structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: size of oob to transfer */ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { switch (ops->mode) { - case MTD_OOB_PLACE: - case MTD_OOB_RAW: + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: memcpy(oob, chip->oob_poi + ops->ooboffs, len); return oob + len; - case MTD_OOB_AUTO: { + case MTD_OPS_AUTO_OOB: { struct nand_oobfree *free = chip->ecc.layout->oobfree; uint32_t boffs = 0, roffs = ops->ooboffs; size_t bytes = 0; for (; free->length && len; free++, len -= bytes) { - /* Read request not from offset 0 ? */ + /* Read request not from offset 0? */ if (unlikely(roffs)) { if (roffs >= free->length) { roffs -= free->length; @@ -1440,31 +1458,53 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, } /** - * nand_do_read_ops - [Internal] Read data with ECC + * nand_setup_read_retry - [INTERN] Set the READ RETRY mode + * @mtd: MTD device structure + * @retry_mode: the retry mode to use * - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob ops structure + * Some vendors supply a special command to shift the Vt threshold, to be used + * when there are too many bitflips in a page (i.e., ECC error). After setting + * a new threshold, the host should retry reading the page. + */ +static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd->priv; + + pr_debug("setting READ RETRY mode %d\n", retry_mode); + + if (retry_mode >= chip->read_retries) + return -EINVAL; + + if (!chip->setup_read_retry) + return -EOPNOTSUPP; + + return chip->setup_read_retry(mtd, retry_mode); +} + +/** + * nand_do_read_ops - [INTERN] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob ops structure * * Internal function. Called with chip held. */ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int chipnr, page, realpage, col, bytes, aligned; + int chipnr, page, realpage, col, bytes, aligned, oob_required; struct nand_chip *chip = mtd->priv; - struct mtd_ecc_stats stats; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int sndcmd = 1; int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; - uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ? + uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize; uint8_t *bufpoi, *oob, *buf; - - stats = mtd->ecc_stats; + int use_bufpoi; + unsigned int max_bitflips = 0; + int retry_mode = 0; + bool ecc_fail = false; chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1476,45 +1516,72 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = ops->datbuf; oob = ops->oobbuf; + oob_required = oob ? 1 : 0; while (1) { + unsigned int ecc_failures = mtd->ecc_stats.failed; + bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); - /* Is the current page in the buffer ? */ + if (!aligned) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !virt_addr_valid(buf); + else + use_bufpoi = 0; + + /* Is the current page in the buffer? */ if (realpage != chip->pagebuf || oob) { - bufpoi = aligned ? buf : chip->buffers->databuf; + bufpoi = use_bufpoi ? chip->buffers->databuf : buf; - if (likely(sndcmd)) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - sndcmd = 0; - } + if (use_bufpoi && aligned) + pr_debug("%s: using read bounce buffer for buf@%p\n", + __func__, buf); - /* Now read the page into the buffer */ - if (unlikely(ops->mode == MTD_OOB_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, - bufpoi, page); - else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) +read_retry: + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ + if (unlikely(ops->mode == MTD_OPS_RAW)) + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, + oob_required, + page); + else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && + !oob) ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi); + col, bytes, bufpoi, + page); else ret = chip->ecc.read_page(mtd, chip, bufpoi, - page); - if (ret < 0) + oob_required, page); + if (ret < 0) { + if (use_bufpoi) + /* Invalidate page cache */ + chip->pagebuf = -1; break; + } + + max_bitflips = max_t(unsigned int, max_bitflips, ret); /* Transfer not aligned data */ - if (!aligned) { - if (!NAND_SUBPAGE_READ(chip) && !oob && - !(mtd->ecc_stats.failed - stats.failed)) + if (use_bufpoi) { + if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && + !(mtd->ecc_stats.failed - ecc_failures) && + (ops->mode != MTD_OPS_RAW)) { chip->pagebuf = realpage; + chip->pagebuf_bitflips = ret; + } else { + /* Invalidate page cache */ + chip->pagebuf = -1; + } memcpy(buf, chip->buffers->databuf + col, bytes); } - buf += bytes; - if (unlikely(oob)) { - int toread = min(oobreadlen, max_oobsize); if (toread) { @@ -1524,30 +1591,53 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } } - if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do - * this before the AUTOINCR check, so no - * problems arise if a chip which does auto - * increment is marked as NOAUTOINCR by the - * board driver. - */ + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else nand_wait_ready(mtd); } + + if (mtd->ecc_stats.failed - ecc_failures) { + if (retry_mode + 1 < chip->read_retries) { + retry_mode++; + ret = nand_setup_read_retry(mtd, + retry_mode); + if (ret < 0) + break; + + /* Reset failures; retry */ + mtd->ecc_stats.failed = ecc_failures; + goto read_retry; + } else { + /* No more retry modes; real failure */ + ecc_fail = true; + } + } + + buf += bytes; } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); } readlen -= bytes; + /* Reset to retry mode 0 */ + if (retry_mode) { + ret = nand_setup_read_retry(mtd, 0); + if (ret < 0) + break; + retry_mode = 0; + } + if (!readlen) break; - /* For subsequent reads align to page boundary. */ + /* For subsequent reads align to page boundary */ col = 0; /* Increment page address */ realpage++; @@ -1559,92 +1649,72 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* Check, if the chip supports auto page increment - * or if we have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } + chip->select_chip(mtd, -1); ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; - if (ret) + if (ret < 0) return ret; - if (mtd->ecc_stats.failed - stats.failed) + if (ecc_fail) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** - * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc - * @mtd: MTD device structure - * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data + * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data * - * Get hold of the chip and call nand_do_read + * Get hold of the chip and call nand_do_read. */ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { - struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) - return -EINVAL; - if (!len) - return 0; - - nand_get_device(chip, mtd, FL_READING); - - chip->ops.len = len; - chip->ops.datbuf = buf; - chip->ops.oobbuf = NULL; - - ret = nand_do_read_ops(mtd, from, &chip->ops); - - *retlen = chip->ops.retlen; - + nand_get_device(mtd, FL_READING); + ops.len = len; + ops.datbuf = buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_read_ops(mtd, from, &ops); + *retlen = ops.retlen; nand_release_device(mtd); - return ret; } /** - * nand_read_oob_std - [REPLACABLE] the most common OOB data read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - * @sndcmd: flag whether to issue read command or not + * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read */ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /** - * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC + * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC * with syndromes - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - * @sndcmd: flag whether to issue read command or not + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read */ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; @@ -1671,14 +1741,14 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (length > 0) chip->read_buf(mtd, bufpoi, length); - return 1; + return 0; } /** - * nand_write_oob_std - [REPLACABLE] the most common OOB data write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write + * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write */ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) @@ -1698,11 +1768,11 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC - * with syndrome - only for large page flash ! - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write + * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write */ static int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) @@ -1757,34 +1827,37 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, } /** - * nand_do_read_oob - [Intern] NAND read out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operations description structure + * nand_do_read_oob - [INTERN] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operations description structure * - * NAND read out-of-band data from the spare area + * NAND read out-of-band data from the spare area. */ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int page, realpage, chipnr, sndcmd = 1; + int page, realpage, chipnr; struct nand_chip *chip = mtd->priv; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + struct mtd_ecc_stats stats; int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; + int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + pr_debug("%s: from = 0x%08Lx, len = %i\n", __func__, (unsigned long long)from, readlen); - if (ops->mode == MTD_OOB_AUTO) + stats = mtd->ecc_stats; + + if (ops->mode == MTD_OPS_AUTO_OOB) len = chip->ecc.layout->oobavail; else len = mtd->oobsize; if (unlikely(ops->ooboffs >= len)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " - "outside oob\n", __func__); + pr_debug("%s: attempt to start read outside oob\n", + __func__); return -EINVAL; } @@ -1792,8 +1865,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, if (unlikely(from >= mtd->size || ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - (from >> chip->page_shift)) * len)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " - "of device\n", __func__); + pr_debug("%s: attempt to read beyond end of device\n", + __func__); return -EINVAL; } @@ -1805,18 +1878,19 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, page = realpage & chip->pagemask; while (1) { - sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); + if (ops->mode == MTD_OPS_RAW) + ret = chip->ecc.read_oob_raw(mtd, chip, page); + else + ret = chip->ecc.read_oob(mtd, chip, page); + + if (ret < 0) + break; len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); - if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do this - * before the AUTOINCR check, so no problems arise if a - * chip which does auto increment is marked as - * NOAUTOINCR by the board driver. - */ + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else @@ -1837,47 +1911,48 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* Check, if the chip supports auto page increment - * or if we have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } + chip->select_chip(mtd, -1); - ops->oobretlen = ops->ooblen; - return 0; + ops->oobretlen = ops->ooblen - readlen; + + if (ret < 0) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } /** * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operation description structure + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure * - * NAND read data and/or out-of-band data + * NAND read data and/or out-of-band data. */ static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; /* Do not allow reads past end of device */ if (ops->datbuf && (from + ops->len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " - "beyond end of device\n", __func__); + pr_debug("%s: attempt to read beyond end of device\n", + __func__); return -EINVAL; } - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); switch (ops->mode) { - case MTD_OOB_PLACE: - case MTD_OOB_AUTO: - case MTD_OOB_RAW: + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: break; default: @@ -1896,31 +1971,36 @@ out: /** - * nand_write_page_raw - [Intern] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer + * nand_write_page_raw - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * - * Not for syndrome calculating ecc controllers, which use a special oob layout + * Not for syndrome calculating ECC controllers, which use a special oob layout. */ -static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) +static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; } /** - * nand_write_page_raw_syndrome - [Intern] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer + * nand_write_page_raw_syndrome - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * We need a special oob layout and handling even when ECC isn't checked. */ -static void nand_write_page_raw_syndrome(struct mtd_info *mtd, +static int nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1936,7 +2016,7 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, oob += chip->ecc.prepad; } - chip->read_buf(mtd, oob, eccbytes); + chip->write_buf(mtd, oob, eccbytes); oob += eccbytes; if (chip->ecc.postpad) { @@ -1948,15 +2028,18 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, size = mtd->oobsize - (oob - chip->oob_poi); if (size) chip->write_buf(mtd, oob, size); + + return 0; } /** - * nand_write_page_swecc - [REPLACABLE] software ecc based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer + * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ -static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) +static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1965,24 +2048,25 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *p = buf; uint32_t *eccpos = chip->ecc.layout->eccpos; - /* Software ecc calculation */ + /* Software ECC calculation */ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->ecc.write_page_raw(mtd, chip, buf); + return chip->ecc.write_page_raw(mtd, chip, buf, 1); } /** - * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer + * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ -static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) +static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2001,19 +2085,85 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, chip->oob_poi[eccpos[i]] = ecc_calc[i]; chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; } + /** - * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write + * nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write * @mtd: mtd info structure * @chip: nand chip info structure + * @offset: column address of subpage within the page + * @data_len: data length * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int nand_write_subpage_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint32_t offset, + uint32_t data_len, const uint8_t *buf, + int oob_required) +{ + uint8_t *oob_buf = chip->oob_poi; + uint8_t *ecc_calc = chip->buffers->ecccalc; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int ecc_steps = chip->ecc.steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t start_step = offset / ecc_size; + uint32_t end_step = (offset + data_len - 1) / ecc_size; + int oob_bytes = mtd->oobsize / ecc_steps; + int step, i; + + for (step = 0; step < ecc_steps; step++) { + /* configure controller for WRITE access */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* write data (untouched subpages already masked by 0xFF) */ + chip->write_buf(mtd, buf, ecc_size); + + /* mask ECC of un-touched subpages by padding 0xFF */ + if ((step < start_step) || (step > end_step)) + memset(ecc_calc, 0xff, ecc_bytes); + else + chip->ecc.calculate(mtd, buf, ecc_calc); + + /* mask OOB of un-touched subpages by padding 0xFF */ + /* if oob_required, preserve OOB metadata of written subpage */ + if (!oob_required || (step < start_step) || (step > end_step)) + memset(oob_buf, 0xff, oob_bytes); + + buf += ecc_size; + ecc_calc += ecc_bytes; + oob_buf += oob_bytes; + } + + /* copy calculated ECC for whole page to chip->buffer->oob */ + /* this include masked-value(0xFF) for unwritten subpages */ + ecc_calc = chip->buffers->ecccalc; + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + /* write OOB buffer to NAND device */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + + +/** + * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. */ -static void nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) +static int nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2045,42 +2195,61 @@ static void nand_write_page_syndrome(struct mtd_info *mtd, i = mtd->oobsize - (oob - chip->oob_poi); if (i) chip->write_buf(mtd, oob, i); + + return 0; } /** * nand_write_page - [REPLACEABLE] write one page - * @mtd: MTD device structure - * @chip: NAND chip descriptor - * @buf: the data to write - * @page: page number to write - * @cached: cached programming - * @raw: use _raw version of write_page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @offset: address offset within the page + * @data_len: length of actual data to be written + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * @cached: cached programming + * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw) { - int status; + int status, subpage; + + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + chip->ecc.write_subpage) + subpage = offset || (data_len < mtd->writesize); + else + subpage = 0; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + status = chip->ecc.write_page_raw(mtd, chip, buf, + oob_required); + else if (subpage) + status = chip->ecc.write_subpage(mtd, chip, offset, data_len, + buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + status = chip->ecc.write_page(mtd, chip, buf, oob_required); + + if (status < 0) + return status; /* - * Cached progamming disabled for now, Not sure if its worth the - * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + * Cached progamming disabled for now. Not sure if it's worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s). */ cached = 0; - if (!cached || !(chip->options & NAND_CACHEPRG)) { + if (!cached || !NAND_HAS_CACHEPROG(chip)) { chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are - * available + * available. */ if ((status & NAND_STATUS_FAIL) && (chip->errstat)) status = chip->errstat(mtd, chip, FL_WRITING, status, @@ -2093,40 +2262,41 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, status = chip->waitfunc(mtd, chip); } -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - if (chip->verify_buf(mtd, buf, mtd->writesize)) - return -EIO; -#endif return 0; } /** - * nand_fill_oob - [Internal] Transfer client buffer to oob - * @chip: nand chip structure - * @oob: oob data buffer - * @len: oob data write length - * @ops: oob ops structure + * nand_fill_oob - [INTERN] Transfer client buffer to oob + * @mtd: MTD device structure + * @oob: oob data buffer + * @len: oob data write length + * @ops: oob ops structure */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, - struct mtd_oob_ops *ops) +static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) { + struct nand_chip *chip = mtd->priv; + + /* + * Initialise to all 0xFF, to avoid the possibility of left over OOB + * data from a previous OOB read. + */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + switch (ops->mode) { - case MTD_OOB_PLACE: - case MTD_OOB_RAW: + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: memcpy(chip->oob_poi + ops->ooboffs, oob, len); return oob + len; - case MTD_OOB_AUTO: { + case MTD_OPS_AUTO_OOB: { struct nand_oobfree *free = chip->ecc.layout->oobfree; uint32_t boffs = 0, woffs = ops->ooboffs; size_t bytes = 0; for (; free->length && len; free++, len -= bytes) { - /* Write request not from offset 0 ? */ + /* Write request not from offset 0? */ if (unlikely(woffs)) { if (woffs >= free->length) { woffs -= free->length; @@ -2154,12 +2324,12 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, #define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) /** - * nand_do_write_ops - [Internal] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operations description structure + * nand_do_write_ops - [INTERN] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure * - * NAND write with ECC + * NAND write with ECC. */ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) @@ -2169,36 +2339,35 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint32_t writelen = ops->len; uint32_t oobwritelen = ops->ooblen; - uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ? + uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize; uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; - int ret, subpage; + int ret; + int oob_required = oob ? 1 : 0; ops->retlen = 0; if (!writelen) return 0; - /* reject writes, which are not page aligned */ + /* Reject writes, which are not page aligned */ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { - printk(KERN_NOTICE "%s: Attempt to write not " - "page aligned data\n", __func__); + pr_notice("%s: attempt to write non page aligned data\n", + __func__); return -EINVAL; } column = to & (mtd->writesize - 1); - subpage = column || (writelen & (mtd->writesize - 1)); - - if (subpage && oob) - return -EINVAL; chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) - return -EIO; + if (nand_check_wp(mtd)) { + ret = -EIO; + goto err_out; + } realpage = (int)(to >> chip->page_shift); page = realpage & chip->pagemask; @@ -2209,23 +2378,33 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, (chip->pagebuf << chip->page_shift) < (to + ops->len)) chip->pagebuf = -1; - /* If we're not given explicit OOB data, let it be 0xFF */ - if (likely(!oob)) - memset(chip->oob_poi, 0xff, mtd->oobsize); - /* Don't allow multipage oob writes with offset */ - if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) - return -EINVAL; + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { + ret = -EINVAL; + goto err_out; + } while (1) { int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf; + int use_bufpoi; + int part_pagewr = (column || writelen < (mtd->writesize - 1)); + + if (part_pagewr) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !virt_addr_valid(buf); + else + use_bufpoi = 0; - /* Partial page write ? */ - if (unlikely(column || writelen < (mtd->writesize - 1))) { + /* Partial page write?, or need to use bounce buffer */ + if (use_bufpoi) { + pr_debug("%s: using write bounce buffer for buf@%p\n", + __func__, buf); cached = 0; - bytes = min_t(int, bytes - column, (int) writelen); + if (part_pagewr) + bytes = min_t(int, bytes - column, writelen); chip->pagebuf = -1; memset(chip->buffers->databuf, 0xff, mtd->writesize); memcpy(&chip->buffers->databuf[column], buf, bytes); @@ -2234,12 +2413,15 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (unlikely(oob)) { size_t len = min(oobwritelen, oobmaxlen); - oob = nand_fill_oob(chip, oob, len, ops); + oob = nand_fill_oob(mtd, oob, len, ops); oobwritelen -= len; + } else { + /* We still need to erase leftover OOB data */ + memset(chip->oob_poi, 0xff, mtd->oobsize); } - - ret = chip->write_page(mtd, chip, wbuf, page, cached, - (ops->mode == MTD_OOB_RAW)); + ret = chip->write_page(mtd, chip, column, bytes, wbuf, + oob_required, page, cached, + (ops->mode == MTD_OPS_RAW)); if (ret) break; @@ -2263,16 +2445,19 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ops->retlen = ops->len - writelen; if (unlikely(oob)) ops->oobretlen = ops->ooblen; + +err_out: + chip->select_chip(mtd, -1); return ret; } /** * panic_nand_write - [MTD Interface] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write * * NAND write with ECC. Used when performing writes in interrupt context, this * may for example be called by mtdoops when writing an oops while in panic. @@ -2281,74 +2466,60 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if ((to + len) > mtd->size) - return -EINVAL; - if (!len) - return 0; - - /* Wait for the device to get ready. */ + /* Wait for the device to get ready */ panic_nand_wait(mtd, chip, 400); - /* Grab the device. */ + /* Grab the device */ panic_nand_get_device(chip, mtd, FL_WRITING); - chip->ops.len = len; - chip->ops.datbuf = (uint8_t *)buf; - chip->ops.oobbuf = NULL; + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; - ret = nand_do_write_ops(mtd, to, &chip->ops); + ret = nand_do_write_ops(mtd, to, &ops); - *retlen = chip->ops.retlen; + *retlen = ops.retlen; return ret; } /** * nand_write - [MTD Interface] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write * - * NAND write with ECC + * NAND write with ECC. */ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { - struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if ((to + len) > mtd->size) - return -EINVAL; - if (!len) - return 0; - - nand_get_device(chip, mtd, FL_WRITING); - - chip->ops.len = len; - chip->ops.datbuf = (uint8_t *)buf; - chip->ops.oobbuf = NULL; - - ret = nand_do_write_ops(mtd, to, &chip->ops); - - *retlen = chip->ops.retlen; - + nand_get_device(mtd, FL_WRITING); + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_write_ops(mtd, to, &ops); + *retlen = ops.retlen; nand_release_device(mtd); - return ret; } /** * nand_do_write_oob - [MTD Interface] NAND write out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure * - * NAND write out-of-band + * NAND write out-of-band. */ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) @@ -2356,34 +2527,34 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; - DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, (int)ops->ooblen); - if (ops->mode == MTD_OOB_AUTO) + if (ops->mode == MTD_OPS_AUTO_OOB) len = chip->ecc.layout->oobavail; else len = mtd->oobsize; /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write " - "past end of page\n", __func__); + pr_debug("%s: attempt to write past end of page\n", + __func__); return -EINVAL; } if (unlikely(ops->ooboffs >= len)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start " - "write outside oob\n", __func__); + pr_debug("%s: attempt to start write outside oob\n", + __func__); return -EINVAL; } - /* Do not allow reads past end of device */ + /* Do not allow write past end of device */ if (unlikely(to >= mtd->size || ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - (to >> chip->page_shift)) * len)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " - "end of device\n", __func__); + pr_debug("%s: attempt to write beyond end of device\n", + __func__); return -EINVAL; } @@ -2402,17 +2573,23 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) + if (nand_check_wp(mtd)) { + chip->select_chip(mtd, -1); return -EROFS; + } /* Invalidate the page cache, if we write to the cached page */ if (page == chip->pagebuf) chip->pagebuf = -1; - memset(chip->oob_poi, 0xff, mtd->oobsize); - nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); - status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); - memset(chip->oob_poi, 0xff, mtd->oobsize); + nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); + + if (ops->mode == MTD_OPS_RAW) + status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); + else + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + + chip->select_chip(mtd, -1); if (status) return status; @@ -2424,31 +2601,30 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, /** * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure */ static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " - "end of device\n", __func__); + pr_debug("%s: attempt to write beyond end of device\n", + __func__); return -EINVAL; } - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(mtd, FL_WRITING); switch (ops->mode) { - case MTD_OOB_PLACE: - case MTD_OOB_AUTO: - case MTD_OOB_RAW: + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: break; default: @@ -2466,80 +2642,58 @@ out: } /** - * single_erease_cmd - [GENERIC] NAND standard block erase command function - * @mtd: MTD device structure - * @page: the page address of the block which will be erased + * single_erase - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased * - * Standard erase command for NAND chips + * Standard erase command for NAND chips. Returns NAND status. */ -static void single_erase_cmd(struct mtd_info *mtd, int page) +static int single_erase(struct mtd_info *mtd, int page) { struct nand_chip *chip = mtd->priv; /* Send commands to erase a block */ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); -} -/** - * multi_erease_cmd - [GENERIC] AND specific block erase command function - * @mtd: MTD device structure - * @page: the page address of the block which will be erased - * - * AND multi block erase command function - * Erase 4 consecutive blocks - */ -static void multi_erase_cmd(struct mtd_info *mtd, int page) -{ - struct nand_chip *chip = mtd->priv; - /* Send commands to erase a block */ - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + return chip->waitfunc(mtd, chip); } /** * nand_erase - [MTD Interface] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction + * @mtd: MTD device structure + * @instr: erase instruction * - * Erase one ore more blocks + * Erase one ore more blocks. */ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) { return nand_erase_nand(mtd, instr, 0); } -#define BBT_PAGE_MASK 0xffffff3f /** - * nand_erase_nand - [Internal] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction - * @allowbbt: allow erasing the bbt area + * nand_erase_nand - [INTERN] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area * - * Erase one ore more blocks + * Erase one ore more blocks. */ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt) { int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; - loff_t rewrite_bbt[NAND_MAX_CHIPS] = {0}; - unsigned int bbt_masked_page = 0xffffffff; loff_t len; - DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", - __func__, (unsigned long long)instr->addr, - (unsigned long long)instr->len); + pr_debug("%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)instr->addr, + (unsigned long long)instr->len); if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; - instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_ERASING); + nand_get_device(mtd, FL_ERASING); /* Shift to get first page */ page = (int)(instr->addr >> chip->page_shift); @@ -2553,49 +2707,36 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", - __func__); + pr_debug("%s: device is write protected!\n", + __func__); instr->state = MTD_ERASE_FAILED; goto erase_exit; } - /* - * If BBT requires refresh, set the BBT page mask to see if the BBT - * should be rewritten. Otherwise the mask is set to 0xffffffff which - * can not be matched. This is also done when the bbt is actually - * erased to avoid recusrsive updates - */ - if (chip->options & BBT_AUTO_REFRESH && !allowbbt) - bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; - /* Loop through the pages */ len = instr->len; instr->state = MTD_ERASING; while (len) { - /* - * heck if we have a bad block, we do not erase bad blocks ! - */ + /* Check if we have a bad block, we do not erase bad blocks! */ if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { - printk(KERN_WARNING "%s: attempt to erase a bad block " - "at page 0x%08x\n", __func__, page); + pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", + __func__, page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* * Invalidate the page cache, if we erase the block which - * contains the current cached page + * contains the current cached page. */ if (page <= chip->pagebuf && chip->pagebuf < (page + pages_per_block)) chip->pagebuf = -1; - chip->erase_cmd(mtd, page & chip->pagemask); - - status = chip->waitfunc(mtd, chip); + status = chip->erase(mtd, page & chip->pagemask); /* * See if operation failed and additional status checks are @@ -2607,25 +2748,16 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* See if block erase succeeded */ if (status & NAND_STATUS_FAIL) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, " - "page 0x%08x\n", __func__, page); + pr_debug("%s: failed erase, page 0x%08x\n", + __func__, page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = ((loff_t)page << chip->page_shift); goto erase_exit; } - /* - * If BBT requires refresh, set the BBT rewrite flag to the - * page being erased - */ - if (bbt_masked_page != 0xffffffff && - (page & BBT_PAGE_MASK) == bbt_masked_page) - rewrite_bbt[chipnr] = - ((loff_t)page << chip->page_shift); - /* Increment page address and decrement length */ - len -= (1 << chip->phys_erase_shift); + len -= (1ULL << chip->phys_erase_shift); page += pages_per_block; /* Check, if we cross a chip boundary */ @@ -2633,15 +2765,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, chipnr++; chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); - - /* - * If BBT requires refresh and BBT-PERCHIP, set the BBT - * page mask to see if this BBT should be rewritten - */ - if (bbt_masked_page != 0xffffffff && - (chip->bbt_td->options & NAND_BBT_PERCHIP)) - bbt_masked_page = chip->bbt_td->pages[chipnr] & - BBT_PAGE_MASK; } } instr->state = MTD_ERASE_DONE; @@ -2651,100 +2774,129 @@ 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); - /* - * If BBT requires refresh and erase was successful, rewrite any - * selected bad block tables - */ - if (bbt_masked_page == 0xffffffff || ret) - return ret; - - for (chipnr = 0; chipnr < chip->numchips; chipnr++) { - if (!rewrite_bbt[chipnr]) - continue; - /* update the BBT for chip */ - DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " - "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, - rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); - nand_update_bbt(mtd, rewrite_bbt[chipnr]); - } - /* Return more or less happy */ return ret; } /** * nand_sync - [MTD Interface] sync - * @mtd: MTD device structure + * @mtd: MTD device structure * - * Sync is actually a wait for chip ready function + * Sync is actually a wait for chip ready function. */ static void nand_sync(struct mtd_info *mtd) { - struct nand_chip *chip = mtd->priv; - - DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); + pr_debug("%s: called\n", __func__); /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_SYNCING); + nand_get_device(mtd, FL_SYNCING); /* Release it and go back */ nand_release_device(mtd); } /** * nand_block_isbad - [MTD Interface] Check if block at offset is bad - * @mtd: MTD device structure - * @offs: offset relative to mtd start + * @mtd: MTD device structure + * @offs: offset relative to mtd start */ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) { - /* Check for invalid offset */ - if (offs > mtd->size) - return -EINVAL; - return nand_block_checkbad(mtd, offs, 1, 0); } /** * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad - * @mtd: MTD device structure - * @ofs: offset relative to mtd start + * @mtd: MTD device structure + * @ofs: offset relative to mtd start */ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *chip = mtd->priv; int ret; ret = nand_block_isbad(mtd, ofs); if (ret) { - /* If it was bad already, return success and do nothing. */ + /* If it was bad already, return success and do nothing */ if (ret > 0) return 0; return ret; } - return chip->block_markbad(mtd, ofs); + return nand_block_markbad_lowlevel(mtd, ofs); +} + +/** + * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand + * @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) +{ + int status; + int i; + + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, subfeature_param[i]); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + return 0; +} + +/** + * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand + * @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) +{ + int i; + + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + /* clear the sub feature parameters */ + memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN); + + chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + *subfeature_param++ = chip->read_byte(mtd); + return 0; } /** * nand_suspend - [MTD Interface] Suspend the NAND flash - * @mtd: MTD device structure + * @mtd: MTD device structure */ static int nand_suspend(struct mtd_info *mtd) { - struct nand_chip *chip = mtd->priv; - - return nand_get_device(chip, mtd, FL_PM_SUSPENDED); + return nand_get_device(mtd, FL_PM_SUSPENDED); } /** * nand_resume - [MTD Interface] Resume the NAND flash - * @mtd: MTD device structure + * @mtd: MTD device structure */ static void nand_resume(struct mtd_info *mtd) { @@ -2753,13 +2905,11 @@ static void nand_resume(struct mtd_info *mtd) if (chip->state == FL_PM_SUSPENDED) nand_release_device(mtd); else - printk(KERN_ERR "%s called for a chip which is not " - "in suspended state\n", __func__); + pr_err("%s called for a chip which is not in suspended state\n", + __func__); } -/* - * Set default functions - */ +/* Set default functions */ static void nand_set_defaults(struct nand_chip *chip, int busw) { /* check for proper chip_delay setup, set 20us if not */ @@ -2776,7 +2926,15 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->select_chip) chip->select_chip = nand_select_chip; - if (!chip->read_byte) + + /* 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 called twice, pointers that depend on busw may need to be reset */ + if (!chip->read_byte || chip->read_byte == nand_read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; @@ -2784,12 +2942,12 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->block_bad = nand_block_bad; if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf) + if (!chip->write_buf || chip->write_buf == nand_write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!chip->read_buf) + if (!chip->write_byte || chip->write_byte == nand_write_byte) + chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; + if (!chip->read_buf || chip->read_buf == nand_read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; - if (!chip->verify_buf) - chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; @@ -2801,23 +2959,21 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) } -/* - * sanitize ONFI strings so we can safely print them - */ +/* Sanitize ONFI strings so we can safely print them */ static void sanitize_string(uint8_t *s, size_t len) { ssize_t i; - /* null terminate */ + /* Null terminate */ s[len - 1] = 0; - /* remove non printable chars */ + /* Remove non printable chars */ for (i = 0; i < len - 1; i++) { if (s[i] < ' ' || s[i] > 127) s[i] = '?'; } - /* remove trailing spaces */ + /* Remove trailing spaces */ strim(s); } @@ -2833,37 +2989,133 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) return crc; } +/* Parse the Extended Parameter Page. */ +static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, + struct nand_chip *chip, struct nand_onfi_params *p) +{ + struct onfi_ext_param_page *ep; + struct onfi_ext_section *s; + struct onfi_ext_ecc_info *ecc; + uint8_t *cursor; + int ret = -EINVAL; + int len; + int i; + + len = le16_to_cpu(p->ext_param_page_length) * 16; + ep = kmalloc(len, GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Send our own NAND_CMD_PARAM. */ + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + + /* Use the Change Read Column command to skip the ONFI param pages. */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + sizeof(*p) * p->num_of_param_pages , -1); + + /* Read out the Extended Parameter Page. */ + chip->read_buf(mtd, (uint8_t *)ep, len); + if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) + != le16_to_cpu(ep->crc))) { + pr_debug("fail in the CRC.\n"); + goto ext_out; + } + + /* + * Check the signature. + * Do not strictly follow the ONFI spec, maybe changed in future. + */ + if (strncmp(ep->sig, "EPPS", 4)) { + pr_debug("The signature is invalid.\n"); + goto ext_out; + } + + /* find the ECC section. */ + cursor = (uint8_t *)(ep + 1); + for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { + s = ep->sections + i; + if (s->type == ONFI_SECTION_TYPE_2) + break; + cursor += s->length * 16; + } + if (i == ONFI_EXT_SECTION_MAX) { + pr_debug("We can not find the ECC section.\n"); + goto ext_out; + } + + /* get the info we want. */ + ecc = (struct onfi_ext_ecc_info *)cursor; + + if (!ecc->codeword_size) { + pr_debug("Invalid codeword size\n"); + goto ext_out; + } + + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + ret = 0; + +ext_out: + kfree(ep); + return ret; +} + +static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd->priv; + uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; + + return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, + feature); +} + /* - * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise + * Configure chip properties from Micron vendor-specific ONFI table + */ +static void nand_onfi_detect_micron(struct nand_chip *chip, + struct nand_onfi_params *p) +{ + struct nand_onfi_vendor_micron *micron = (void *)p->vendor; + + if (le16_to_cpu(p->vendor_revision) < 1) + return; + + chip->read_retries = micron->read_retry_options; + chip->setup_read_retry = nand_setup_read_retry_micron; +} + +/* + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. */ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, - int busw) + int *busw) { struct nand_onfi_params *p = &chip->onfi_params; - int i; + int i, j; int val; - /* try ONFI for unknow chip or LP */ + /* Try ONFI for unknown chip or LP */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') return 0; - printk(KERN_INFO "ONFI flash detected\n"); chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); for (i = 0; i < 3; i++) { - chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); + for (j = 0; j < sizeof(*p); j++) + ((uint8_t *)p)[j] = chip->read_byte(mtd); if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { - printk(KERN_INFO "ONFI param page %d valid\n", i); break; } } - if (i == 3) + if (i == 3) { + pr_err("Could not find valid ONFI parameter page; aborting\n"); return 0; + } - /* check version */ + /* Check version */ val = le16_to_cpu(p->revision); if (val & (1 << 5)) chip->onfi_version = 23; @@ -2875,12 +3127,9 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, chip->onfi_version = 20; else if (val & (1 << 1)) chip->onfi_version = 10; - else - chip->onfi_version = 0; if (!chip->onfi_version) { - printk(KERN_INFO "%s: unsupported ONFI version: %d\n", - __func__, val); + pr_info("unsupported ONFI version: %d\n", val); return 0; } @@ -2888,40 +3137,457 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, sanitize_string(p->model, sizeof(p->model)); if (!mtd->name) mtd->name = p->model; + + mtd->writesize = le32_to_cpu(p->byte_per_page); + + /* + * pages_per_block and blocks_per_lun may not be a power-of-2 size + * (don't ask me who thought of this...). MTD assumes that these + * dimensions will be power-of-2, so just truncate the remaining area. + */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + + /* See erasesize comment */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + chip->bits_per_cell = p->bits_per_cell; + + if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) + *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + 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)) { + + /* + * The nand_flash_detect_ext_param_page() uses the + * Change Read Column command which maybe not supported + * by the chip->cmdfunc. So try to update the chip->cmdfunc + * now. We do not replace user supplied command function. + */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + /* The Extended Parameter Page is supported since ONFI 2.1. */ + if (nand_flash_detect_ext_param_page(mtd, chip, p)) + pr_warn("Failed to detect ONFI extended param page\n"); + } else { + pr_warn("Could not retrieve ONFI ECC requirements\n"); + } + + if (p->jedec_id == NAND_MFR_MICRON) + nand_onfi_detect_micron(chip, p); + + return 1; +} + +/* + * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + struct nand_jedec_params *p = &chip->jedec_params; + struct jedec_ecc_info *ecc; + int val; + int i, j; + + /* Try JEDEC for unknown chip or LP */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); + if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'C') + return 0; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1); + for (i = 0; i < 3; i++) { + for (j = 0; j < sizeof(*p); j++) + ((uint8_t *)p)[j] = chip->read_byte(mtd); + + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == + le16_to_cpu(p->crc)) + break; + } + + if (i == 3) { + pr_err("Could not find valid JEDEC parameter page; aborting\n"); + return 0; + } + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 2)) + chip->jedec_version = 10; + else if (val & (1 << 1)) + chip->jedec_version = 1; /* vendor specific version */ + + if (!chip->jedec_version) { + pr_info("unsupported JEDEC version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + mtd->writesize = le32_to_cpu(p->byte_per_page); - mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; + + /* Please reference to the comment for nand_flash_detect_onfi. */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize; - busw = 0; - if (le16_to_cpu(p->features) & 1) - busw = NAND_BUSWIDTH_16; - chip->options &= ~NAND_CHIPOPTIONS_MSK; - chip->options |= (NAND_NO_READRDY | - NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; + /* Please reference to the comment for nand_flash_detect_onfi. */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + 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) + *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + /* ECC info */ + ecc = &p->ecc_info[0]; + + if (ecc->codeword_size >= 9) { + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + } else { + pr_warn("Invalid codeword size\n"); + } return 1; } /* - * Get the flash and manufacturer id and lookup if the type is supported + * nand_id_has_period - Check if an ID string has a given wraparound period + * @id_data: the ID string + * @arrlen: the length of the @id_data array + * @period: the period of repitition + * + * Check if an ID string is repeated within a given sequence of bytes at + * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a + * period of 3). This is a helper function for nand_id_len(). Returns non-zero + * if the repetition has a period of @period; otherwise, returns zero. + */ +static int nand_id_has_period(u8 *id_data, int arrlen, int period) +{ + int i, j; + for (i = 0; i < period; i++) + for (j = i + period; j < arrlen; j += period) + if (id_data[i] != id_data[j]) + return 0; + return 1; +} + +/* + * nand_id_len - Get the length of an ID string returned by CMD_READID + * @id_data: the ID string + * @arrlen: the length of the @id_data array + + * Returns the length of the ID string, according to known wraparound/trailing + * zero patterns. If no pattern exists, returns the length of the array. + */ +static int nand_id_len(u8 *id_data, int arrlen) +{ + int last_nonzero, period; + + /* Find last non-zero byte */ + for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) + if (id_data[last_nonzero]) + break; + + /* All zeros */ + if (last_nonzero < 0) + return 0; + + /* Calculate wraparound period */ + for (period = 1; period < arrlen; period++) + if (nand_id_has_period(id_data, arrlen, period)) + break; + + /* There's a repeated pattern */ + if (period < arrlen) + return period; + + /* There are trailing zeros */ + if (last_nonzero < arrlen - 1) + return last_nonzero + 1; + + /* No pattern detected */ + return arrlen; +} + +/* Extract the bits of per cell from the 3rd byte of the extended ID */ +static int nand_get_bits_per_cell(u8 cellinfo) +{ + int bits; + + bits = cellinfo & NAND_CI_CELLTYPE_MSK; + bits >>= NAND_CI_CELLTYPE_SHIFT; + return bits + 1; +} + +/* + * Many new NAND share similar device ID codes, which represent the size of the + * chip. The rest of the parameters must be decoded according to generic or + * manufacturer-specific "extended ID" decoding patterns. + */ +static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, + u8 id_data[8], int *busw) +{ + int extid, id_len; + /* The 3rd id byte holds MLC / multichip data */ + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); + /* The 4th id byte is the important one */ + extid = id_data[3]; + + id_len = nand_id_len(id_data, 8); + + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) + * Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22) + * + * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung + * ID to decide what to do. + */ + if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && + !nand_is_slc(chip) && id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (((extid >> 2) & 0x04) | (extid & 0x03)) { + case 1: + mtd->oobsize = 128; + break; + case 2: + mtd->oobsize = 218; + break; + case 3: + mtd->oobsize = 400; + break; + case 4: + mtd->oobsize = 436; + break; + case 5: + mtd->oobsize = 512; + break; + case 6: + mtd->oobsize = 640; + break; + case 7: + default: /* Other cases are "reserved" (unknown) */ + mtd->oobsize = 1024; + break; + } + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + *busw = 0; + } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && + !nand_is_slc(chip)) { + unsigned int tmp; + + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (((extid >> 2) & 0x04) | (extid & 0x03)) { + case 0: + mtd->oobsize = 128; + break; + case 1: + mtd->oobsize = 224; + break; + case 2: + mtd->oobsize = 448; + break; + case 3: + mtd->oobsize = 64; + break; + case 4: + mtd->oobsize = 32; + break; + case 5: + mtd->oobsize = 16; + break; + default: + mtd->oobsize = 640; + break; + } + extid >>= 2; + /* Calc blocksize */ + tmp = ((extid >> 1) & 0x04) | (extid & 0x03); + if (tmp < 0x03) + mtd->erasesize = (128 * 1024) << tmp; + else if (tmp == 0x03) + mtd->erasesize = 768 * 1024; + else + mtd->erasesize = (64 * 1024) << tmp; + *busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + /* + * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per + * 512B page. For Toshiba SLC, we decode the 5th/6th byte as + * follows: + * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, + * 110b -> 24nm + * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC + */ + if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA && + nand_is_slc(chip) && + (id_data[5] & 0x7) == 0x6 /* 24nm */ && + !(id_data[4] & 0x80) /* !BENAND */) { + mtd->oobsize = 32 * mtd->writesize >> 9; + } + + } +} + +/* + * Old devices have chip data hardcoded in the device ID table. nand_decode_id + * decodes a matching ID table entry and assigns the MTD size parameters for + * the chip. + */ +static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, + struct nand_flash_dev *type, u8 id_data[8], + int *busw) +{ + int maf_id = id_data[0]; + + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + *busw = type->options & NAND_BUSWIDTH_16; + + /* All legacy ID NAND are small-page, SLC */ + chip->bits_per_cell = 1; + + /* + * Check for Spansion/AMD ID + repeating 5th, 6th byte since + * some Spansion chips have erasesize that conflicts with size + * listed in nand_ids table. + * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) + */ + if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 + && id_data[6] == 0x00 && id_data[7] == 0x00 + && mtd->writesize == 512) { + mtd->erasesize = 128 * 1024; + mtd->erasesize <<= ((id_data[3] & 0x03) << 1); + } +} + +/* + * Set the bad block marker/indicator (BBM/BBI) patterns according to some + * heuristic patterns using various detected parameters (e.g., manufacturer, + * page size, cell-type information). + */ +static void nand_decode_bbm_options(struct mtd_info *mtd, + struct nand_chip *chip, u8 id_data[8]) +{ + int maf_id = id_data[0]; + + /* Set the bad block position */ + if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) + chip->badblockpos = NAND_LARGE_BADBLOCK_POS; + else + chip->badblockpos = NAND_SMALL_BADBLOCK_POS; + + /* + * Bad block marker is stored in the last page of each block on Samsung + * and Hynix MLC devices; stored in first two pages of each block on + * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, + * AMD/Spansion, and Macronix. All others scan only the first page. + */ + if (!nand_is_slc(chip) && + (maf_id == NAND_MFR_SAMSUNG || + maf_id == NAND_MFR_HYNIX)) + chip->bbt_options |= NAND_BBT_SCANLASTPAGE; + else if ((nand_is_slc(chip) && + (maf_id == NAND_MFR_SAMSUNG || + maf_id == NAND_MFR_HYNIX || + maf_id == NAND_MFR_TOSHIBA || + maf_id == NAND_MFR_AMD || + maf_id == NAND_MFR_MACRONIX)) || + (mtd->writesize == 2048 && + maf_id == NAND_MFR_MICRON)) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; +} + +static inline bool is_full_id_nand(struct nand_flash_dev *type) +{ + return type->id_len; +} + +static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, + struct nand_flash_dev *type, u8 *id_data, int *busw) +{ + if (!strncmp(type->id, id_data, type->id_len)) { + mtd->writesize = type->pagesize; + mtd->erasesize = type->erasesize; + mtd->oobsize = type->oobsize; + + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); + chip->chipsize = (uint64_t)type->chipsize << 20; + chip->options |= type->options; + chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); + chip->ecc_step_ds = NAND_ECC_STEP(type); + + *busw = type->options & NAND_BUSWIDTH_16; + + if (!mtd->name) + mtd->name = type->name; + + return true; + } + return false; +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported. */ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, - int busw, int *maf_id, int *dev_id, struct nand_flash_dev *type) { + int busw; int i, maf_idx; u8 id_data[8]; - int ret; /* Select the device */ chip->select_chip(mtd, 0); /* * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) - * after power-up + * after power-up. */ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); @@ -2932,7 +3598,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, *maf_id = chip->read_byte(mtd); *dev_id = chip->read_byte(mtd); - /* Try again to make sure, as some systems the bus-hold or other + /* + * Try again to make sure, as some systems the bus-hold or other * interface concerns can cause random data which looks like a * possibly credible NAND flash to appear. If the two results do * not match, ignore the device completely. @@ -2940,37 +3607,38 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - for (i = 0; i < 2; i++) + /* Read entire ID string */ + for (i = 0; i < 8; i++) id_data[i] = chip->read_byte(mtd); if (id_data[0] != *maf_id || id_data[1] != *dev_id) { - printk(KERN_INFO "%s: second ID read did not match " - "%02x,%02x against %02x,%02x\n", __func__, - *maf_id, *dev_id, id_data[0], id_data[1]); + pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", + *maf_id, *dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } if (!type) type = nand_flash_ids; - for (; type->name != NULL; type++) - if (*dev_id == type->id) - break; + for (; type->name != NULL; type++) { + if (is_full_id_nand(type)) { + if (find_full_id_nand(mtd, chip, type, id_data, &busw)) + goto ident_done; + } else if (*dev_id == type->dev_id) { + break; + } + } chip->onfi_version = 0; if (!type->name || !type->pagesize) { - /* Check is chip is ONFI compliant */ - ret = nand_flash_detect_onfi(mtd, chip, busw); - if (ret) + /* Check if the chip is ONFI compliant */ + if (nand_flash_detect_onfi(mtd, chip, &busw)) goto ident_done; - } - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - - /* Read entire ID string */ - - for (i = 0; i < 8; i++) - id_data[i] = chip->read_byte(mtd); + /* Check if the chip is JEDEC compliant */ + if (nand_flash_detect_jedec(mtd, chip, &busw)) + goto ident_done; + } if (!type->name) return ERR_PTR(-ENODEV); @@ -2981,125 +3649,54 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->chipsize = (uint64_t)type->chipsize << 20; if (!type->pagesize && chip->init_size) { - /* set the pagesize, oobsize, erasesize by the driver*/ + /* Set the pagesize, oobsize, erasesize by the driver */ busw = chip->init_size(mtd, chip, id_data); } else if (!type->pagesize) { - int extid; - /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = id_data[2]; - /* The 4th id byte is the important one */ - extid = id_data[3]; - - /* - * Field definitions are in the following datasheets: - * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) - * New style (6 byte ID): Samsung K9GBG08U0M (p.40) - * - * Check for wraparound + Samsung ID + nonzero 6th byte - * to decide what to do. - */ - if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && - id_data[0] == NAND_MFR_SAMSUNG && - (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && - id_data[5] != 0x00) { - /* Calc pagesize */ - mtd->writesize = 2048 << (extid & 0x03); - extid >>= 2; - /* Calc oobsize */ - switch (extid & 0x03) { - case 1: - mtd->oobsize = 128; - break; - case 2: - mtd->oobsize = 218; - break; - case 3: - mtd->oobsize = 400; - break; - default: - mtd->oobsize = 436; - break; - } - extid >>= 2; - /* Calc blocksize */ - mtd->erasesize = (128 * 1024) << - (((extid >> 1) & 0x04) | (extid & 0x03)); - busw = 0; - } else { - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x03); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * - (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; - } + /* Decode parameters from extended ID */ + nand_decode_ext_id(mtd, chip, id_data, &busw); } else { - /* - * Old devices have chip data hardcoded in the device id table - */ - mtd->erasesize = type->erasesize; - mtd->writesize = type->pagesize; - mtd->oobsize = mtd->writesize / 32; - busw = type->options & NAND_BUSWIDTH_16; - - /* - * Check for Spansion/AMD ID + repeating 5th, 6th byte since - * some Spansion chips have erasesize that conflicts with size - * listed in nand_ids table - * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) - */ - if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && - id_data[5] == 0x00 && id_data[6] == 0x00 && - id_data[7] == 0x00 && mtd->writesize == 512) { - mtd->erasesize = 128 * 1024; - mtd->erasesize <<= ((id_data[3] & 0x03) << 1); - } + nand_decode_id(mtd, chip, type, id_data, &busw); } - /* Get chip options, preserve non chip based options */ - chip->options &= ~NAND_CHIPOPTIONS_MSK; - chip->options |= type->options & NAND_CHIPOPTIONS_MSK; + /* Get chip options */ + chip->options |= type->options; - /* Check if chip is a not a samsung device. Do not clear the - * options for chips which are not having an extended id. + /* + * Check if chip is not a Samsung device. Do not clear the + * options for chips which do not have an extended id. */ if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; ident_done: - /* - * Set chip as a default. Board drivers can override it, if necessary - */ - chip->options |= NAND_NO_AUTOINCR; - /* Try to identify manufacturer */ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { if (nand_manuf_ids[maf_idx].id == *maf_id) break; } - /* - * Check, if buswidth is correct. Hardware drivers should set - * chip correct ! - */ - if (busw != (chip->options & NAND_BUSWIDTH_16)) { - printk(KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, - *dev_id, nand_manuf_ids[maf_idx].name, mtd->name); - printk(KERN_WARNING "NAND bus width %d instead %d bit\n", - (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, - busw ? 16 : 8); + if (chip->options & NAND_BUSWIDTH_AUTO) { + WARN_ON(chip->options & NAND_BUSWIDTH_16); + chip->options |= busw; + nand_set_defaults(chip, busw); + } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct! + */ + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", + *maf_id, *dev_id); + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name); + pr_warn("bus width %d instead %d bit\n", + (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); return ERR_PTR(-EINVAL); } + nand_decode_bbm_options(mtd, chip, id_data); + /* Calculate the address shift from the page size */ chip->page_shift = ffs(mtd->writesize) - 1; - /* Convert chipsize to number of pages per chip -1. */ + /* Convert chipsize to number of pages per chip -1 */ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; chip->bbt_erase_shift = chip->phys_erase_shift = @@ -3111,95 +3708,66 @@ ident_done: chip->chip_shift += 32 - 1; } - /* Set the bad block position */ - if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) - chip->badblockpos = NAND_LARGE_BADBLOCK_POS; - else - chip->badblockpos = NAND_SMALL_BADBLOCK_POS; - - /* - * Bad block marker is stored in the last page of each block - * on Samsung and Hynix MLC devices; stored in first two pages - * of each block on Micron devices with 2KiB pages and on - * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan - * only the first page. - */ - if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && - (*maf_id == NAND_MFR_SAMSUNG || - *maf_id == NAND_MFR_HYNIX)) - chip->options |= NAND_BBT_SCANLASTPAGE; - else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && - (*maf_id == NAND_MFR_SAMSUNG || - *maf_id == NAND_MFR_HYNIX || - *maf_id == NAND_MFR_TOSHIBA || - *maf_id == NAND_MFR_AMD)) || - (mtd->writesize == 2048 && - *maf_id == NAND_MFR_MICRON)) - chip->options |= NAND_BBT_SCAN2NDPAGE; - - /* - * Numonyx/ST 2K pages, x8 bus use BOTH byte 1 and 6 - */ - if (!(busw & NAND_BUSWIDTH_16) && - *maf_id == NAND_MFR_STMICRO && - mtd->writesize == 2048) { - chip->options |= NAND_BBT_SCANBYTE1AND6; - chip->badblockpos = 0; - } - - /* Check for AND chips with 4 page planes */ - if (chip->options & NAND_4PAGE_ARRAY) - chip->erase_cmd = multi_erase_cmd; - else - chip->erase_cmd = single_erase_cmd; + chip->badblockbits = 8; + chip->erase = single_erase; - /* Do not replace user supplied command function ! */ + /* Do not replace user supplied command function! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - /* TODO onfi flash name */ - printk(KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, - nand_manuf_ids[maf_idx].name, - chip->onfi_version ? chip->onfi_params.model : type->name); + 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_manuf_ids[maf_idx].name, + chip->onfi_params.model); + else if (chip->jedec_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->jedec_params.model); + else + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); + + pr_info("%dMiB, %s, page size: %d, OOB size: %d\n", + (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", + mtd->writesize, mtd->oobsize); return type; } /** * nand_scan_ident - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * @maxchips: Number of chips to scan for - * @table: Alternative NAND ID table + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * @table: alternative NAND ID table * - * This is the first phase of the normal nand_scan() function. It - * reads the flash ID and sets up MTD fields accordingly. + * This is the first phase of the normal nand_scan() function. It reads the + * flash ID and sets up MTD fields accordingly. * * The mtd->owner field must be set to the module of the caller. */ int nand_scan_ident(struct mtd_info *mtd, int maxchips, struct nand_flash_dev *table) { - int i, busw, nand_maf_id, nand_dev_id; + int i, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; - /* Get buswidth to select the correct functions */ - busw = chip->options & NAND_BUSWIDTH_16; /* Set the default functions */ - nand_set_defaults(chip, busw); + nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, - &nand_maf_id, &nand_dev_id, table); + type = nand_get_flash_type(mtd, chip, &nand_maf_id, + &nand_dev_id, table); if (IS_ERR(type)) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) - printk(KERN_WARNING "No NAND device found.\n"); + pr_warn("No NAND device found\n"); chip->select_chip(mtd, -1); return PTR_ERR(type); } + chip->select_chip(mtd, -1); + /* Check for a chip array */ for (i = 1; i < maxchips; i++) { chip->select_chip(mtd, i); @@ -3209,11 +3777,14 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != chip->read_byte(mtd) || - nand_dev_id != chip->read_byte(mtd)) + nand_dev_id != chip->read_byte(mtd)) { + chip->select_chip(mtd, -1); break; + } + chip->select_chip(mtd, -1); } if (i > 1) - printk(KERN_INFO "%d NAND chips detected\n", i); + pr_info("%d chips detected\n", i); /* Store the number of chips and calc total size for mtd */ chip->numchips = i; @@ -3223,48 +3794,97 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, } EXPORT_SYMBOL(nand_scan_ident); +/* + * Check if the chip configuration meet the datasheet requirements. + + * If our configuration corrects A bits per B bytes and the minimum + * required correction level is X bits per Y bytes, then we must ensure + * both of the following are true: + * + * (1) A / B >= X / Y + * (2) A >= X + * + * Requirement (1) ensures we can correct for the required bitflip density. + * Requirement (2) ensures we can correct even when all bitflips are clumped + * in the same sector. + */ +static bool nand_ecc_strength_good(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + int corr, ds_corr; + + if (ecc->size == 0 || chip->ecc_step_ds == 0) + /* Not enough information */ + return true; + + /* + * We get the number of corrected bits per page to compare + * the correction density. + */ + corr = (mtd->writesize * ecc->strength) / ecc->size; + ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds; + + return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; +} /** * nand_scan_tail - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure + * @mtd: MTD device structure * - * This is the second phase of the normal nand_scan() function. It - * fills out all the uninitialized function pointers with the defaults - * and scans for a bad block table if appropriate. + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate. */ int nand_scan_tail(struct mtd_info *mtd) { int i; struct nand_chip *chip = mtd->priv; - - if (!(chip->options & NAND_OWN_BUFFERS)) - chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); - if (!chip->buffers) - return -ENOMEM; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_buffers *nbuf; + + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + + if (!(chip->options & NAND_OWN_BUFFERS)) { + nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + + mtd->oobsize * 3, GFP_KERNEL); + if (!nbuf) + return -ENOMEM; + nbuf->ecccalc = (uint8_t *)(nbuf + 1); + nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; + nbuf->databuf = nbuf->ecccode + mtd->oobsize; + + chip->buffers = nbuf; + } else { + if (!chip->buffers) + return -ENOMEM; + } /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* - * If no default placement scheme is given, select an appropriate one + * If no default placement scheme is given, select an appropriate one. */ - if (!chip->ecc.layout) { + if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: - chip->ecc.layout = &nand_oob_8; + ecc->layout = &nand_oob_8; break; case 16: - chip->ecc.layout = &nand_oob_16; + ecc->layout = &nand_oob_16; break; case 64: - chip->ecc.layout = &nand_oob_64; + ecc->layout = &nand_oob_64; break; case 128: - chip->ecc.layout = &nand_oob_128; + ecc->layout = &nand_oob_128; break; default: - printk(KERN_WARNING "No oob scheme defined for " - "oobsize %d\n", mtd->oobsize); + pr_warn("No oob scheme defined for oobsize %d\n", + mtd->oobsize); BUG(); } } @@ -3273,132 +3893,179 @@ int nand_scan_tail(struct mtd_info *mtd) chip->write_page = nand_write_page; /* - * check ECC mode, default to software if 3byte/512byte hardware ECC is + * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ - switch (chip->ecc.mode) { + switch (ecc->mode) { case NAND_ECC_HW_OOB_FIRST: /* Similar to NAND_ECC_HW, but a separate read_page handle */ - if (!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) { - printk(KERN_WARNING "No ECC functions supplied; " - "Hardware ECC not possible\n"); + if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { + pr_warn("No ECC functions supplied; " + "hardware ECC not possible\n"); BUG(); } - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_hwecc_oob_first; + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc_oob_first; case NAND_ECC_HW: - /* Use standard hwecc read page function ? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_hwecc; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_hwecc; - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_std; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_std; + /* Use standard hwecc read page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc; + if (!ecc->write_page) + ecc->write_page = nand_write_page_hwecc; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_std; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_std; + if (!ecc->read_subpage) + ecc->read_subpage = nand_read_subpage; + if (!ecc->write_subpage) + ecc->write_subpage = nand_write_subpage_hwecc; case NAND_ECC_HW_SYNDROME: - if ((!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) && - (!chip->ecc.read_page || - chip->ecc.read_page == nand_read_page_hwecc || - !chip->ecc.write_page || - chip->ecc.write_page == nand_write_page_hwecc)) { - printk(KERN_WARNING "No ECC functions supplied; " - "Hardware ECC not possible\n"); + if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && + (!ecc->read_page || + ecc->read_page == nand_read_page_hwecc || + !ecc->write_page || + ecc->write_page == nand_write_page_hwecc)) { + pr_warn("No ECC functions supplied; " + "hardware ECC not possible\n"); BUG(); } - /* Use standard syndrome read/write page function ? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_syndrome; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_syndrome; - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw_syndrome; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw_syndrome; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_syndrome; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_syndrome; - - if (mtd->writesize >= chip->ecc.size) + /* Use standard syndrome read/write page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_syndrome; + if (!ecc->write_page) + ecc->write_page = nand_write_page_syndrome; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw_syndrome; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw_syndrome; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_syndrome; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= ecc->size) { + if (!ecc->strength) { + pr_warn("Driver must set ecc.strength when using hardware ECC\n"); + BUG(); + } break; - printk(KERN_WARNING "%d byte HW ECC not possible on " - "%d byte page size, fallback to SW ECC\n", - chip->ecc.size, mtd->writesize); - chip->ecc.mode = NAND_ECC_SOFT; + } + pr_warn("%d byte HW ECC not possible on " + "%d byte page size, fallback to SW ECC\n", + ecc->size, mtd->writesize); + ecc->mode = NAND_ECC_SOFT; case NAND_ECC_SOFT: - chip->ecc.calculate = nand_calculate_ecc; - chip->ecc.correct = nand_correct_data; - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.read_subpage = nand_read_subpage; - chip->ecc.write_page = nand_write_page_swecc; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.write_oob = nand_write_oob_std; - if (!chip->ecc.size) - chip->ecc.size = 256; - chip->ecc.bytes = 3; + ecc->calculate = nand_calculate_ecc; + ecc->correct = nand_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + if (!ecc->size) + ecc->size = 256; + ecc->bytes = 3; + ecc->strength = 1; + break; + + case NAND_ECC_SOFT_BCH: + if (!mtd_nand_has_bch()) { + pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + BUG(); + } + ecc->calculate = nand_bch_calculate_ecc; + ecc->correct = nand_bch_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.bytes values to + * select how many bits are correctable; see nand_bch_init() + * for details. Otherwise, default to 4 bits for large page + * devices. + */ + if (!ecc->size && (mtd->oobsize >= 64)) { + ecc->size = 512; + ecc->bytes = 7; + } + ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes, + &ecc->layout); + if (!ecc->priv) { + pr_warn("BCH ECC initialization failed!\n"); + BUG(); + } + ecc->strength = ecc->bytes * 8 / fls(8 * ecc->size); break; case NAND_ECC_NONE: - printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " - "This is not recommended !!\n"); - chip->ecc.read_page = nand_read_page_raw; - chip->ecc.write_page = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.write_oob = nand_write_oob_std; - chip->ecc.size = mtd->writesize; - chip->ecc.bytes = 0; + pr_warn("NAND_ECC_NONE selected by board driver. " + "This is not recommended!\n"); + ecc->read_page = nand_read_page_raw; + ecc->write_page = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->write_oob = nand_write_oob_std; + ecc->size = mtd->writesize; + ecc->bytes = 0; + ecc->strength = 0; break; default: - printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", - chip->ecc.mode); + pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode); BUG(); } + /* For many systems, the standard OOB write also works for raw */ + if (!ecc->read_oob_raw) + ecc->read_oob_raw = ecc->read_oob; + if (!ecc->write_oob_raw) + ecc->write_oob_raw = ecc->write_oob; + /* * The number of bytes available for a client to place data into - * the out of band area + * the out of band area. */ - chip->ecc.layout->oobavail = 0; - for (i = 0; chip->ecc.layout->oobfree[i].length - && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) - chip->ecc.layout->oobavail += - chip->ecc.layout->oobfree[i].length; - mtd->oobavail = chip->ecc.layout->oobavail; + ecc->layout->oobavail = 0; + for (i = 0; ecc->layout->oobfree[i].length + && i < ARRAY_SIZE(ecc->layout->oobfree); i++) + ecc->layout->oobavail += ecc->layout->oobfree[i].length; + mtd->oobavail = ecc->layout->oobavail; + + /* ECC sanity check: warn if it's too weak */ + if (!nand_ecc_strength_good(mtd)) + pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", + mtd->name); /* * Set the number of read / write steps for one page depending on ECC - * mode + * mode. */ - chip->ecc.steps = mtd->writesize / chip->ecc.size; - if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { - printk(KERN_WARNING "Invalid ecc parameters\n"); + ecc->steps = mtd->writesize / ecc->size; + if (ecc->steps * ecc->size != mtd->writesize) { + pr_warn("Invalid ECC parameters\n"); BUG(); } - chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; + ecc->total = ecc->steps * ecc->bytes; - /* - * Allow subpage writes up to ecc.steps. Not possible for MLC - * FLASH. - */ - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { - switch (chip->ecc.steps) { + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { + switch (ecc->steps) { case 2: mtd->subpage_sft = 1; break; @@ -3414,35 +4081,53 @@ int nand_scan_tail(struct mtd_info *mtd) /* Initialize state */ chip->state = FL_READY; - /* De-select the device */ - chip->select_chip(mtd, -1); - /* Invalidate the pagebuffer reference */ chip->pagebuf = -1; + /* Large page NAND with SOFT_ECC should support subpage reads */ + switch (ecc->mode) { + case NAND_ECC_SOFT: + case NAND_ECC_SOFT_BCH: + if (chip->page_shift > 9) + chip->options |= NAND_SUBPAGE_READ; + break; + + default: + break; + } + /* Fill in remaining MTD driver data */ - mtd->type = MTD_NANDFLASH; + mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; - mtd->erase = nand_erase; - mtd->point = NULL; - mtd->unpoint = NULL; - mtd->read = nand_read; - mtd->write = nand_write; - mtd->panic_write = panic_nand_write; - mtd->read_oob = nand_read_oob; - mtd->write_oob = nand_write_oob; - mtd->sync = nand_sync; - mtd->lock = NULL; - mtd->unlock = NULL; - mtd->suspend = nand_suspend; - mtd->resume = nand_resume; - mtd->block_isbad = nand_block_isbad; - mtd->block_markbad = nand_block_markbad; + mtd->_erase = nand_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = nand_read; + mtd->_write = nand_write; + mtd->_panic_write = panic_nand_write; + mtd->_read_oob = nand_read_oob; + mtd->_write_oob = nand_write_oob; + mtd->_sync = nand_sync; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; + mtd->_block_isbad = nand_block_isbad; + mtd->_block_markbad = nand_block_markbad; mtd->writebufsize = mtd->writesize; - /* propagate ecc.layout to mtd_info */ - mtd->ecclayout = chip->ecc.layout; + /* propagate ecc info to mtd_info */ + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = mtd->ecc_strength; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) @@ -3453,9 +4138,11 @@ int nand_scan_tail(struct mtd_info *mtd) } EXPORT_SYMBOL(nand_scan_tail); -/* is_module_text_address() isn't exported, and it's mostly a pointless +/* + * is_module_text_address() isn't exported, and it's mostly a pointless * test if this is a module _anyway_ -- they'd have to try _really_ hard - * to call us from in-kernel code if the core NAND support is modular. */ + * to call us from in-kernel code if the core NAND support is modular. + */ #ifdef MODULE #define caller_is_module() (1) #else @@ -3465,15 +4152,13 @@ EXPORT_SYMBOL(nand_scan_tail); /** * nand_scan - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * @maxchips: Number of chips to scan for - * - * This fills out all the uninitialized function pointers - * with the defaults. - * The flash ID is read and the mtd/chip structures are - * filled with the appropriate values. - * The mtd->owner field must be set to the module of the caller + * @mtd: MTD device structure + * @maxchips: number of chips to scan for * + * This fills out all the uninitialized function pointers with the defaults. + * The flash ID is read and the mtd/chip structures are filled with the + * appropriate values. The mtd->owner field must be set to the module of the + * caller. */ int nand_scan(struct mtd_info *mtd, int maxchips) { @@ -3481,8 +4166,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* Many callers got this wrong, so check for it for a while... */ if (!mtd->owner && caller_is_module()) { - printk(KERN_CRIT "%s called with NULL mtd->owner!\n", - __func__); + pr_crit("%s called with NULL mtd->owner!\n", __func__); BUG(); } @@ -3495,18 +4179,16 @@ EXPORT_SYMBOL(nand_scan); /** * nand_release - [NAND Interface] Free resources held by the NAND device - * @mtd: MTD device structure -*/ + * @mtd: MTD device structure + */ void nand_release(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; -#ifdef CONFIG_MTD_PARTITIONS - /* Deregister partitions */ - del_mtd_partitions(mtd); -#endif - /* Deregister the device */ - del_mtd_device(mtd); + if (chip->ecc.mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + + mtd_device_unregister(mtd); /* Free bad block table memory */ kfree(chip->bbt); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 6ebd869993a..7f0c3b4c2a4 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -4,7 +4,7 @@ * Overview: * Bad block table support for the NAND driver * - * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) * * 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 @@ -14,7 +14,7 @@ * * When nand_scan_bbt is called, then it tries to find the bad block table * depending on the options in the BBT descriptor(s). If no flash based BBT - * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory + * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory * marked good / bad blocks. This information is used to create a memory BBT. * Once a new bad block is discovered then the "factory" information is updated * on the device. @@ -22,7 +22,7 @@ * BBT on flash. If a BBT is found then the contents are read and the memory * based BBT is created. If a mirrored BBT is selected then the mirror is * searched too and the versions are compared. If the mirror has a greater - * version number than the mirror BBT is used to build the memory based BBT. + * version number, then the mirror BBT is used to build the memory based BBT. * If the tables are not versioned, then we "or" the bad block information. * If one of the BBTs is out of date or does not exist it is (re)created. * If no BBT exists at all then the device is scanned for factory marked @@ -36,9 +36,9 @@ * The table is marked in the OOB area with an ident pattern and a version * number which indicates which of both tables is more up to date. If the NAND * controller needs the complete OOB area for the ECC information then the - * option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern - * and the version byte into the data area and the OOB area will remain - * untouched. + * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of + * course): it moves the ident pattern and the version byte into the data area + * and the OOB area will remain untouched. * * The table uses 2 bits per block * 11b: block is good @@ -62,126 +62,90 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/bbm.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/vmalloc.h> +#include <linux/export.h> +#include <linux/string.h> -static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) +#define BBT_BLOCK_GOOD 0x00 +#define BBT_BLOCK_WORN 0x01 +#define BBT_BLOCK_RESERVED 0x02 +#define BBT_BLOCK_FACTORY_BAD 0x03 + +#define BBT_ENTRY_MASK 0x03 +#define BBT_ENTRY_SHIFT 2 + +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); + +static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) { - int ret; + uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; + entry >>= (block & BBT_ENTRY_MASK) * 2; + return entry & BBT_ENTRY_MASK; +} - ret = memcmp(buf, td->pattern, td->len); - if (!ret) - return ret; - return -1; +static inline void bbt_mark_entry(struct nand_chip *chip, int block, + uint8_t mark) +{ + uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); + chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; +} + +static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) +{ + if (memcmp(buf, td->pattern, td->len)) + return -1; + return 0; } /** * check_pattern - [GENERIC] check if a pattern is in the buffer - * @buf: the buffer to search - * @len: the length of buffer to search - * @paglen: the pagelength - * @td: search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block - * tables and good / bad block identifiers. - * If the SCAN_EMPTY option is set then check, if all bytes except the - * pattern area contain 0xff + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor * -*/ + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. + */ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int i, end = 0; - uint8_t *p = buf; - if (td->options & NAND_BBT_NO_OOB) return check_pattern_no_oob(buf, td); - end = paglen + td->offs; - if (td->options & NAND_BBT_SCANEMPTY) { - for (i = 0; i < end; i++) { - if (p[i] != 0xff) - return -1; - } - } - p += end; - /* Compare the pattern */ - for (i = 0; i < td->len; i++) { - if (p[i] != td->pattern[i]) - return -1; - } - - /* Check both positions 1 and 6 for pattern? */ - if (td->options & NAND_BBT_SCANBYTE1AND6) { - if (td->options & NAND_BBT_SCANEMPTY) { - p += td->len; - end += NAND_SMALL_BADBLOCK_POS - td->offs; - /* Check region between positions 1 and 6 */ - for (i = 0; i < NAND_SMALL_BADBLOCK_POS - td->offs - td->len; - i++) { - if (*p++ != 0xff) - return -1; - } - } - else { - p += NAND_SMALL_BADBLOCK_POS - td->offs; - } - /* Compare the pattern */ - for (i = 0; i < td->len; i++) { - if (p[i] != td->pattern[i]) - return -1; - } - } + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) + return -1; - if (td->options & NAND_BBT_SCANEMPTY) { - p += td->len; - end += td->len; - for (i = end; i < len; i++) { - if (*p++ != 0xff) - return -1; - } - } return 0; } /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer - * @buf: the buffer to search - * @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 + * @buf: the buffer to search + * @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. + */ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) { - int i; - uint8_t *p = buf; - /* Compare the pattern */ - for (i = 0; i < td->len; i++) { - if (p[td->offs + i] != td->pattern[i]) - return -1; - } - /* Need to check location 1 AND 6? */ - if (td->options & NAND_BBT_SCANBYTE1AND6) { - for (i = 0; i < td->len; i++) { - if (p[NAND_SMALL_BADBLOCK_POS + i] != td->pattern[i]) - return -1; - } - } + if (memcmp(buf + td->offs, td->pattern, td->len)) + return -1; return 0; } /** * add_marker_len - compute the length of the marker in data area - * @td: BBT descriptor used for computation + * @td: BBT descriptor used for computation * - * The length will be 0 if the markeris located in OOB area. + * The length will be 0 if the marker is located in OOB area. */ static u32 add_marker_len(struct nand_bbt_descr *td) { @@ -198,34 +162,33 @@ static u32 add_marker_len(struct nand_bbt_descr *td) /** * read_bbt - [GENERIC] Read the bad block table starting from page - * @mtd: MTD device structure - * @buf: temporary buffer - * @page: the starting page - * @num: the number of bbt descriptors to read - * @td: the bbt describtion table - * @offs: offset in the memory table + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @td: the bbt describtion table + * @offs: block number offset in the table * * Read the bad block table starting from page. - * */ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, struct nand_bbt_descr *td, int offs) { - int res, i, j, act = 0; + int res, ret = 0, i, j, act = 0; struct nand_chip *this = mtd->priv; size_t retlen, len, totlen; loff_t from; int bits = td->options & NAND_BBT_NRBITS_MSK; - uint8_t msk = (uint8_t) ((1 << bits) - 1); + uint8_t msk = (uint8_t)((1 << bits) - 1); u32 marker_len; int reserved_block_code = td->reserved_block_code; totlen = (num * bits) >> 3; marker_len = add_marker_len(td); - from = ((loff_t) page) << this->page_shift; + from = ((loff_t)page) << this->page_shift; while (totlen) { - len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); + len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); if (marker_len) { /* * In case the BBT marker is not in the OOB area it @@ -235,58 +198,72 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, from += marker_len; marker_len = 0; } - res = mtd->read(mtd, from, len, &retlen, buf); + res = mtd_read(mtd, from, len, &retlen, buf); if (res < 0) { - if (retlen != len) { - printk(KERN_INFO "nand_bbt: Error reading bad block table\n"); + if (mtd_is_eccerr(res)) { + pr_info("nand_bbt: ECC error in BBT at " + "0x%012llx\n", from & ~mtd->writesize); + return res; + } else if (mtd_is_bitflip(res)) { + pr_info("nand_bbt: corrected error in BBT at " + "0x%012llx\n", from & ~mtd->writesize); + ret = res; + } else { + pr_info("nand_bbt: error reading BBT\n"); return res; } - printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); } /* Analyse data */ for (i = 0; i < len; i++) { uint8_t dat = buf[i]; - for (j = 0; j < 8; j += bits, act += 2) { + for (j = 0; j < 8; j += bits, act++) { uint8_t tmp = (dat >> j) & msk; if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { - printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); - this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + pr_info("nand_read_bbt: reserved block at 0x%012llx\n", + (loff_t)(offs + act) << + this->bbt_erase_shift); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_RESERVED); mtd->ecc_stats.bbtblocks++; continue; } - /* Leave it for now, if its matured we can move this - * message to MTD_DEBUG_LEVEL0 */ - printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); - /* Factory marked bad or worn out ? */ + /* + * Leave it for now, if it's matured we can + * move this message to pr_debug. + */ + pr_info("nand_read_bbt: bad block at 0x%012llx\n", + (loff_t)(offs + act) << + this->bbt_erase_shift); + /* Factory marked bad or worn out? */ if (tmp == 0) - this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_FACTORY_BAD); else - this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_WORN); mtd->ecc_stats.badblocks++; } } totlen -= len; from += len; } - return 0; + return ret; } /** * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @chip: read the table for a specific chip, -1 read all chips. - * Applies only if NAND_BBT_PERCHIP option is set + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips; applies only if + * NAND_BBT_PERCHIP option is set * - * Read the bad block table for all chips starting at a given page - * We assume that the bbt bits are in consecutive order. -*/ + * Read the bad block table for all chips starting at a given page. We assume + * that the bbt bits are in consecutive order. + */ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) { struct nand_chip *this = mtd->priv; @@ -301,7 +278,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc td, offs); if (res) return res; - offs += this->chipsize >> (this->bbt_erase_shift + 2); + offs += this->chipsize >> this->bbt_erase_shift; } } else { res = read_bbt(mtd, buf, td->pages[0], @@ -312,10 +289,8 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc return 0; } -/* - * BBT marker is in the first page, no OOB. - */ -static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +/* BBT marker is in the first page, no OOB */ +static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, struct nand_bbt_descr *td) { size_t retlen; @@ -325,70 +300,73 @@ static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, if (td->options & NAND_BBT_VERSION) len++; - return mtd->read(mtd, offs, len, &retlen, buf); + return mtd_read(mtd, offs, len, &retlen, buf); } -/* - * Scan read raw data from flash +/** + * scan_read_oob - [GENERIC] Scan data+OOB region to buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @offs: offset at which to scan + * @len: length of data region to read + * + * Scan read data from data+OOB. May traverse multiple pages, interleaving + * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" + * ECC condition (error or bitflip). May quit on the first (non-ECC) error. */ -static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len) { struct mtd_oob_ops ops; - int res; + int res, ret = 0; - ops.mode = MTD_OOB_RAW; + ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; - while (len > 0) { - if (len <= mtd->writesize) { - ops.oobbuf = buf + len; - ops.datbuf = buf; - ops.len = len; - return mtd->read_oob(mtd, offs, &ops); - } else { - ops.oobbuf = buf + mtd->writesize; - ops.datbuf = buf; - ops.len = mtd->writesize; - res = mtd->read_oob(mtd, offs, &ops); + ops.datbuf = buf; + ops.len = min(len, (size_t)mtd->writesize); + ops.oobbuf = buf + ops.len; - if (res) + res = mtd_read_oob(mtd, offs, &ops); + if (res) { + if (!mtd_is_bitflip_or_eccerr(res)) return res; + else if (mtd_is_eccerr(res) || !ret) + ret = res; } buf += mtd->oobsize + mtd->writesize; len -= mtd->writesize; + offs += mtd->writesize; } - return 0; + return ret; } -static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len, struct nand_bbt_descr *td) { if (td->options & NAND_BBT_NO_OOB) - return scan_read_raw_data(mtd, buf, offs, td); + return scan_read_data(mtd, buf, offs, td); else - return scan_read_raw_oob(mtd, buf, offs, len); + return scan_read_oob(mtd, buf, offs, len); } -/* - * Scan write data with oob to flash - */ +/* Scan write data with oob to flash */ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, uint8_t *buf, uint8_t *oob) { struct mtd_oob_ops ops; - ops.mode = MTD_OOB_PLACE; + ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; ops.datbuf = buf; ops.oobbuf = oob; ops.len = len; - return mtd->write_oob(mtd, offs, &ops); + return mtd_write_oob(mtd, offs, &ops); } static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) @@ -402,65 +380,41 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) /** * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror * - * Read the bad block table(s) for all chips starting at a given page - * We assume that the bbt bits are in consecutive order. - * -*/ -static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md) + * Read the bad block table(s) for all chips starting at a given page. We + * assume that the bbt bits are in consecutive order. + */ +static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) { struct nand_chip *this = mtd->priv; /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, + scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, mtd->writesize, td); td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; - printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", - td->pages[0], td->version[0]); + pr_info("Bad block table at page %d, version 0x%02X\n", + td->pages[0], td->version[0]); } /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift, - mtd->writesize, td); + scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, + mtd->writesize, md); md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; - printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", - md->pages[0], md->version[0]); - } - return 1; -} - -/* - * Scan a given block full - */ -static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, size_t readlen, - int scanlen, int len) -{ - int ret, j; - - ret = scan_read_raw_oob(mtd, buf, offs, readlen); - if (ret) - return ret; - - for (j = 0; j < len; j++, buf += scanlen) { - if (check_pattern(buf, scanlen, mtd->writesize, bd)) - return 1; + pr_info("Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); } - return 0; } -/* - * Scan a given block partially - */ +/* Scan a given block partially */ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, int len) + loff_t offs, uint8_t *buf, int numpages) { struct mtd_oob_ops ops; int j, ret; @@ -469,16 +423,16 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, ops.oobbuf = buf; ops.ooboffs = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; + ops.mode = MTD_OPS_PLACE_OOB; - for (j = 0; j < len; j++) { + for (j = 0; j < numpages; j++) { /* - * Read the full oob until read_oob is fixed to - * handle single byte reads for 16 bit - * buswidth + * Read the full oob until read_oob is fixed to handle single + * byte reads for 16 bit buswidth. */ - ret = mtd->read_oob(mtd, offs, &ops); - if (ret) + ret = mtd_read_oob(mtd, offs, &ops); + /* Ignore ECC errors when checking for BBM */ + if (ret && !mtd_is_bitflip_or_eccerr(ret)) return ret; if (check_short_pattern(buf, bd)) @@ -491,86 +445,65 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, /** * create_bbt - [GENERIC] Create a bad block table by scanning the device - * @mtd: MTD device structure - * @buf: temporary buffer - * @bd: descriptor for the good/bad block search pattern - * @chip: create the table for a specific chip, -1 read all chips. - * Applies only if NAND_BBT_PERCHIP option is set + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @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 + * 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 nand_chip *this = mtd->priv; - int i, numblocks, len, scanlen; + int i, numblocks, numpages; int startblock; loff_t from; - size_t readlen; - printk(KERN_INFO "Scanning device for bad blocks\n"); + pr_info("Scanning device for bad blocks\n"); - if (bd->options & NAND_BBT_SCANALLPAGES) - len = 1 << (this->bbt_erase_shift - this->page_shift); - else if (bd->options & NAND_BBT_SCAN2NDPAGE) - len = 2; + if (bd->options & NAND_BBT_SCAN2NDPAGE) + numpages = 2; else - len = 1; - - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - /* We need only read few bytes from the OOB area */ - scanlen = 0; - readlen = bd->len; - } else { - /* Full page content should be read */ - scanlen = mtd->writesize + mtd->oobsize; - readlen = len * mtd->writesize; - } + numpages = 1; if (chip == -1) { - /* Note that numblocks is 2 * (real numblocks) here, see i+=2 - * below as it makes shifting and masking less painful */ - numblocks = mtd->size >> (this->bbt_erase_shift - 1); + numblocks = mtd->size >> this->bbt_erase_shift; startblock = 0; from = 0; } else { if (chip >= this->numchips) { - printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", + pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", chip + 1, this->numchips); return -EINVAL; } - numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + numblocks = this->chipsize >> this->bbt_erase_shift; startblock = chip * numblocks; numblocks += startblock; - from = (loff_t)startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << this->bbt_erase_shift; } - if (this->options & NAND_BBT_SCANLASTPAGE) - from += mtd->erasesize - (mtd->writesize * len); + if (this->bbt_options & NAND_BBT_SCANLASTPAGE) + from += mtd->erasesize - (mtd->writesize * numpages); - for (i = startblock; i < numblocks;) { + for (i = startblock; i < numblocks; i++) { int ret; BUG_ON(bd->options & NAND_BBT_NO_OOB); - if (bd->options & NAND_BBT_SCANALLPAGES) - ret = scan_block_full(mtd, bd, from, buf, readlen, - scanlen, len); - else - ret = scan_block_fast(mtd, bd, from, buf, len); - + ret = scan_block_fast(mtd, bd, from, buf, numpages); if (ret < 0) return ret; if (ret) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%012llx\n", - i >> 1, (unsigned long long)from); + bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); + pr_warn("Bad eraseblock %d at 0x%012llx\n", + i, (unsigned long long)from); mtd->ecc_stats.badblocks++; } - i += 2; from += (1 << this->bbt_erase_shift); } return 0; @@ -578,31 +511,29 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, /** * search_bbt - [GENERIC] scan the device for a specific bad block table - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table * - * Read the bad block table by searching for a given ident pattern. - * Search is preformed either from the beginning up or from the end of - * the device downwards. The search starts always at the start of a - * block. - * If the option NAND_BBT_PERCHIP is given, each chip is searched - * for a bbt, which contains the bad block information of this chip. - * This is necessary to provide support for certain DOC devices. + * Read the bad block table by searching for a given ident pattern. Search is + * preformed either from the beginning up or from the end of the device + * downwards. The search starts always at the start of a block. If the option + * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains + * the bad block information of this chip. This is necessary to provide support + * for certain DOC devices. * - * The bbt ident pattern resides in the oob area of the first page - * in a block. + * The bbt ident pattern resides in the oob area of the first page in a block. */ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) { struct nand_chip *this = mtd->priv; int i, chips; - int bits, startblock, block, dir; + int startblock, block, dir; int scanlen = mtd->writesize + mtd->oobsize; int bbtblocks; int blocktopage = this->bbt_erase_shift - this->page_shift; - /* Search direction top -> down ? */ + /* Search direction top -> down? */ if (td->options & NAND_BBT_LASTBLOCK) { startblock = (mtd->size >> this->bbt_erase_shift) - 1; dir = -1; @@ -611,7 +542,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr dir = 1; } - /* Do we have a bbt per chip ? */ + /* Do we have a bbt per chip? */ if (td->options & NAND_BBT_PERCHIP) { chips = this->numchips; bbtblocks = this->chipsize >> this->bbt_erase_shift; @@ -621,9 +552,6 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr bbtblocks = mtd->size >> this->bbt_erase_shift; } - /* Number of bits for each erase block in the bbt */ - bits = td->options & NAND_BBT_NRBITS_MSK; - for (i = 0; i < chips; i++) { /* Reset version information */ td->version[i] = 0; @@ -635,7 +563,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr loff_t offs = (loff_t)actblock << this->bbt_erase_shift; /* Read first page */ - scan_read_raw(mtd, buf, offs, mtd->writesize, td); + scan_read(mtd, buf, offs, mtd->writesize, td); if (!check_pattern(buf, scanlen, mtd->writesize, td)) { td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { @@ -650,24 +578,26 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* Check, if we found a bbt for each requested chip */ for (i = 0; i < chips; i++) { if (td->pages[i] == -1) - printk(KERN_WARNING "Bad block table not found for chip %d\n", i); + pr_warn("Bad block table not found for chip %d\n", i); else - printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], - td->version[i]); + pr_info("Bad block table found at page %d, version " + "0x%02X\n", td->pages[i], td->version[i]); } return 0; } /** * search_read_bbts - [GENERIC] scan the device for bad block table(s) - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror * - * Search and read the bad block table(s) -*/ -static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) + * Search and read the bad block table(s). + */ +static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, + struct nand_bbt_descr *md) { /* Search the primary table */ search_bbt(mtd, buf, td); @@ -675,32 +605,27 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt /* Search the mirror table */ if (md) search_bbt(mtd, buf, md); - - /* Force result check */ - return 1; } /** * write_bbt - [GENERIC] (Re)write the bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all * - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror - * @chipsel: selector for a specific chip, -1 for all - * - * (Re)write the bad block table - * -*/ + * (Re)write the bad block table. + */ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) { struct nand_chip *this = mtd->priv; struct erase_info einfo; - int i, j, res, chip = 0; + int i, res, chip = 0; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, bbtoffs, pageoffs, ooboffs; + int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; size_t retlen, len = 0; @@ -710,14 +635,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, ops.ooblen = mtd->oobsize; ops.ooboffs = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; + ops.mode = MTD_OPS_PLACE_OOB; if (!rcode) rcode = 0xff; - /* Write bad block table per chip rather than per device ? */ + /* Write bad block table per chip rather than per device? */ if (td->options & NAND_BBT_PERCHIP) { numblocks = (int)(this->chipsize >> this->bbt_erase_shift); - /* Full device write or specific chip ? */ + /* Full device write or specific chip? */ if (chipsel == -1) { nrchips = this->numchips; } else { @@ -731,8 +656,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, /* Loop through the chips */ for (; chip < nrchips; chip++) { - - /* There was already a version of the table, reuse the page + /* + * There was already a version of the table, reuse the page * This applies for absolute placement too, as we have the * page nr. in td->pages. */ @@ -741,8 +666,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, goto write; } - /* Automatic placement of the bad block table */ - /* Search direction top -> down ? */ + /* + * Automatic placement of the bad block table. Search direction + * top -> down? + */ if (td->options & NAND_BBT_LASTBLOCK) { startblock = numblocks * (chip + 1) - 1; dir = -1; @@ -754,10 +681,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, for (i = 0; i < td->maxblocks; i++) { int block = startblock + dir * i; /* Check, if the block is bad */ - switch ((this->bbt[block >> 2] >> - (2 * (block & 0x03))) & 0x03) { - case 0x01: - case 0x03: + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: continue; } page = block << @@ -766,7 +692,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (!md || md->pages[chip] != page) goto write; } - printk(KERN_ERR "No space left to write bad block table\n"); + pr_err("No space left to write bad block table\n"); return -ENOSPC; write: @@ -789,31 +715,27 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, default: return -EINVAL; } - bbtoffs = chip * (numblocks >> 2); - - to = ((loff_t) page) << this->page_shift; + to = ((loff_t)page) << this->page_shift; - /* Must we save the block contents ? */ + /* Must we save the block contents? */ if (td->options & NAND_BBT_SAVECONTENT) { /* Make it block aligned */ - to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); + to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1)); len = 1 << this->bbt_erase_shift; - res = mtd->read(mtd, to, len, &retlen, buf); + res = mtd_read(mtd, to, len, &retlen, buf); if (res < 0) { if (retlen != len) { - printk(KERN_INFO "nand_bbt: Error " - "reading block for writing " - "the bad block table\n"); + pr_info("nand_bbt: error reading block " + "for writing the bad block table\n"); return res; } - printk(KERN_WARNING "nand_bbt: ECC error " - "while reading block for writing " - "bad block table\n"); + pr_warn("nand_bbt: ECC error while reading " + "block for writing bad block table\n"); } /* Read oob data */ ops.ooblen = (len >> this->page_shift) * mtd->oobsize; ops.oobbuf = &buf[len]; - res = mtd->read_oob(mtd, to + mtd->writesize, &ops); + res = mtd_read_oob(mtd, to + mtd->writesize, &ops); if (res < 0 || ops.oobretlen != ops.ooblen) goto outerr; @@ -821,19 +743,19 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, pageoffs = page - (int)(to >> this->page_shift); offs = pageoffs << this->page_shift; /* Preset the bbt area with 0xff */ - memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); + memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); ooboffs = len + (pageoffs * mtd->oobsize); } else if (td->options & NAND_BBT_NO_OOB) { ooboffs = 0; offs = td->len; - /* the version byte */ + /* The version byte */ if (td->options & NAND_BBT_VERSION) offs++; /* Calc length */ - len = (size_t) (numblocks >> sft); + len = (size_t)(numblocks >> sft); len += offs; - /* Make it page aligned ! */ + /* Make it page aligned! */ len = ALIGN(len, mtd->writesize); /* Preset the buffer with 0xff */ memset(buf, 0xff, len); @@ -841,8 +763,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, memcpy(buf, td->pattern, td->len); } else { /* Calc length */ - len = (size_t) (numblocks >> sft); - /* Make it page aligned ! */ + len = (size_t)(numblocks >> sft); + /* Make it page aligned! */ len = ALIGN(len, mtd->writesize); /* Preset the buffer with 0xff */ memset(buf, 0xff, len + @@ -856,17 +778,13 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (td->options & NAND_BBT_VERSION) buf[ooboffs + td->veroffs] = td->version[chip]; - /* walk through the memory table */ - for (i = 0; i < numblocks;) { + /* Walk through the memory table */ + for (i = 0; i < numblocks; i++) { uint8_t dat; - dat = this->bbt[bbtoffs + (i >> 2)]; - for (j = 0; j < 4; j++, i++) { - int sftcnt = (i << (3 - sft)) & sftmsk; - /* Do not store the reserved bbt blocks ! */ - buf[offs + (i >> sft)] &= - ~(msk[dat & 0x03] << sftcnt); - dat >>= 2; - } + int sftcnt = (i << (3 - sft)) & sftmsk; + dat = bbt_get_entry(this, chip * numblocks + i); + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); } memset(&einfo, 0, sizeof(einfo)); @@ -883,8 +801,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (res < 0) goto outerr; - printk(KERN_DEBUG "Bad block table written to 0x%012llx, version " - "0x%02X\n", (unsigned long long)to, td->version[chip]); + pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", + (unsigned long long)to, td->version[chip]); /* Mark it as used */ td->pages[chip] = page; @@ -892,48 +810,45 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, return 0; outerr: - printk(KERN_WARNING - "nand_bbt: Error while writing bad block table %d\n", res); + pr_warn("nand_bbt: error while writing bad block table %d\n", res); return res; } /** * nand_memory_bbt - [GENERIC] create a memory based bad block table - * @mtd: MTD device structure - * @bd: descriptor for the good/bad block search pattern + * @mtd: MTD device structure + * @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 -*/ + * The function creates a memory based bbt by scanning the device for + * manufacturer / software marked good / bad blocks. + */ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->buffers->databuf, bd, -1); } /** * check_create - [GENERIC] create and write bbt(s) if necessary - * @mtd: MTD device structure - * @buf: temporary buffer - * @bd: descriptor for the good/bad block search pattern + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern * - * The function checks the results of the previous call to read_bbt - * and creates / updates the bbt(s) if necessary - * Creation is necessary if no bbt was found for the chip/device - * Update is necessary if one of the tables is missing or the - * version nr. of one table is less than the other -*/ + * The function checks the results of the previous call to read_bbt and creates + * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found + * for the chip/device. Update is necessary if one of the tables is missing or + * the version nr. of one table is less than the other. + */ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) { - int i, chips, writeops, chipsel, res; + int i, chips, writeops, create, chipsel, res, res2; struct nand_chip *this = mtd->priv; struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; struct nand_bbt_descr *rd, *rd2; - /* Do we have a bbt per chip ? */ + /* Do we have a bbt per chip? */ if (td->options & NAND_BBT_PERCHIP) chips = this->numchips; else @@ -941,86 +856,98 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc for (i = 0; i < chips; i++) { writeops = 0; + create = 0; rd = NULL; rd2 = NULL; - /* Per chip or per device ? */ + res = res2 = 0; + /* Per chip or per device? */ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; - /* Mirrored table avilable ? */ + /* Mirrored table available? */ if (md) { if (td->pages[i] == -1 && md->pages[i] == -1) { + create = 1; writeops = 0x03; - goto create; - } - - if (td->pages[i] == -1) { + } else if (td->pages[i] == -1) { rd = md; - td->version[i] = md->version[i]; - writeops = 1; - goto writecheck; - } - - if (md->pages[i] == -1) { + writeops = 0x01; + } else if (md->pages[i] == -1) { rd = td; - md->version[i] = td->version[i]; - writeops = 2; - goto writecheck; - } - - if (td->version[i] == md->version[i]) { + writeops = 0x02; + } else if (td->version[i] == md->version[i]) { rd = td; if (!(td->options & NAND_BBT_VERSION)) rd2 = md; - goto writecheck; - } - - if (((int8_t) (td->version[i] - md->version[i])) > 0) { + } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { rd = td; - md->version[i] = td->version[i]; - writeops = 2; + writeops = 0x02; } else { rd = md; - td->version[i] = md->version[i]; - writeops = 1; + writeops = 0x01; } - - goto writecheck; - } else { if (td->pages[i] == -1) { + create = 1; writeops = 0x01; - goto create; + } else { + rd = td; } - rd = td; - goto writecheck; } - create: - /* Create the bad block table by scanning the device ? */ - if (!(td->options & NAND_BBT_CREATE)) - continue; - /* Create the table in memory by scanning the chip(s) */ - if (!(this->options & NAND_CREATE_EMPTY_BBT)) - create_bbt(mtd, buf, bd, chipsel); - - td->version[i] = 1; - if (md) - md->version[i] = 1; - writecheck: - /* read back first ? */ - if (rd) - read_abs_bbt(mtd, buf, rd, chipsel); - /* If they weren't versioned, read both. */ - if (rd2) - read_abs_bbt(mtd, buf, rd2, chipsel); - - /* Write the bad block table to the device ? */ + if (create) { + /* Create the bad block table by scanning the device? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) + create_bbt(mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; + } + + /* Read back first? */ + if (rd) { + res = read_abs_bbt(mtd, buf, rd, chipsel); + if (mtd_is_eccerr(res)) { + /* Mark table as invalid */ + rd->pages[i] = -1; + rd->version[i] = 0; + i--; + continue; + } + } + /* If they weren't versioned, read both */ + if (rd2) { + res2 = read_abs_bbt(mtd, buf, rd2, chipsel); + if (mtd_is_eccerr(res2)) { + /* Mark table as invalid */ + rd2->pages[i] = -1; + rd2->version[i] = 0; + i--; + continue; + } + } + + /* Scrub the flash table(s)? */ + if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) + writeops = 0x03; + + /* Update version numbers before writing */ + if (md) { + td->version[i] = max(td->version[i], md->version[i]); + md->version[i] = td->version[i]; + } + + /* Write the bad block table to the device? */ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { res = write_bbt(mtd, buf, td, md, chipsel); if (res < 0) return res; } - /* Write the mirror bad block table to the device ? */ + /* Write the mirror bad block table to the device? */ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { res = write_bbt(mtd, buf, md, td, chipsel); if (res < 0) @@ -1032,20 +959,19 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc /** * mark_bbt_regions - [GENERIC] mark the bad block table regions - * @mtd: MTD device structure - * @td: bad block table descriptor + * @mtd: MTD device structure + * @td: bad block table descriptor * - * The bad block table regions are marked as "bad" to prevent - * accidental erasures / writes. The regions are identified by - * the mark 0x02. -*/ + * The bad block table regions are marked as "bad" to prevent accidental + * erasures / writes. The regions are identified by the mark 0x02. + */ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) { struct nand_chip *this = mtd->priv; int i, j, chips, block, nrblocks, update; - uint8_t oldval, newval; + uint8_t oldval; - /* Do we have a bbt per chip ? */ + /* Do we have a bbt per chip? */ if (td->options & NAND_BBT_PERCHIP) { chips = this->numchips; nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); @@ -1060,12 +986,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) if (td->pages[i] == -1) continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); - block <<= 1; - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if ((oldval != newval) && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if ((oldval != BBT_BLOCK_RESERVED) && + td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)block << + this->bbt_erase_shift); continue; } update = 0; @@ -1073,27 +999,28 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) block = ((i + 1) * nrblocks) - td->maxblocks; else block = i * nrblocks; - block <<= 1; for (j = 0; j < td->maxblocks; j++) { - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if (oldval != newval) + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if (oldval != BBT_BLOCK_RESERVED) update = 1; - block += 2; + block++; } - /* If we want reserved blocks to be recorded to flash, and some - new ones have been marked, then we need to update the stored - bbts. This should only happen once. */ + /* + * If we want reserved blocks to be recorded to flash, and some + * new ones have been marked, then we need to update the stored + * bbts. This should only happen once. + */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 1) << + this->bbt_erase_shift); } } /** * verify_bbt_descr - verify the bad block description - * @mtd: MTD device structure - * @bd: the table to verify + * @mtd: MTD device structure + * @bd: the table to verify * * This functions performs a few sanity checks on the bad block description * table. @@ -1101,22 +1028,26 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; - u32 pattern_len = bd->len; - u32 bits = bd->options & NAND_BBT_NRBITS_MSK; + u32 pattern_len; + u32 bits; u32 table_size; if (!bd) return; - BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) && - !(this->options & NAND_USE_FLASH_BBT)); + + pattern_len = bd->len; + bits = bd->options & NAND_BBT_NRBITS_MSK; + + BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && + !(this->bbt_options & NAND_BBT_USE_FLASH)); BUG_ON(!bits); if (bd->options & NAND_BBT_VERSION) pattern_len++; if (bd->options & NAND_BBT_NO_OOB) { - BUG_ON(!(this->options & NAND_USE_FLASH_BBT)); - BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB)); + BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); + BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); BUG_ON(bd->offs); if (bd->options & NAND_BBT_VERSION) BUG_ON(bd->veroffs != bd->len); @@ -1136,18 +1067,16 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) /** * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) - * @mtd: MTD device structure - * @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. + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern * - * The bad block table memory is allocated here. It must be freed - * by calling the nand_free_bbt function. + * 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 must be freed by calling + * the nand_free_bbt function. + */ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; @@ -1157,19 +1086,21 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) struct nand_bbt_descr *md = this->bbt_md; len = mtd->size >> (this->bbt_erase_shift + 2); - /* Allocate memory (2bit per block) and clear the memory bad block table */ + /* + * Allocate memory (2bit per block) and clear the memory bad block + * table. + */ this->bbt = kzalloc(len, GFP_KERNEL); - if (!this->bbt) { - printk(KERN_ERR "nand_scan_bbt: Out of memory\n"); + if (!this->bbt) return -ENOMEM; - } - /* If no primary table decriptor is given, scan the device - * to build a memory based bad block table + /* + * If no primary table decriptor is given, scan the device to build a + * memory based bad block table. */ if (!td) { if ((res = nand_memory_bbt(mtd, bd))) { - printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); + pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); kfree(this->bbt); this->bbt = NULL; } @@ -1183,22 +1114,20 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) len += (len >> this->page_shift) * mtd->oobsize; buf = vmalloc(len); if (!buf) { - printk(KERN_ERR "nand_bbt: Out of memory\n"); kfree(this->bbt); this->bbt = NULL; return -ENOMEM; } - /* Is the bbt at a given page ? */ + /* Is the bbt at a given page? */ if (td->options & NAND_BBT_ABSPAGE) { - res = read_abs_bbts(mtd, buf, td, md); + read_abs_bbts(mtd, buf, td, md); } else { /* Search the bad block table using a pattern in oob */ - res = search_read_bbts(mtd, buf, td, md); + search_read_bbts(mtd, buf, td, md); } - if (res) - res = check_create(mtd, buf, bd); + res = check_create(mtd, buf, bd); /* Prevent the bbt regions from erasing / writing */ mark_bbt_region(mtd, td); @@ -1210,16 +1139,16 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) } /** - * nand_update_bbt - [NAND Interface] update bad block table(s) - * @mtd: MTD device structure - * @offs: the offset of the newly marked block + * nand_update_bbt - update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block * - * The function updates the bad block table(s) -*/ -int nand_update_bbt(struct mtd_info *mtd, loff_t offs) + * The function updates the bad block table(s). + */ +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) { struct nand_chip *this = mtd->priv; - int len, res = 0, writeops = 0; + int len, res = 0; int chip, chipsel; uint8_t *buf; struct nand_bbt_descr *td = this->bbt_td; @@ -1232,14 +1161,10 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - printk(KERN_ERR "nand_update_bbt: Out of memory\n"); + if (!buf) return -ENOMEM; - } - - writeops = md != NULL ? 0x03 : 0x01; - /* Do we have a bbt per chip ? */ + /* Do we have a bbt per chip? */ if (td->options & NAND_BBT_PERCHIP) { chip = (int)(offs >> this->chip_shift); chipsel = chip; @@ -1252,14 +1177,14 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) if (md) md->version[chip]++; - /* Write the bad block table to the device ? */ - if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + /* Write the bad block table to the device? */ + if (td->options & NAND_BBT_WRITE) { res = write_bbt(mtd, buf, td, md, chipsel); if (res < 0) goto out; } - /* Write the mirror bad block table to the device ? */ - if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + /* Write the mirror bad block table to the device? */ + if (md && (md->options & NAND_BBT_WRITE)) { res = write_bbt(mtd, buf, md, td, chipsel); } @@ -1268,35 +1193,13 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) return res; } -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. */ +/* + * 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 smallpage_flashbased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = NAND_SMALL_BADBLOCK_POS, - .len = 1, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr largepage_flashbased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = NAND_LARGE_BADBLOCK_POS, - .len = 2, - .pattern = scan_ff_pattern -}; - -static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; - -static struct nand_bbt_descr agand_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, - .offs = 0x20, - .len = 6, - .pattern = scan_agand_pattern -}; - -/* Generic flash bbt decriptors -*/ +/* Generic flash bbt descriptors */ static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; @@ -1306,7 +1209,7 @@ static struct nand_bbt_descr bbt_main_descr = { .offs = 8, .len = 4, .veroffs = 12, - .maxblocks = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, .pattern = bbt_pattern }; @@ -1316,59 +1219,51 @@ static struct nand_bbt_descr bbt_mirror_descr = { .offs = 8, .len = 4, .veroffs = 12, - .maxblocks = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, .pattern = mirror_pattern }; -static struct nand_bbt_descr bbt_main_no_bbt_descr = { +static struct nand_bbt_descr bbt_main_no_oob_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP | NAND_BBT_NO_OOB, .len = 4, .veroffs = 4, - .maxblocks = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, .pattern = bbt_pattern }; -static struct nand_bbt_descr bbt_mirror_no_bbt_descr = { +static struct nand_bbt_descr bbt_mirror_no_oob_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP | NAND_BBT_NO_OOB, .len = 4, .veroffs = 4, - .maxblocks = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, .pattern = mirror_pattern }; -#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \ - NAND_BBT_SCANBYTE1AND6) +#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) /** - * nand_create_default_bbt_descr - [Internal] Creates a BBT descriptor structure - * @this: NAND chip to create descriptor for + * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure + * @this: NAND chip to create descriptor for * * This function allocates and initializes a nand_bbt_descr for BBM detection - * based on the properties of "this". The new descriptor is stored in + * based on the properties of @this. The new descriptor is stored in * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when * passed to this function. - * - * TODO: Handle other flags, replace other static structs - * (e.g. handle NAND_BBT_FLASH for flash-based BBT, - * replace smallpage_flashbased) - * */ -static int nand_create_default_bbt_descr(struct nand_chip *this) +static int nand_create_badblock_pattern(struct nand_chip *this) { struct nand_bbt_descr *bd; if (this->badblock_pattern) { - printk(KERN_WARNING "BBT descr already allocated; not replacing.\n"); + pr_warn("Bad block pattern already allocated; not replacing\n"); return -EINVAL; } bd = kzalloc(sizeof(*bd), GFP_KERNEL); - if (!bd) { - printk(KERN_ERR "nand_create_default_bbt_descr: Out of memory\n"); + if (!bd) return -ENOMEM; - } - bd->options = this->options & BBT_SCAN_OPTIONS; + bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; bd->offs = this->badblockpos; bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; bd->pattern = scan_ff_pattern; @@ -1379,87 +1274,91 @@ static int nand_create_default_bbt_descr(struct nand_chip *this) /** * nand_default_bbt - [NAND Interface] Select a default bad block table for the device - * @mtd: MTD device structure - * - * This function selects the default bad block table - * support for the device and calls the nand_scan_bbt function + * @mtd: MTD device structure * -*/ + * This function selects the default bad block table support for the device and + * calls the nand_scan_bbt function. + */ int nand_default_bbt(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; + int ret; - /* Default for AG-AND. We must use a flash based - * bad block table as the devices have factory marked - * _good_ blocks. Erasing those blocks leads to loss - * of the good / bad information, so we _must_ store - * this information in a good / bad table during - * startup - */ - if (this->options & NAND_IS_AND) { - /* Use the default pattern descriptors */ - if (!this->bbt_td) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - } - this->options |= NAND_USE_FLASH_BBT; - return nand_scan_bbt(mtd, &agand_flashbased); - } - - /* Is a flash based bad block table requested ? */ - if (this->options & NAND_USE_FLASH_BBT) { + /* Is a flash based bad block table requested? */ + if (this->bbt_options & NAND_BBT_USE_FLASH) { /* Use the default pattern descriptors */ if (!this->bbt_td) { - if (this->options & NAND_USE_FLASH_BBT_NO_OOB) { - this->bbt_td = &bbt_main_no_bbt_descr; - this->bbt_md = &bbt_mirror_no_bbt_descr; + if (this->bbt_options & NAND_BBT_NO_OOB) { + this->bbt_td = &bbt_main_no_oob_descr; + this->bbt_md = &bbt_mirror_no_oob_descr; } else { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; } } - if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; - } } else { this->bbt_td = NULL; this->bbt_md = NULL; - if (!this->badblock_pattern) - nand_create_default_bbt_descr(this); } + + if (!this->badblock_pattern) { + ret = nand_create_badblock_pattern(this); + if (ret) + return ret; + } + return nand_scan_bbt(mtd, this->badblock_pattern); } /** * nand_isbad_bbt - [NAND Interface] Check if a block is bad - * @mtd: MTD device structure - * @offs: offset in the device - * @allowbbt: allow access to bad block table region - * -*/ + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + */ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) { struct nand_chip *this = mtd->priv; - int block; - uint8_t res; + int block, res; - /* Get block number * 2 */ - block = (int)(offs >> (this->bbt_erase_shift - 1)); - res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + block = (int)(offs >> this->bbt_erase_shift); + res = bbt_get_entry(this, block); - DEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); + pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: " + "(block %d) 0x%02x\n", + (unsigned int)offs, block, res); - switch ((int)res) { - case 0x00: + switch (res) { + case BBT_BLOCK_GOOD: return 0; - case 0x01: + case BBT_BLOCK_WORN: return 1; - case 0x02: + case BBT_BLOCK_RESERVED: return allowbbt ? 0 : 1; } return 1; } +/** + * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT + * @mtd: MTD device structure + * @offs: offset of the bad block + */ +int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int block, ret = 0; + + block = (int)(offs >> this->bbt_erase_shift); + + /* Mark bad block in memory */ + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + /* Update flash-based bad block table */ + if (this->bbt_options & NAND_BBT_USE_FLASH) + ret = nand_update_bbt(mtd, offs); + + return ret; +} + EXPORT_SYMBOL(nand_scan_bbt); -EXPORT_SYMBOL(nand_default_bbt); diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c new file mode 100644 index 00000000000..3803e0bba23 --- /dev/null +++ b/drivers/mtd/nand/nand_bch.c @@ -0,0 +1,243 @@ +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> + * + * This file 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 or (at your option) any + * later version. + * + * This file 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 file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_bch.h> +#include <linux/bch.h> + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @ecclayout: private ecc layout for this BCH configuration + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + struct nand_ecclayout ecclayout; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + + memset(code, 0, chip->ecc.bytes); + encode_bch(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + + return 0; +} +EXPORT_SYMBOL(nand_bch_calculate_ecc); + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + + count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + pr_debug("%s: corrected bitflip %u\n", __func__, + errloc[i]); + } + } else if (count < 0) { + printk(KERN_ERR "ecc unrecoverable error\n"); + count = -1; + } + return count; +} +EXPORT_SYMBOL(nand_bch_correct_data); + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * @eccsize: ecc block size in bytes + * @eccbytes: ecc length in bytes + * @ecclayout: output default layout + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, + struct nand_ecclayout **ecclayout) +{ + unsigned int m, t, eccsteps, i; + struct nand_ecclayout *layout; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + + if (!eccsize || !eccbytes) { + printk(KERN_WARNING "ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = init_bch(m, t, 0); + if (!nbc->bch) + goto fail; + + /* 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); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* if no ecc placement scheme was provided, build one */ + if (!*ecclayout) { + + /* handle large page devices only */ + if (mtd->oobsize < 64) { + printk(KERN_WARNING "must provide an oob scheme for " + "oobsize %d\n", mtd->oobsize); + goto fail; + } + + layout = &nbc->ecclayout; + layout->eccbytes = eccsteps*eccbytes; + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + printk(KERN_WARNING "no suitable oob scheme available " + "for oobsize %d eccbytes %u\n", mtd->oobsize, + eccbytes); + goto fail; + } + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + + *ecclayout = layout; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + goto fail; + } + if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { + printk(KERN_WARNING "invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + memset(nbc->eccmask, 0, eccbytes); + encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} +EXPORT_SYMBOL(nand_bch_init); + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + free_bch(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} +EXPORT_SYMBOL(nand_bch_free); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); +MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/nand_bcm_umi.c b/drivers/mtd/nand/nand_bcm_umi.c deleted file mode 100644 index 46a6bc9c4b7..00000000000 --- a/drivers/mtd/nand/nand_bcm_umi.c +++ /dev/null @@ -1,149 +0,0 @@ -/***************************************************************************** -* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. -* -* Unless you and Broadcom execute a separate written software license -* agreement governing use of this software, this software is licensed to you -* under the terms of the GNU General Public License version 2, available at -* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -* -* Notwithstanding the above, under no circumstances may you combine this -* software in any way with any other Broadcom software provided under a -* license other than the GPL, without Broadcom's express prior written -* consent. -*****************************************************************************/ - -/* ---- Include Files ---------------------------------------------------- */ -#include <mach/reg_umi.h> -#include "nand_bcm_umi.h" -#ifdef BOOT0_BUILD -#include <uart.h> -#endif - -/* ---- External Variable Declarations ----------------------------------- */ -/* ---- External Function Prototypes ------------------------------------- */ -/* ---- Public Variables ------------------------------------------------- */ -/* ---- Private Constants and Types -------------------------------------- */ -/* ---- Private Function Prototypes -------------------------------------- */ -/* ---- Private Variables ------------------------------------------------ */ -/* ---- Private Functions ------------------------------------------------ */ - -#if NAND_ECC_BCH -/**************************************************************************** -* nand_bch_ecc_flip_bit - Routine to flip an errored bit -* -* PURPOSE: -* This is a helper routine that flips the bit (0 -> 1 or 1 -> 0) of the -* errored bit specified -* -* PARAMETERS: -* datap - Container that holds the 512 byte data -* errorLocation - Location of the bit that needs to be flipped -* -* RETURNS: -* None -****************************************************************************/ -static void nand_bcm_umi_bch_ecc_flip_bit(uint8_t *datap, int errorLocation) -{ - int locWithinAByte = (errorLocation & REG_UMI_BCH_ERR_LOC_BYTE) >> 0; - int locWithinAWord = (errorLocation & REG_UMI_BCH_ERR_LOC_WORD) >> 3; - int locWithinAPage = (errorLocation & REG_UMI_BCH_ERR_LOC_PAGE) >> 5; - - uint8_t errorByte = 0; - uint8_t byteMask = 1 << locWithinAByte; - - /* BCH uses big endian, need to change the location - * bits to little endian */ - locWithinAWord = 3 - locWithinAWord; - - errorByte = datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord]; - -#ifdef BOOT0_BUILD - puthexs("\nECC Correct Offset: ", - locWithinAPage * sizeof(uint32_t) + locWithinAWord); - puthexs(" errorByte:", errorByte); - puthex8(" Bit: ", locWithinAByte); -#endif - - if (errorByte & byteMask) { - /* bit needs to be cleared */ - errorByte &= ~byteMask; - } else { - /* bit needs to be set */ - errorByte |= byteMask; - } - - /* write back the value with the fixed bit */ - datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord] = errorByte; -} - -/**************************************************************************** -* nand_correct_page_bch - Routine to correct bit errors when reading NAND -* -* PURPOSE: -* This routine reads the BCH registers to determine if there are any bit -* errors during the read of the last 512 bytes of data + ECC bytes. If -* errors exists, the routine fixes it. -* -* PARAMETERS: -* datap - Container that holds the 512 byte data -* -* RETURNS: -* 0 or greater = Number of errors corrected -* (No errors are found or errors have been fixed) -* -1 = Error(s) cannot be fixed -****************************************************************************/ -int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData, - int numEccBytes) -{ - int numErrors; - int errorLocation; - int idx; - uint32_t regValue; - - /* wait for read ECC to be valid */ - regValue = nand_bcm_umi_bch_poll_read_ecc_calc(); - - /* - * read the control status register to determine if there - * are error'ed bits - * see if errors are correctible - */ - if ((regValue & REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR) > 0) { - int i; - - for (i = 0; i < numEccBytes; i++) { - if (readEccData[i] != 0xff) { - /* errors cannot be fixed, return -1 */ - return -1; - } - } - /* If ECC is unprogrammed then we can't correct, - * assume everything OK */ - return 0; - } - - if ((regValue & REG_UMI_BCH_CTRL_STATUS_CORR_ERR) == 0) { - /* no errors */ - return 0; - } - - /* - * Fix errored bits by doing the following: - * 1. Read the number of errors in the control and status register - * 2. Read the error location registers that corresponds to the number - * of errors reported - * 3. Invert the bit in the data - */ - numErrors = (regValue & REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR) >> 20; - - for (idx = 0; idx < numErrors; idx++) { - errorLocation = - REG_UMI_BCH_ERR_LOC_ADDR(idx) & REG_UMI_BCH_ERR_LOC_MASK; - - /* Flip bit */ - nand_bcm_umi_bch_ecc_flip_bit(datap, errorLocation); - } - /* Errors corrected */ - return numErrors; -} -#endif diff --git a/drivers/mtd/nand/nand_bcm_umi.h b/drivers/mtd/nand/nand_bcm_umi.h deleted file mode 100644 index 198b304d6f7..00000000000 --- a/drivers/mtd/nand/nand_bcm_umi.h +++ /dev/null @@ -1,337 +0,0 @@ -/***************************************************************************** -* Copyright 2003 - 2009 Broadcom Corporation. All rights reserved. -* -* Unless you and Broadcom execute a separate written software license -* agreement governing use of this software, this software is licensed to you -* under the terms of the GNU General Public License version 2, available at -* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -* -* Notwithstanding the above, under no circumstances may you combine this -* software in any way with any other Broadcom software provided under a -* license other than the GPL, without Broadcom's express prior written -* consent. -*****************************************************************************/ -#ifndef NAND_BCM_UMI_H -#define NAND_BCM_UMI_H - -/* ---- Include Files ---------------------------------------------------- */ -#include <mach/reg_umi.h> -#include <mach/reg_nand.h> -#include <cfg_global.h> - -/* ---- Constants and Types ---------------------------------------------- */ -#if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING) -#define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0) -#else -#define NAND_ECC_BCH 0 -#endif - -#define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13 - -#if NAND_ECC_BCH -#ifdef BOOT0_BUILD -#define NAND_ECC_NUM_BYTES 13 -#else -#define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES -#endif -#else -#define NAND_ECC_NUM_BYTES 3 -#endif - -#define NAND_DATA_ACCESS_SIZE 512 - -/* ---- Variable Externs ------------------------------------------ */ -/* ---- Function Prototypes --------------------------------------- */ -int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData, - int numEccBytes); - -/* Check in device is ready */ -static inline int nand_bcm_umi_dev_ready(void) -{ - return REG_UMI_NAND_RCSR & REG_UMI_NAND_RCSR_RDY; -} - -/* Wait until device is ready */ -static inline void nand_bcm_umi_wait_till_ready(void) -{ - while (nand_bcm_umi_dev_ready() == 0) - ; -} - -/* Enable Hamming ECC */ -static inline void nand_bcm_umi_hamming_enable_hwecc(void) -{ - /* disable and reset ECC, 512 byte page */ - REG_UMI_NAND_ECC_CSR &= ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE | - REG_UMI_NAND_ECC_CSR_256BYTE); - /* enable ECC */ - REG_UMI_NAND_ECC_CSR |= REG_UMI_NAND_ECC_CSR_ECC_ENABLE; -} - -#if NAND_ECC_BCH -/* BCH ECC specifics */ -#define ECC_BITS_PER_CORRECTABLE_BIT 13 - -/* Enable BCH Read ECC */ -static inline void nand_bcm_umi_bch_enable_read_hwecc(void) -{ - /* disable and reset ECC */ - REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; - /* Turn on ECC */ - REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; -} - -/* Enable BCH Write ECC */ -static inline void nand_bcm_umi_bch_enable_write_hwecc(void) -{ - /* disable and reset ECC */ - REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID; - /* Turn on ECC */ - REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN; -} - -/* Config number of BCH ECC bytes */ -static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes) -{ - uint32_t nValue; - uint32_t tValue; - uint32_t kValue; - uint32_t numBits = numEccBytes * 8; - - /* disable and reset ECC */ - REG_UMI_BCH_CTRL_STATUS = - REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID | - REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; - - /* Every correctible bit requires 13 ECC bits */ - tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT); - - /* Total data in number of bits for generating and computing BCH ECC */ - nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8; - - /* K parameter is used internally. K = N - (T * 13) */ - kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT); - - /* Write the settings */ - REG_UMI_BCH_N = nValue; - REG_UMI_BCH_T = tValue; - REG_UMI_BCH_K = kValue; -} - -/* Pause during ECC read calculation to skip bytes in OOB */ -static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void) -{ - REG_UMI_BCH_CTRL_STATUS = - REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN | - REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC; -} - -/* Resume during ECC read calculation after skipping bytes in OOB */ -static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void) -{ - REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; -} - -/* Poll read ECC calc to check when hardware completes */ -static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void) -{ - uint32_t regVal; - - do { - /* wait for ECC to be valid */ - regVal = REG_UMI_BCH_CTRL_STATUS; - } while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0); - - return regVal; -} - -/* Poll write ECC calc to check when hardware completes */ -static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void) -{ - /* wait for ECC to be valid */ - while ((REG_UMI_BCH_CTRL_STATUS & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID) - == 0) - ; -} - -/* Read the OOB and ECC, for kernel write OOB to a buffer */ -#if defined(__KERNEL__) && !defined(STANDALONE) -static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, - uint8_t *eccCalc, int numEccBytes, uint8_t *oobp) -#else -static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, - uint8_t *eccCalc, int numEccBytes) -#endif -{ - int eccPos = 0; - int numToRead = 16; /* There are 16 bytes per sector in the OOB */ - - /* ECC is already paused when this function is called */ - if (pageSize != NAND_DATA_ACCESS_SIZE) { - /* skip BI */ -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; -#else - REG_NAND_DATA8; -#endif - numToRead--; - } - - while (numToRead > numEccBytes) { - /* skip free oob region */ -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; -#else - REG_NAND_DATA8; -#endif - numToRead--; - } - - if (pageSize == NAND_DATA_ACCESS_SIZE) { - /* read ECC bytes before BI */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - - while (numToRead > 11) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } - - nand_bcm_umi_bch_pause_read_ecc_calc(); - - if (numToRead == 11) { - /* read BI */ -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; -#else - REG_NAND_DATA8; -#endif - numToRead--; - } - - } - /* read ECC bytes */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - while (numToRead) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } -} - -/* Helper function to write ECC */ -static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos, - uint8_t *oobp, uint8_t eccVal) -{ - if (eccBytePos <= numEccBytes) - *oobp = eccVal; -} - -/* Write OOB with ECC */ -static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize, - uint8_t *oobp, int numEccBytes) -{ - uint32_t eccVal = 0xffffffff; - - /* wait for write ECC to be valid */ - nand_bcm_umi_bch_poll_write_ecc_calc(); - - /* - ** Get the hardware ecc from the 32-bit result registers. - ** Read after 512 byte accesses. Format B3B2B1B0 - ** where B3 = ecc3, etc. - */ - - if (pageSize == NAND_DATA_ACCESS_SIZE) { - /* Now fill in the ECC bytes */ - if (numEccBytes >= 13) - eccVal = REG_UMI_BCH_WR_ECC_3; - - /* Usually we skip CM in oob[0,1] */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0], - (eccVal >> 16) & 0xff); - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1], - (eccVal >> 8) & 0xff); - - /* Write ECC in oob[2,3,4] */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2], - eccVal & 0xff); /* ECC 12 */ - - if (numEccBytes >= 9) - eccVal = REG_UMI_BCH_WR_ECC_2; - - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3], - (eccVal >> 24) & 0xff); /* ECC11 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4], - (eccVal >> 16) & 0xff); /* ECC10 */ - - /* Always Skip BI in oob[5] */ - } else { - /* Always Skip BI in oob[0] */ - - /* Now fill in the ECC bytes */ - if (numEccBytes >= 13) - eccVal = REG_UMI_BCH_WR_ECC_3; - - /* Usually skip CM in oob[1,2] */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1], - (eccVal >> 16) & 0xff); - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2], - (eccVal >> 8) & 0xff); - - /* Write ECC in oob[3-15] */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3], - eccVal & 0xff); /* ECC12 */ - - if (numEccBytes >= 9) - eccVal = REG_UMI_BCH_WR_ECC_2; - - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4], - (eccVal >> 24) & 0xff); /* ECC11 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5], - (eccVal >> 16) & 0xff); /* ECC10 */ - } - - /* Fill in the remainder of ECC locations */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6], - (eccVal >> 8) & 0xff); /* ECC9 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7], - eccVal & 0xff); /* ECC8 */ - - if (numEccBytes >= 5) - eccVal = REG_UMI_BCH_WR_ECC_1; - - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8], - (eccVal >> 24) & 0xff); /* ECC7 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9], - (eccVal >> 16) & 0xff); /* ECC6 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10], - (eccVal >> 8) & 0xff); /* ECC5 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11], - eccVal & 0xff); /* ECC4 */ - - if (numEccBytes >= 1) - eccVal = REG_UMI_BCH_WR_ECC_0; - - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12], - (eccVal >> 24) & 0xff); /* ECC3 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13], - (eccVal >> 16) & 0xff); /* ECC2 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14], - (eccVal >> 8) & 0xff); /* ECC1 */ - NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15], - eccVal & 0xff); /* ECC0 */ -} -#endif - -#endif /* NAND_BCM_UMI_H */ diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 271b8e735e8..97c4c0216c9 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -55,8 +55,7 @@ struct mtd_info; #define MODULE_AUTHOR(x) /* x */ #define MODULE_DESCRIPTION(x) /* x */ -#define printk printf -#define KERN_ERR "" +#define pr_err printf #endif /* @@ -110,7 +109,7 @@ static const char bitsperbyte[256] = { /* * addressbits is a lookup table to filter out the bits from the xor-ed - * ecc data that identify the faulty location. + * ECC data that identify the faulty location. * this is only used for repairing parity * see the comments in nand_correct_data for more details */ @@ -153,7 +152,7 @@ static const char addressbits[256] = { * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte * block * @buf: input buffer with raw data - * @eccsize: data bytes per ecc step (256 or 512) + * @eccsize: data bytes per ECC step (256 or 512) * @code: output buffer with ECC */ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, @@ -348,7 +347,7 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, rp17 = (par ^ rp16) & 0xff; /* - * Finally calculate the ecc bits. + * Finally calculate the ECC bits. * Again here it might seem that there are performance optimisations * possible, but benchmarks showed that on the system this is developed * the code below is the fastest @@ -436,7 +435,7 @@ EXPORT_SYMBOL(nand_calculate_ecc); * @buf: raw data read from the chip * @read_ecc: ECC from the chip * @calc_ecc: the ECC calculated from raw data - * @eccsize: data bytes per ecc step (256 or 512) + * @eccsize: data bytes per ECC step (256 or 512) * * Detect and correct a 1 bit error for eccsize byte block */ @@ -505,9 +504,9 @@ int __nand_correct_data(unsigned char *buf, } /* count nr of bits; use table lookup, faster than calculating it */ if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) - return 1; /* error in ecc data; no action needed */ + return 1; /* error in ECC data; no action needed */ - printk(KERN_ERR "uncorrectable error : "); + pr_err("%s: uncorrectable ECC error\n", __func__); return -1; } EXPORT_SYMBOL(__nand_correct_data); diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 00cf1b0d605..3d7c89fc103 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -10,162 +10,156 @@ */ #include <linux/module.h> #include <linux/mtd/nand.h> +#include <linux/sizes.h> + +#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + +#define SP_OPTIONS NAND_NEED_READRDY +#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16) + /* -* Chip ID list -* -* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, -* options -* -* Pagesize; 0, 256, 512 -* 0 get this information from the extended chip ID -+ 256 256 Byte page size -* 512 512 Byte page size -*/ + * The chip ID list: + * name, device ID, page size, chip size in MiB, eraseblock size, options + * + * If page size and eraseblock size are 0, the sizes are taken from the + * extended chip ID. + */ struct nand_flash_dev nand_flash_ids[] = { - -#ifdef CONFIG_MTD_NAND_MUSEUM_IDS - {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, - {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, - {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, - {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, - - {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, - {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, - {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, -#endif - - {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, - {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, - {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, - {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, - {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, - {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, - {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, - {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + /* + * Some incompatible NAND chips share device ID's and so must be + * listed by full ID. We list them first so that we can easily identify + * the most specific match. + */ + {"TC58NVG2S0F 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG3S0F 8G 3.3V 8-bit", + { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, + SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG5D2 32G 3.3V 8-bit", + { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, + SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"TC58NVG6D2 64G 3.3V 8-bit", + { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, + SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"SDTNRGAMA 64G 3.3V 8-bit", + { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, + + LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS), + + LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS), /* - * These are the new chips with large page size. The pagesize and the - * erasesize is determined from the extended id bytes + * These are the new chips with large page size. Their page size and + * eraseblock size are determined from the extended ID bytes. */ -#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) -#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) - /*512 Megabit */ - {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, - {"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16}, - {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, - {"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16}, + /* 512 Megabit */ + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16), /* 1 Gigabit */ - {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, - {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, - {"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16), /* 2 Gigabit */ - {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, - {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, - {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, - {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16), /* 4 Gigabit */ - {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, - {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, - {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, - {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16), /* 8 Gigabit */ - {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, - {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, - {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, - {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16), /* 16 Gigabit */ - {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, - {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, - {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, - {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16), /* 32 Gigabit */ - {"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS}, - {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, - {"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16}, - {"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16), /* 64 Gigabit */ - {"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS}, - {"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS}, - {"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16}, - {"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16), /* 128 Gigabit */ - {"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS}, - {"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS}, - {"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16}, - {"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16), /* 256 Gigabit */ - {"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS}, - {"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS}, - {"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16}, - {"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16), /* 512 Gigabit */ - {"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS}, - {"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS}, - {"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16}, - {"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), - /* - * Renesas AND 1 Gigabit. Those chips do not support extended id and - * have a strange page/block layout ! The chosen minimum erasesize is - * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page - * planes 1 block = 2 pages, but due to plane arrangement the blocks - * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would - * increase the eraseblock size so we chose a combined one which can be - * erased in one go There are more speed improvements for reads and - * writes possible, but not implemented now - */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, - NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | - BBT_AUTO_REFRESH - }, - - {NULL,} + {NULL} }; -/* -* Manufacturer ID list -*/ +/* Manufacturer IDs */ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_TOSHIBA, "Toshiba"}, {NAND_MFR_SAMSUNG, "Samsung"}, @@ -175,7 +169,11 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_HYNIX, "Hynix"}, {NAND_MFR_MICRON, "Micron"}, - {NAND_MFR_AMD, "AMD"}, + {NAND_MFR_AMD, "AMD/Spansion"}, + {NAND_MFR_MACRONIX, "Macronix"}, + {NAND_MFR_EON, "Eon"}, + {NAND_MFR_SANDISK, "SanDisk"}, + {NAND_MFR_INTEL, "Intel"}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index a5aa99f014b..4f0d83648e5 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -28,12 +28,13 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/vmalloc.h> -#include <asm/div64.h> +#include <linux/math64.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> +#include <linux/mtd/nand_bch.h> #include <linux/mtd/partitions.h> #include <linux/delay.h> #include <linux/list.h> @@ -41,6 +42,8 @@ #include <linux/sched.h> #include <linux/fs.h> #include <linux/pagemap.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -104,10 +107,10 @@ static char *weakblocks = NULL; static char *weakpages = NULL; static unsigned int bitflips = 0; static char *gravepages = NULL; -static unsigned int rptwear = 0; static unsigned int overridesize = 0; static char *cache_file = NULL; static unsigned int bbt; +static unsigned int bch; module_param(first_id_byte, uint, 0400); module_param(second_id_byte, uint, 0400); @@ -128,10 +131,10 @@ module_param(weakblocks, charp, 0400); module_param(weakpages, charp, 0400); module_param(bitflips, uint, 0400); module_param(gravepages, charp, 0400); -module_param(rptwear, uint, 0400); module_param(overridesize, uint, 0400); module_param(cache_file, charp, 0400); module_param(bbt, uint, 0400); +module_param(bch, uint, 0400); MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); @@ -159,12 +162,13 @@ MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (z MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" " separated by commas e.g. 1401:2 means page 1401" " can be read only twice before failing"); -MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero"); MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " "The size is specified in erase blocks and as the exponent of a power of two" " e.g. 5 means a size of 32 erase blocks"); MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area"); +MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " + "be correctable in 512-byte blocks"); /* The largest possible page size */ #define NS_LARGEST_PAGE_SIZE 4096 @@ -201,7 +205,7 @@ MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in d /* Calculate the page offset in flash RAM image by (row, column) address */ #define NS_RAW_OFFSET(ns) \ - (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column) + (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column) /* Calculate the OOB offset in flash RAM image by (row, column) address */ #define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) @@ -214,7 +218,6 @@ MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in d #define STATE_CMD_READOOB 0x00000005 /* read OOB area */ #define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ #define STATE_CMD_STATUS 0x00000007 /* read status */ -#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */ #define STATE_CMD_SEQIN 0x00000009 /* sequential data input */ #define STATE_CMD_READID 0x0000000A /* read ID */ #define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ @@ -259,15 +262,13 @@ MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in d #define NS_OPER_STATES 6 /* Maximum number of states in operation */ #define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ -#define OPT_PAGE256 0x00000001 /* 256-byte page chips */ #define OPT_PAGE512 0x00000002 /* 512-byte page chips */ #define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ #define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ -#define OPT_AUTOINCR 0x00000020 /* page number auto incrementation is possible */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ #define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ #define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ -#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */ +#define OPT_SMALLPAGE (OPT_PAGE512) /* 512-byte page chips */ /* Remove action bits from state */ #define NS_STATE(x) ((x) & ~ACTION_MASK) @@ -282,6 +283,11 @@ MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in d /* Maximum page cache pages needed to read or write a NAND page to the cache_file */ #define NS_MAX_HELD_PAGES 16 +struct nandsim_debug_info { + struct dentry *dfs_root; + struct dentry *dfs_wear_report; +}; + /* * A union to represent flash memory contents and flash buffer. */ @@ -330,7 +336,6 @@ struct nandsim { uint pgsec; /* number of pages per sector */ uint secshift; /* bits number in sector size */ uint pgshift; /* bits number in page size */ - uint oobshift; /* bits number in OOB size */ uint pgaddrbytes; /* bytes per page address */ uint secaddrbytes; /* bytes per sector address */ uint idbytes; /* the number ID bytes that this chip outputs */ @@ -357,10 +362,12 @@ struct nandsim { /* Fields needed when using a cache file */ struct file *cfile; /* Open file */ - unsigned char *pages_written; /* Which pages have been written */ + unsigned long *pages_written; /* Which pages have been written */ void *file_buf; struct page *held_pages[NS_MAX_HELD_PAGES]; int held_cnt; + + struct nandsim_debug_info dbg; }; /* @@ -396,8 +403,6 @@ static struct nandsim_operations { {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, /* Read status */ {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, - /* Read multi-plane status */ - {OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}}, /* Read ID */ {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, /* Large page devices read page */ @@ -438,12 +443,122 @@ static LIST_HEAD(grave_pages); static unsigned long *erase_block_wear = NULL; static unsigned int wear_eb_count = 0; static unsigned long total_wear = 0; -static unsigned int rptwear_cnt = 0; /* MTD structure for NAND controller */ static struct mtd_info *nsmtd; -static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; +static int nandsim_debugfs_show(struct seq_file *m, void *private) +{ + unsigned long wmin = -1, wmax = 0, avg; + unsigned long deciles[10], decile_max[10], tot = 0; + unsigned int i; + + /* Calc wear stats */ + for (i = 0; i < wear_eb_count; ++i) { + unsigned long wear = erase_block_wear[i]; + if (wear < wmin) + wmin = wear; + if (wear > wmax) + wmax = wear; + tot += wear; + } + + for (i = 0; i < 9; ++i) { + deciles[i] = 0; + decile_max[i] = (wmax * (i + 1) + 5) / 10; + } + deciles[9] = 0; + decile_max[9] = wmax; + for (i = 0; i < wear_eb_count; ++i) { + int d; + unsigned long wear = erase_block_wear[i]; + for (d = 0; d < 10; ++d) + if (wear <= decile_max[d]) { + deciles[d] += 1; + break; + } + } + avg = tot / wear_eb_count; + + /* Output wear report */ + seq_printf(m, "Total numbers of erases: %lu\n", tot); + seq_printf(m, "Number of erase blocks: %u\n", wear_eb_count); + seq_printf(m, "Average number of erases: %lu\n", avg); + seq_printf(m, "Maximum number of erases: %lu\n", wmax); + seq_printf(m, "Minimum number of erases: %lu\n", wmin); + for (i = 0; i < 10; ++i) { + unsigned long from = (i ? decile_max[i - 1] + 1 : 0); + if (from > decile_max[i]) + continue; + seq_printf(m, "Number of ebs with erase counts from %lu to %lu : %lu\n", + from, + decile_max[i], + deciles[i]); + } + + return 0; +} + +static int nandsim_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, nandsim_debugfs_show, inode->i_private); +} + +static const struct file_operations dfs_fops = { + .open = nandsim_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * nandsim_debugfs_create - initialize debugfs + * @dev: nandsim device description object + * + * This function creates all debugfs files for UBI device @ubi. Returns zero in + * case of success and a negative error code in case of failure. + */ +static int nandsim_debugfs_create(struct nandsim *dev) +{ + struct nandsim_debug_info *dbg = &dev->dbg; + struct dentry *dent; + int err; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + + dent = debugfs_create_dir("nandsim", NULL); + if (IS_ERR_OR_NULL(dent)) { + int err = dent ? -ENODEV : PTR_ERR(dent); + + NS_ERR("cannot create \"nandsim\" debugfs directory, err %d\n", + err); + return err; + } + dbg->dfs_root = dent; + + dent = debugfs_create_file("wear_report", S_IRUSR, + dbg->dfs_root, dev, &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + dbg->dfs_wear_report = dent; + + return 0; + +out_remove: + debugfs_remove_recursive(dbg->dfs_root); + err = dent ? PTR_ERR(dent) : -ENODEV; + return err; +} + +/** + * nandsim_debugfs_remove - destroy all debugfs files + */ +static void nandsim_debugfs_remove(struct nandsim *ns) +{ + if (IS_ENABLED(CONFIG_DEBUG_FS)) + debugfs_remove_recursive(ns->dbg.dfs_root); +} /* * Allocate array of page pointers, create slab allocation for an array @@ -460,17 +575,18 @@ static int alloc_device(struct nandsim *ns) cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); if (IS_ERR(cfile)) return PTR_ERR(cfile); - if (!cfile->f_op || (!cfile->f_op->read && !cfile->f_op->aio_read)) { + if (!(cfile->f_mode & FMODE_CAN_READ)) { NS_ERR("alloc_device: cache file not readable\n"); err = -EINVAL; goto err_close; } - if (!cfile->f_op->write && !cfile->f_op->aio_write) { + if (!(cfile->f_mode & FMODE_CAN_WRITE)) { NS_ERR("alloc_device: cache file not writeable\n"); err = -EINVAL; goto err_close; } - ns->pages_written = vzalloc(ns->geom.pgnum); + ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) * + sizeof(unsigned long)); if (!ns->pages_written) { NS_ERR("alloc_device: unable to allocate pages written array\n"); err = -ENOMEM; @@ -537,15 +653,7 @@ static void free_device(struct nandsim *ns) static char *get_partition_name(int i) { - char buf[64]; - sprintf(buf, "NAND simulator partition %d", i); - return kstrdup(buf, GFP_KERNEL); -} - -static uint64_t divide(uint64_t n, uint32_t d) -{ - do_div(n, d); - return n; + return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i); } /* @@ -576,20 +684,16 @@ static int init_nandsim(struct mtd_info *mtd) ns->geom.oobsz = mtd->oobsize; ns->geom.secsz = mtd->erasesize; ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; - ns->geom.pgnum = divide(ns->geom.totsz, ns->geom.pgsz); + ns->geom.pgnum = div_u64(ns->geom.totsz, ns->geom.pgsz); ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ns->geom.secshift = ffs(ns->geom.secsz) - 1; ns->geom.pgshift = chip->page_shift; - ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; ns->options = 0; - if (ns->geom.pgsz == 256) { - ns->options |= OPT_PAGE256; - } - else if (ns->geom.pgsz == 512) { - ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); + if (ns->geom.pgsz == 512) { + ns->options |= OPT_PAGE512; if (ns->busw == 8) ns->options |= OPT_PAGE512_8BIT; } else if (ns->geom.pgsz == 2048) { @@ -654,14 +758,6 @@ static int init_nandsim(struct mtd_info *mtd) ns->nbparts += 1; } - /* Detect how many ID bytes the NAND chip outputs */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (second_id_byte != nand_flash_ids[i].id) - continue; - if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) - ns->options |= OPT_AUTOINCR; - } - if (ns->busw == 16) NS_WARN("16-bit flashes support wasn't tested\n"); @@ -675,7 +771,7 @@ static int init_nandsim(struct mtd_info *mtd) printk("bus width: %u\n", ns->busw); printk("bits in sector size: %u\n", ns->geom.secshift); printk("bits in page size: %u\n", ns->geom.pgshift); - printk("bits in OOB size: %u\n", ns->geom.oobshift); + printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1); printk("flash size with OOB: %llu KiB\n", (unsigned long long)ns->geom.totszoob >> 10); printk("page address bytes: %u\n", ns->geom.pgaddrbytes); @@ -732,7 +828,7 @@ static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd) return -EINVAL; } offset = erase_block_no * ns->geom.secsz; - if (mtd->block_markbad(mtd, offset)) { + if (mtd_block_markbad(mtd, offset)) { NS_ERR("invalid badblocks.\n"); return -EINVAL; } @@ -917,9 +1013,7 @@ static int setup_wear_reporting(struct mtd_info *mtd) { size_t mem; - if (!rptwear) - return 0; - wear_eb_count = divide(mtd->size, mtd->erasesize); + wear_eb_count = div_u64(mtd->size, mtd->erasesize); mem = wear_eb_count * sizeof(unsigned long); if (mem / sizeof(unsigned long) != wear_eb_count) { NS_ERR("Too many erase blocks for wear reporting\n"); @@ -935,64 +1029,18 @@ static int setup_wear_reporting(struct mtd_info *mtd) static void update_wear(unsigned int erase_block_no) { - unsigned long wmin = -1, wmax = 0, avg; - unsigned long deciles[10], decile_max[10], tot = 0; - unsigned int i; - if (!erase_block_wear) return; total_wear += 1; + /* + * TODO: Notify this through a debugfs entry, + * instead of showing an error message. + */ if (total_wear == 0) NS_ERR("Erase counter total overflow\n"); erase_block_wear[erase_block_no] += 1; if (erase_block_wear[erase_block_no] == 0) NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); - rptwear_cnt += 1; - if (rptwear_cnt < rptwear) - return; - rptwear_cnt = 0; - /* Calc wear stats */ - for (i = 0; i < wear_eb_count; ++i) { - unsigned long wear = erase_block_wear[i]; - if (wear < wmin) - wmin = wear; - if (wear > wmax) - wmax = wear; - tot += wear; - } - for (i = 0; i < 9; ++i) { - deciles[i] = 0; - decile_max[i] = (wmax * (i + 1) + 5) / 10; - } - deciles[9] = 0; - decile_max[9] = wmax; - for (i = 0; i < wear_eb_count; ++i) { - int d; - unsigned long wear = erase_block_wear[i]; - for (d = 0; d < 10; ++d) - if (wear <= decile_max[d]) { - deciles[d] += 1; - break; - } - } - avg = tot / wear_eb_count; - /* Output wear report */ - NS_INFO("*** Wear Report ***\n"); - NS_INFO("Total numbers of erases: %lu\n", tot); - NS_INFO("Number of erase blocks: %u\n", wear_eb_count); - NS_INFO("Average number of erases: %lu\n", avg); - NS_INFO("Maximum number of erases: %lu\n", wmax); - NS_INFO("Minimum number of erases: %lu\n", wmin); - for (i = 0; i < 10; ++i) { - unsigned long from = (i ? decile_max[i - 1] + 1 : 0); - if (from > decile_max[i]) - continue; - NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n", - from, - decile_max[i], - deciles[i]); - } - NS_INFO("*** End of Wear Report ***\n"); } /* @@ -1015,8 +1063,6 @@ static char *get_state_name(uint32_t state) return "STATE_CMD_ERASE1"; case STATE_CMD_STATUS: return "STATE_CMD_STATUS"; - case STATE_CMD_STATUS_M: - return "STATE_CMD_STATUS_M"; case STATE_CMD_SEQIN: return "STATE_CMD_SEQIN"; case STATE_CMD_READID: @@ -1081,7 +1127,6 @@ static int check_command(int cmd) case NAND_CMD_RNDOUTSTART: return 0; - case NAND_CMD_STATUS_MULTI: default: return 1; } @@ -1107,8 +1152,6 @@ static uint32_t get_state_by_command(unsigned command) return STATE_CMD_ERASE1; case NAND_CMD_STATUS: return STATE_CMD_STATUS; - case NAND_CMD_STATUS_MULTI: - return STATE_CMD_STATUS_M; case NAND_CMD_SEQIN: return STATE_CMD_SEQIN; case NAND_CMD_READID: @@ -1344,40 +1387,32 @@ static void clear_memalloc(int memalloc) current->flags &= ~PF_MEMALLOC; } -static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) +static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) { - mm_segment_t old_fs; ssize_t tx; int err, memalloc; - err = get_pages(ns, file, count, *pos); + err = get_pages(ns, file, count, pos); if (err) return err; - old_fs = get_fs(); - set_fs(get_ds()); memalloc = set_memalloc(); - tx = vfs_read(file, (char __user *)buf, count, pos); + tx = kernel_read(file, pos, buf, count); clear_memalloc(memalloc); - set_fs(old_fs); put_pages(ns); return tx; } -static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) +static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) { - mm_segment_t old_fs; ssize_t tx; int err, memalloc; - err = get_pages(ns, file, count, *pos); + err = get_pages(ns, file, count, pos); if (err) return err; - old_fs = get_fs(); - set_fs(get_ds()); memalloc = set_memalloc(); - tx = vfs_write(file, (char __user *)buf, count, pos); + tx = kernel_write(file, buf, count, pos); clear_memalloc(memalloc); - set_fs(old_fs); put_pages(ns); return tx; } @@ -1398,29 +1433,26 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; } -int do_read_error(struct nandsim *ns, int num) +static int do_read_error(struct nandsim *ns, int num) { unsigned int page_no = ns->regs.row; if (read_error(page_no)) { - int i; - memset(ns->buf.byte, 0xFF, num); - for (i = 0; i < num; ++i) - ns->buf.byte[i] = random32(); + prandom_bytes(ns->buf.byte, num); NS_WARN("simulating read error in page %u\n", page_no); return 1; } return 0; } -void do_bit_flips(struct nandsim *ns, int num) +static void do_bit_flips(struct nandsim *ns, int num) { - if (bitflips && random32() < (1 << 22)) { + if (bitflips && prandom_u32() < (1 << 22)) { int flips = 1; if (bitflips > 1) - flips = (random32() % (int) bitflips) + 1; + flips = (prandom_u32() % (int) bitflips) + 1; while (flips--) { - int pos = random32() % (num * 8); + int pos = prandom_u32() % (num * 8); ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); NS_WARN("read_page: flipping bit %d in page %d " "reading from %d ecc: corrected=%u failed=%u\n", @@ -1438,7 +1470,7 @@ static void read_page(struct nandsim *ns, int num) union ns_mem *mypage; if (ns->cfile) { - if (!ns->pages_written[ns->regs.row]) { + if (!test_bit(ns->regs.row, ns->pages_written)) { NS_DBG("read_page: page %d not written\n", ns->regs.row); memset(ns->buf.byte, 0xFF, num); } else { @@ -1449,8 +1481,8 @@ static void read_page(struct nandsim *ns, int num) ns->regs.row, ns->regs.column + ns->regs.off); if (do_read_error(ns, num)) return; - pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; - tx = read_file(ns, ns->cfile, ns->buf.byte, num, &pos); + pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; + tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos); if (tx != num) { NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); return; @@ -1484,9 +1516,9 @@ static void erase_sector(struct nandsim *ns) if (ns->cfile) { for (i = 0; i < ns->geom.pgsec; i++) - if (ns->pages_written[ns->regs.row + i]) { + if (__test_and_clear_bit(ns->regs.row + i, + ns->pages_written)) { NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); - ns->pages_written[ns->regs.row + i] = 0; } return; } @@ -1512,20 +1544,19 @@ static int prog_page(struct nandsim *ns, int num) u_char *pg_off; if (ns->cfile) { - loff_t off, pos; + loff_t off; ssize_t tx; int all; NS_DBG("prog_page: writing page %d\n", ns->regs.row); pg_off = ns->file_buf + ns->regs.column + ns->regs.off; - off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; - if (!ns->pages_written[ns->regs.row]) { + off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; + if (!test_bit(ns->regs.row, ns->pages_written)) { all = 1; memset(ns->file_buf, 0xff, ns->geom.pgszoob); } else { all = 0; - pos = off; - tx = read_file(ns, ns->cfile, pg_off, num, &pos); + tx = read_file(ns, ns->cfile, pg_off, num, off); if (tx != num) { NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); return -1; @@ -1534,16 +1565,15 @@ static int prog_page(struct nandsim *ns, int num) for (i = 0; i < num; i++) pg_off[i] &= ns->buf.byte[i]; if (all) { - pos = (loff_t)ns->regs.row * ns->geom.pgszoob; - tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, &pos); + loff_t pos = (loff_t)ns->regs.row * ns->geom.pgszoob; + tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, pos); if (tx != ns->geom.pgszoob) { NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); return -1; } - ns->pages_written[ns->regs.row] = 1; + __set_bit(ns->regs.row, ns->pages_written); } else { - pos = off; - tx = write_file(ns, ns->cfile, pg_off, num, &pos); + tx = write_file(ns, ns->cfile, pg_off, num, off); if (tx != num) { NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); return -1; @@ -1931,20 +1961,8 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd) if (ns->regs.count == ns->regs.num) { NS_DBG("read_byte: all bytes were read\n"); - /* - * The OPT_AUTOINCR allows to read next consecutive pages without - * new read operation cycle. - */ - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); - } return outb; @@ -2198,33 +2216,13 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ns->regs.count += len; if (ns->regs.count == ns->regs.num) { - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); } return; } -static int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len); - - if (!memcmp(buf, &ns_verify_buf[0], len)) { - NS_DBG("verify_buf: the buffer is OK\n"); - return 0; - } else { - NS_DBG("verify_buf: the buffer is wrong\n"); - return -EFAULT; - } -} - /* * Module initialization function */ @@ -2259,7 +2257,6 @@ static int __init ns_init_module(void) chip->dev_ready = ns_device_ready; chip->write_buf = ns_nand_write_buf; chip->read_buf = ns_nand_read_buf; - chip->verify_buf = ns_nand_verify_buf; chip->read_word = ns_nand_read_word; chip->ecc.mode = NAND_ECC_SOFT; /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ @@ -2268,9 +2265,9 @@ static int __init ns_init_module(void) switch (bbt) { case 2: - chip->options |= NAND_USE_FLASH_BBT_NO_OOB; + chip->bbt_options |= NAND_BBT_NO_OOB; case 1: - chip->options |= NAND_USE_FLASH_BBT; + chip->bbt_options |= NAND_BBT_USE_FLASH; case 0: break; default: @@ -2288,7 +2285,7 @@ static int __init ns_init_module(void) nand->geom.idbytes = 2; nand->regs.status = NS_STATUS_OK(nand); nand->nxstate = STATE_UNKNOWN; - nand->options |= OPT_PAGE256; /* temporary value */ + nand->options |= OPT_PAGE512; /* temporary value */ nand->ids[0] = first_id_byte; nand->ids[1] = second_id_byte; nand->ids[2] = third_id_byte; @@ -2309,7 +2306,43 @@ static int __init ns_init_module(void) if ((retval = parse_gravepages()) != 0) goto error; - if ((retval = nand_scan(nsmtd, 1)) != 0) { + retval = nand_scan_ident(nsmtd, 1, NULL); + if (retval) { + NS_ERR("cannot scan NAND Simulator device\n"); + if (retval > 0) + retval = -ENXIO; + goto error; + } + + if (bch) { + unsigned int eccsteps, eccbytes; + if (!mtd_nand_has_bch()) { + NS_ERR("BCH ECC support is disabled\n"); + retval = -EINVAL; + goto error; + } + /* use 512-byte ecc blocks */ + eccsteps = nsmtd->writesize/512; + eccbytes = (bch*13+7)/8; + /* do not bother supporting small page devices */ + if ((nsmtd->oobsize < 64) || !eccsteps) { + NS_ERR("bch not available on small page devices\n"); + retval = -EINVAL; + goto error; + } + if ((eccbytes*eccsteps+2) > nsmtd->oobsize) { + NS_ERR("invalid bch value %u\n", bch); + retval = -EINVAL; + goto error; + } + chip->ecc.mode = NAND_ECC_SOFT_BCH; + chip->ecc.size = 512; + chip->ecc.bytes = eccbytes; + NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); + } + + retval = nand_scan_tail(nsmtd); + if (retval) { NS_ERR("can't register NAND Simulator\n"); if (retval > 0) retval = -ENXIO; @@ -2320,6 +2353,7 @@ static int __init ns_init_module(void) uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; if (new_size >> overridesize != nsmtd->erasesize) { NS_ERR("overridesize is too big\n"); + retval = -EINVAL; goto err_exit; } /* N.B. This relies on nand_scan not doing anything with the size before we change it */ @@ -2332,17 +2366,22 @@ static int __init ns_init_module(void) if ((retval = setup_wear_reporting(nsmtd)) != 0) goto err_exit; + if ((retval = nandsim_debugfs_create(nand)) != 0) + goto err_exit; + if ((retval = init_nandsim(nsmtd)) != 0) goto err_exit; - if ((retval = nand_default_bbt(nsmtd)) != 0) + if ((retval = chip->scan_bbt(nsmtd)) != 0) goto err_exit; if ((retval = parse_badblocks(nand, nsmtd)) != 0) goto err_exit; /* Register NAND partitions */ - if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0) + retval = mtd_device_register(nsmtd, &nand->partitions[0], + nand->nbparts); + if (retval != 0) goto err_exit; return 0; @@ -2369,6 +2408,7 @@ static void __exit ns_cleanup_module(void) struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv; int i; + nandsim_debugfs_remove(ns); free_nandsim(ns); /* Free nandsim private resources */ nand_release(nsmtd); /* Unregister driver */ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index c9ae0a5023b..69eaba690a9 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -30,9 +30,11 @@ #include <linux/mtd/ndfc.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <asm/io.h> +#define NDFC_MAX_CS 4 struct ndfc_controller { struct platform_device *ofdev; @@ -41,17 +43,15 @@ struct ndfc_controller { struct nand_chip chip; int chip_select; struct nand_hw_control ndfc_control; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *parts; -#endif }; -static struct ndfc_controller ndfc_ctrl; +static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS]; static void ndfc_select_chip(struct mtd_info *mtd, int chip) { uint32_t ccr; - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *nchip = mtd->priv; + struct ndfc_controller *ndfc = nchip->priv; ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); if (chip >= 0) { @@ -64,7 +64,8 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip) static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; if (cmd == NAND_CMD_NONE) return; @@ -77,7 +78,8 @@ static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) static int ndfc_ready(struct mtd_info *mtd) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; } @@ -85,7 +87,8 @@ static int ndfc_ready(struct mtd_info *mtd) static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) { uint32_t ccr; - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); ccr |= NDFC_CCR_RESET_ECC; @@ -96,7 +99,8 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) static int ndfc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; uint32_t ecc; uint8_t *p = (uint8_t *)&ecc; @@ -119,7 +123,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, */ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) @@ -128,39 +133,23 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); } -static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct ndfc_controller *ndfc = &ndfc_ctrl; - uint32_t *p = (uint32_t *) buf; - - for(;len > 0; len -= 4) - if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA)) - return -EFAULT; - return 0; -} - /* * Initialize chip structure */ static int ndfc_chip_init(struct ndfc_controller *ndfc, struct device_node *node) { -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS - static const char *part_types[] = { "cmdlinepart", NULL }; -#else - static const char *part_types[] = { NULL }; -#endif -#endif struct device_node *flash_np; struct nand_chip *chip = &ndfc->chip; + struct mtd_part_parser_data ppdata; int ret; chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; @@ -172,13 +161,14 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->controller = &ndfc->ndfc_control; chip->read_buf = ndfc_read_buf; chip->write_buf = ndfc_write_buf; - chip->verify_buf = ndfc_verify_buf; chip->ecc.correct = nand_correct_data; chip->ecc.hwctl = ndfc_enable_hwecc; chip->ecc.calculate = ndfc_calculate_ecc; chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; + chip->priv = ndfc; ndfc->mtd.priv = chip; ndfc->mtd.owner = THIS_MODULE; @@ -187,6 +177,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, if (!flash_np) return -ENODEV; + ppdata.of_node = flash_np; ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", dev_name(&ndfc->ofdev->dev), flash_np->name); if (!ndfc->mtd.name) { @@ -198,25 +189,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, if (ret) goto err; -#ifdef CONFIG_MTD_PARTITIONS - ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0); - if (ret < 0) - goto err; - -#ifdef CONFIG_MTD_OF_PARTS - if (ret == 0) { - ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np, - &ndfc->parts); - if (ret < 0) - goto err; - } -#endif - - if (ret > 0) - ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret); - else -#endif - ret = add_mtd_device(&ndfc->mtd); + ret = mtd_device_parse_register(&ndfc->mtd, NULL, &ppdata, NULL, 0); err: of_node_put(flash_np); @@ -225,18 +198,12 @@ err: return ret; } -static int __devinit ndfc_probe(struct platform_device *ofdev, - const struct of_device_id *match) +static int ndfc_probe(struct platform_device *ofdev) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct ndfc_controller *ndfc; const __be32 *reg; u32 ccr; - int err, len; - - spin_lock_init(&ndfc->ndfc_control.lock); - init_waitqueue_head(&ndfc->ndfc_control.wq); - ndfc->ofdev = ofdev; - dev_set_drvdata(&ofdev->dev, ndfc); + int err, len, cs; /* Read the reg property to get the chip select */ reg = of_get_property(ofdev->dev.of_node, "reg", &len); @@ -244,7 +211,20 @@ static int __devinit ndfc_probe(struct platform_device *ofdev, dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); return -ENOENT; } - ndfc->chip_select = be32_to_cpu(reg[0]); + + cs = be32_to_cpu(reg[0]); + if (cs >= NDFC_MAX_CS) { + dev_err(&ofdev->dev, "invalid CS number (%d)\n", cs); + return -EINVAL; + } + + ndfc = &ndfc_ctrl[cs]; + ndfc->chip_select = cs; + + spin_lock_init(&ndfc->ndfc_control.lock); + init_waitqueue_head(&ndfc->ndfc_control.wq); + ndfc->ofdev = ofdev; + dev_set_drvdata(&ofdev->dev, ndfc); ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); if (!ndfc->ndfcbase) { @@ -277,11 +257,12 @@ static int __devinit ndfc_probe(struct platform_device *ofdev, return 0; } -static int __devexit ndfc_remove(struct platform_device *ofdev) +static int ndfc_remove(struct platform_device *ofdev) { struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); nand_release(&ndfc->mtd); + kfree(ndfc->mtd.name); return 0; } @@ -292,28 +273,17 @@ static const struct of_device_id ndfc_match[] = { }; MODULE_DEVICE_TABLE(of, ndfc_match); -static struct of_platform_driver ndfc_driver = { +static struct platform_driver ndfc_driver = { .driver = { .name = "ndfc", .owner = THIS_MODULE, .of_match_table = ndfc_match, }, .probe = ndfc_probe, - .remove = __devexit_p(ndfc_remove), + .remove = ndfc_remove, }; -static int __init ndfc_nand_init(void) -{ - return of_register_platform_driver(&ndfc_driver); -} - -static void __exit ndfc_nand_exit(void) -{ - of_unregister_platform_driver(&ndfc_driver); -} - -module_init(ndfc_nand_init); -module_exit(ndfc_nand_exit); +module_platform_driver(ndfc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c deleted file mode 100644 index 8c0b6937522..00000000000 --- a/drivers/mtd/nand/nomadik_nand.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * drivers/mtd/nand/nomadik_nand.c - * - * Overview: - * Driver for on-board NAND flash on Nomadik Platforms - * - * Copyright © 2007 STMicroelectronics Pvt. Ltd. - * Author: Sachin Verma <sachin.verma@st.com> - * - * Copyright © 2009 Alessandro Rubini - * - * 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. - * - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/nand_ecc.h> -#include <linux/platform_device.h> -#include <linux/mtd/partitions.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <mach/nand.h> -#include <mach/fsmc.h> - -#include <mtd/mtd-abi.h> - -struct nomadik_nand_host { - struct mtd_info mtd; - struct nand_chip nand; - void __iomem *data_va; - void __iomem *cmd_va; - void __iomem *addr_va; - struct nand_bbt_descr *bbt_desc; -}; - -static struct nand_ecclayout nomadik_ecc_layout = { - .eccbytes = 3 * 4, - .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ - 0x02, 0x03, 0x04, - 0x12, 0x13, 0x14, - 0x22, 0x23, 0x24, - 0x32, 0x33, 0x34}, - /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */ - .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, -}; - -static void nomadik_ecc_control(struct mtd_info *mtd, int mode) -{ - /* No need to enable hw ecc, it's on by default */ -} - -static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *nand = mtd->priv; - struct nomadik_nand_host *host = nand->priv; - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, host->cmd_va); - else - writeb(cmd, host->addr_va); -} - -static int nomadik_nand_probe(struct platform_device *pdev) -{ - struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; - struct nomadik_nand_host *host; - struct mtd_info *mtd; - struct nand_chip *nand; - struct resource *res; - int ret = 0; - - /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL); - if (!host) { - dev_err(&pdev->dev, "Failed to allocate device structure.\n"); - return -ENOMEM; - } - - /* Call the client's init function, if any */ - if (pdata->init) - ret = pdata->init(); - if (ret < 0) { - dev_err(&pdev->dev, "Init function failed\n"); - goto err; - } - - /* ioremap three regions */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); - if (!res) { - ret = -EIO; - goto err_unmap; - } - host->addr_va = ioremap(res->start, resource_size(res)); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - if (!res) { - ret = -EIO; - goto err_unmap; - } - host->data_va = ioremap(res->start, resource_size(res)); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); - if (!res) { - ret = -EIO; - goto err_unmap; - } - host->cmd_va = ioremap(res->start, resource_size(res)); - - if (!host->addr_va || !host->data_va || !host->cmd_va) { - ret = -ENOMEM; - goto err_unmap; - } - - /* Link all private pointers */ - mtd = &host->mtd; - nand = &host->nand; - mtd->priv = nand; - nand->priv = host; - - host->mtd.owner = THIS_MODULE; - nand->IO_ADDR_R = host->data_va; - nand->IO_ADDR_W = host->data_va; - nand->cmd_ctrl = nomadik_cmd_ctrl; - - /* - * This stanza declares ECC_HW but uses soft routines. It's because - * HW claims to make the calculation but not the correction. However, - * I haven't managed to get the desired data out of it until now. - */ - nand->ecc.mode = NAND_ECC_SOFT; - nand->ecc.layout = &nomadik_ecc_layout; - nand->ecc.hwctl = nomadik_ecc_control; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - - nand->options = pdata->options; - - /* - * Scan to find existance of the device - */ - if (nand_scan(&host->mtd, 1)) { - ret = -ENXIO; - goto err_unmap; - } - -#ifdef CONFIG_MTD_PARTITIONS - add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts); -#else - pr_info("Registering %s as whole device\n", mtd->name); - add_mtd_device(mtd); -#endif - - platform_set_drvdata(pdev, host); - return 0; - - err_unmap: - if (host->cmd_va) - iounmap(host->cmd_va); - if (host->data_va) - iounmap(host->data_va); - if (host->addr_va) - iounmap(host->addr_va); - err: - kfree(host); - return ret; -} - -/* - * Clean up routine - */ -static int nomadik_nand_remove(struct platform_device *pdev) -{ - struct nomadik_nand_host *host = platform_get_drvdata(pdev); - struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; - - if (pdata->exit) - pdata->exit(); - - if (host) { - iounmap(host->cmd_va); - iounmap(host->data_va); - iounmap(host->addr_va); - kfree(host); - } - return 0; -} - -static int nomadik_nand_suspend(struct device *dev) -{ - struct nomadik_nand_host *host = dev_get_drvdata(dev); - int ret = 0; - if (host) - ret = host->mtd.suspend(&host->mtd); - return ret; -} - -static int nomadik_nand_resume(struct device *dev) -{ - struct nomadik_nand_host *host = dev_get_drvdata(dev); - if (host) - host->mtd.resume(&host->mtd); - return 0; -} - -static const struct dev_pm_ops nomadik_nand_pm_ops = { - .suspend = nomadik_nand_suspend, - .resume = nomadik_nand_resume, -}; - -static struct platform_driver nomadik_nand_driver = { - .probe = nomadik_nand_probe, - .remove = nomadik_nand_remove, - .driver = { - .owner = THIS_MODULE, - .name = "nomadik_nand", - .pm = &nomadik_nand_pm_ops, - }, -}; - -static int __init nand_nomadik_init(void) -{ - pr_info("Nomadik NAND driver\n"); - return platform_driver_register(&nomadik_nand_driver); -} - -static void __exit nand_nomadik_exit(void) -{ - platform_driver_unregister(&nomadik_nand_driver); -} - -module_init(nand_nomadik_init); -module_exit(nand_nomadik_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)"); -MODULE_DESCRIPTION("NAND driver for Nomadik Platform"); diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 6eddf7361ed..e8a5fffd6ab 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -10,7 +10,6 @@ */ #include <linux/slab.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -112,22 +111,6 @@ static void nuc900_nand_write_buf(struct mtd_info *mtd, write_data_reg(nand, buf[i]); } -static int nuc900_verify_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) -{ - int i; - struct nuc900_nand *nand; - - nand = container_of(mtd, struct nuc900_nand, mtd); - - for (i = 0; i < len; i++) { - if (buf[i] != (unsigned char)read_data_reg(nand)) - return -EFAULT; - } - - return 0; -} - static int nuc900_check_rb(struct nuc900_nand *nand) { unsigned int val; @@ -168,7 +151,8 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, if (column != -1 || page_addr != -1) { if (column != -1) { - if (chip->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; write_addr_reg(nand, column); write_addr_reg(nand, column >> 8 | ENDADDR); @@ -193,15 +177,6 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_SEQIN: case NAND_CMD_RNDIN: case NAND_CMD_STATUS: - case NAND_CMD_DEPLETE1: - return; - - case NAND_CMD_STATUS_ERROR: - case NAND_CMD_STATUS_ERROR0: - case NAND_CMD_STATUS_ERROR1: - case NAND_CMD_STATUS_ERROR2: - case NAND_CMD_STATUS_ERROR3: - udelay(chip->chip_delay); return; case NAND_CMD_RESET: @@ -250,7 +225,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand) val = __raw_readl(nand->reg + REG_FMICSR); if (!(val & NAND_EN)) - __raw_writel(val | NAND_EN, REG_FMICSR); + __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); val = __raw_readl(nand->reg + REG_SMCSR); @@ -262,16 +237,14 @@ static void nuc900_nand_enable(struct nuc900_nand *nand) spin_unlock(&nand->lock); } -static int __devinit nuc900_nand_probe(struct platform_device *pdev) +static int nuc900_nand_probe(struct platform_device *pdev) { struct nuc900_nand *nuc900_nand; struct nand_chip *chip; - int retval; struct resource *res; - retval = 0; - - nuc900_nand = kzalloc(sizeof(struct nuc900_nand), GFP_KERNEL); + nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand), + GFP_KERNEL); if (!nuc900_nand) return -ENOMEM; chip = &(nuc900_nand->chip); @@ -280,11 +253,9 @@ static int __devinit nuc900_nand_probe(struct platform_device *pdev) nuc900_nand->mtd.owner = THIS_MODULE; spin_lock_init(&nuc900_nand->lock); - nuc900_nand->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(nuc900_nand->clk)) { - retval = -ENOENT; - goto fail1; - } + nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(nuc900_nand->clk)) + return -ENOENT; clk_enable(nuc900_nand->clk); chip->cmdfunc = nuc900_nand_command_lp; @@ -292,89 +263,48 @@ static int __devinit nuc900_nand_probe(struct platform_device *pdev) chip->read_byte = nuc900_nand_read_byte; chip->write_buf = nuc900_nand_write_buf; chip->read_buf = nuc900_nand_read_buf; - chip->verify_buf = nuc900_verify_buf; chip->chip_delay = 50; chip->options = 0; chip->ecc.mode = NAND_ECC_SOFT; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - retval = -ENXIO; - goto fail1; - } - - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - retval = -EBUSY; - goto fail1; - } - - nuc900_nand->reg = ioremap(res->start, resource_size(res)); - if (!nuc900_nand->reg) { - retval = -ENOMEM; - goto fail2; - } + nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nuc900_nand->reg)) + return PTR_ERR(nuc900_nand->reg); nuc900_nand_enable(nuc900_nand); - if (nand_scan(&(nuc900_nand->mtd), 1)) { - retval = -ENXIO; - goto fail3; - } + if (nand_scan(&(nuc900_nand->mtd), 1)) + return -ENXIO; - add_mtd_partitions(&(nuc900_nand->mtd), partitions, - ARRAY_SIZE(partitions)); + mtd_device_register(&(nuc900_nand->mtd), partitions, + ARRAY_SIZE(partitions)); platform_set_drvdata(pdev, nuc900_nand); - return retval; - -fail3: iounmap(nuc900_nand->reg); -fail2: release_mem_region(res->start, resource_size(res)); -fail1: kfree(nuc900_nand); - return retval; + return 0; } -static int __devexit nuc900_nand_remove(struct platform_device *pdev) +static int nuc900_nand_remove(struct platform_device *pdev) { struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); - struct resource *res; - - iounmap(nuc900_nand->reg); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); + nand_release(&nuc900_nand->mtd); clk_disable(nuc900_nand->clk); - clk_put(nuc900_nand->clk); - - kfree(nuc900_nand); - - platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver nuc900_nand_driver = { .probe = nuc900_nand_probe, - .remove = __devexit_p(nuc900_nand_remove), + .remove = nuc900_nand_remove, .driver = { .name = "nuc900-fmi", .owner = THIS_MODULE, }, }; -static int __init nuc900_nand_init(void) -{ - return platform_driver_register(&nuc900_nand_driver); -} - -static void __exit nuc900_nand_exit(void) -{ - platform_driver_unregister(&nuc900_nand_driver); -} - -module_init(nuc900_nand_init); -module_exit(nuc900_nand_exit); +module_platform_driver(nuc900_nand_driver); MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 15682ec8530..f0ed92e210a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -9,21 +9,29 @@ */ #include <linux/platform_device.h> +#include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/module.h> +#include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/sched.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/omap-dma.h> #include <linux/io.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> -#include <plat/dma.h> -#include <plat/gpmc.h> -#include <plat/nand.h> +#include <linux/mtd/nand_bch.h> +#include <linux/platform_data/elm.h> + +#include <linux/platform_data/mtd-nand-omap2.h> #define DRIVER_NAME "omap2-nand" +#define OMAP_NAND_TIMEOUT_MS 5000 #define NAND_Ecc_P1e (1 << 0) #define NAND_Ecc_P2e (1 << 1) @@ -92,46 +100,139 @@ #define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) #define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL }; +#define PREFETCH_CONFIG1_CS_SHIFT 24 +#define ECC_CONFIG_CS_SHIFT 1 +#define CS_MASK 0x7 +#define ENABLE_PREFETCH (0x1 << 7) +#define DMA_MPU_MODE_SHIFT 2 +#define ECCSIZE0_SHIFT 12 +#define ECCSIZE1_SHIFT 22 +#define ECC1RESULTSIZE 0x1 +#define ECCCLEAR 0x100 +#define ECC1 0x1 +#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 +#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) +#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) +#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) +#define STATUS_BUFF_EMPTY 0x00000001 + +#define OMAP24XX_DMA_GPMC 4 + +#define SECTOR_BYTES 512 +/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ +#define BCH4_BIT_PAD 4 + +/* GPMC ecc engine settings for read */ +#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ +#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */ +#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */ +#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */ +#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */ + +/* GPMC ecc engine settings for write */ +#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ +#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ +#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ + +#define BADBLOCK_MARKER_LENGTH 2 + +#ifdef CONFIG_MTD_NAND_OMAP_BCH +static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, + 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, + 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, + 0x07, 0x0e}; +static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, + 0xac, 0x6b, 0xff, 0x99, 0x7b}; +static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; #endif -#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH -static int use_prefetch = 1; - -/* "modprobe ... use_prefetch=0" etc */ -module_param(use_prefetch, bool, 0); -MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); - -#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA -static int use_dma = 1; - -/* "modprobe ... use_dma=0" etc */ -module_param(use_dma, bool, 0); -MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); -#else -static const int use_dma; -#endif -#else -const int use_prefetch; -static const int use_dma; -#endif +/* oob info generated runtime depending on ecc algorithm and layout selected */ +static struct nand_ecclayout omap_oobinfo; struct omap_nand_info { struct nand_hw_control controller; struct omap_nand_platform_data *pdata; struct mtd_info mtd; - struct mtd_partition *parts; struct nand_chip nand; struct platform_device *pdev; int gpmc_cs; unsigned long phys_base; + enum omap_ecc ecc_opt; struct completion comp; - int dma_ch; + struct dma_chan *dma; + int gpmc_irq_fifo; + int gpmc_irq_count; + enum { + OMAP_NAND_IO_READ = 0, /* read */ + OMAP_NAND_IO_WRITE, /* write */ + } iomode; + u_char *buf; + int buf_len; + struct gpmc_nand_regs reg; + /* fields specific for BCHx_HW ECC scheme */ + struct device *elm_dev; + struct device_node *of_node; }; /** + * omap_prefetch_enable - configures and starts prefetch transfer + * @cs: cs (chip select) number + * @fifo_th: fifo threshold to be used for read/ write + * @dma_mode: dma mode enable (1) or disable (0) + * @u32_count: number of bytes to be transferred + * @is_write: prefetch read(0) or write post(1) mode + */ +static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, + unsigned int u32_count, int is_write, struct omap_nand_info *info) +{ + u32 val; + + if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) + return -1; + + if (readl(info->reg.gpmc_prefetch_control)) + return -EBUSY; + + /* Set the amount of bytes to be prefetched */ + writel(u32_count, info->reg.gpmc_prefetch_config2); + + /* Set dma/mpu mode, the prefetch read / post write and + * enable the engine. Set which cs is has requested for. + */ + val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) | + PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | + (dma_mode << DMA_MPU_MODE_SHIFT) | (0x1 & is_write)); + writel(val, info->reg.gpmc_prefetch_config1); + + /* Start the prefetch engine */ + writel(0x1, info->reg.gpmc_prefetch_control); + + return 0; +} + +/** + * omap_prefetch_reset - disables and stops the prefetch engine + */ +static int omap_prefetch_reset(int cs, struct omap_nand_info *info) +{ + u32 config1; + + /* check if the same module/cs is trying to reset */ + config1 = readl(info->reg.gpmc_prefetch_config1); + if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs) + return -EINVAL; + + /* Stop the PFPW engine */ + writel(0x0, info->reg.gpmc_prefetch_control); + + /* Reset/disable the PFPW engine */ + writel(0x0, info->reg.gpmc_prefetch_config1); + + return 0; +} + +/** * omap_hwcontrol - hardware specific access to control-lines * @mtd: MTD device structure * @cmd: command to device @@ -149,13 +250,13 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) if (cmd != NAND_CMD_NONE) { if (ctrl & NAND_CLE) - gpmc_nand_write(info->gpmc_cs, GPMC_NAND_COMMAND, cmd); + writeb(cmd, info->reg.gpmc_nand_command); else if (ctrl & NAND_ALE) - gpmc_nand_write(info->gpmc_cs, GPMC_NAND_ADDRESS, cmd); + writeb(cmd, info->reg.gpmc_nand_address); else /* NAND_NCE */ - gpmc_nand_write(info->gpmc_cs, GPMC_NAND_DATA, cmd); + writeb(cmd, info->reg.gpmc_nand_data); } } @@ -189,7 +290,8 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) iowrite8(*p++, info->nand.IO_ADDR_W); /* wait until buffer is available for write */ do { - status = gpmc_read_status(GPMC_STATUS_BUFFER); + status = readl(info->reg.gpmc_status) & + STATUS_BUFF_EMPTY; } while (!status); } } @@ -226,7 +328,8 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) iowrite16(*p++, info->nand.IO_ADDR_W); /* wait until buffer is available for write */ do { - status = gpmc_read_status(GPMC_STATUS_BUFFER); + status = readl(info->reg.gpmc_status) & + STATUS_BUFF_EMPTY; } while (!status); } } @@ -256,24 +359,25 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) } /* configure and start prefetch transfer */ - ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info); if (ret) { /* PFPW engine is busy, use cpu copy method */ if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, buf, len); + omap_read_buf16(mtd, (u_char *)p, len); else - omap_read_buf8(mtd, buf, len); + omap_read_buf8(mtd, (u_char *)p, len); } else { - p = (u32 *) buf; do { - r_count = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT); + r_count = readl(info->reg.gpmc_prefetch_status); + r_count = PREFETCH_STATUS_FIFO_CNT(r_count); r_count = r_count >> 2; ioread32_rep(info->nand.IO_ADDR_R, p, r_count); p += r_count; len -= r_count << 2; } while (len); /* disable and stop the PFPW engine */ - gpmc_prefetch_reset(info->gpmc_cs); + omap_prefetch_reset(info->gpmc_cs, info); } } @@ -288,9 +392,11 @@ static void omap_write_buf_pref(struct mtd_info *mtd, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - uint32_t pref_count = 0, w_count = 0; + uint32_t w_count = 0; int i = 0, ret = 0; - u16 *p; + u16 *p = (u16 *)buf; + unsigned long tim, limit; + u32 val; /* take care of subpage writes */ if (len % 2 != 0) { @@ -300,44 +406,48 @@ static void omap_write_buf_pref(struct mtd_info *mtd, } /* configure and start prefetch transfer */ - ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); if (ret) { /* PFPW engine is busy, use cpu copy method */ if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, buf, len); + omap_write_buf16(mtd, (u_char *)p, len); else - omap_write_buf8(mtd, buf, len); + omap_write_buf8(mtd, (u_char *)p, len); } else { - p = (u16 *) buf; while (len) { - w_count = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT); + w_count = readl(info->reg.gpmc_prefetch_status); + w_count = PREFETCH_STATUS_FIFO_CNT(w_count); w_count = w_count >> 1; for (i = 0; (i < w_count) && len; i++, len -= 2) iowrite16(*p++, info->nand.IO_ADDR_W); } /* wait for data to flushed-out before reset the prefetch */ + tim = 0; + limit = (loops_per_jiffy * + msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); do { - pref_count = gpmc_read_status(GPMC_PREFETCH_COUNT); - } while (pref_count); + cpu_relax(); + val = readl(info->reg.gpmc_prefetch_status); + val = PREFETCH_STATUS_COUNT(val); + } while (val && (tim++ < limit)); + /* disable and stop the PFPW engine */ - gpmc_prefetch_reset(info->gpmc_cs); + omap_prefetch_reset(info->gpmc_cs, info); } } -#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA /* - * omap_nand_dma_cb: callback on the completion of dma transfer - * @lch: logical channel - * @ch_satuts: channel status + * omap_nand_dma_callback: callback on the completion of dma transfer * @data: pointer to completion data structure */ -static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) +static void omap_nand_dma_callback(void *data) { complete((struct completion *) data); } /* - * omap_nand_dma_transfer: configer and start dma transfer + * omap_nand_dma_transfer: configure and start dma transfer * @mtd: MTD device structure * @addr: virtual address in RAM of source/destination * @len: number of data bytes to be transferred @@ -348,16 +458,14 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - uint32_t prefetch_status = 0; + struct dma_async_tx_descriptor *tx; enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - dma_addr_t dma_addr; + struct scatterlist sg; + unsigned long tim, limit; + unsigned n; int ret; - - /* The fifo depth is 64 bytes. We have a sync at each frame and frame - * length is 64 bytes. - */ - int buf_len = len >> 6; + u32 val; if (addr >= high_memory) { struct page *p1; @@ -371,52 +479,53 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK); } - dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir); - if (dma_mapping_error(&info->pdev->dev, dma_addr)) { + sg_init_one(&sg, addr, len); + n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); + if (n == 0) { dev_err(&info->pdev->dev, "Couldn't DMA map a %d byte buffer\n", len); goto out_copy; } - if (is_write) { - omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, - info->phys_base, 0, 0); - omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr, 0, 0); - omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, - 0x10, buf_len, OMAP_DMA_SYNC_FRAME, - OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC); - } else { - omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, - info->phys_base, 0, 0); - omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr, 0, 0); - omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, - 0x10, buf_len, OMAP_DMA_SYNC_FRAME, - OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC); - } + tx = dmaengine_prep_slave_sg(info->dma, &sg, n, + is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx) + goto out_copy_unmap; + + tx->callback = omap_nand_dma_callback; + tx->callback_param = &info->comp; + dmaengine_submit(tx); + /* configure and start prefetch transfer */ - ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write); + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); if (ret) - /* PFPW engine is busy, use cpu copy methode */ - goto out_copy; + /* PFPW engine is busy, use cpu copy method */ + goto out_copy_unmap; init_completion(&info->comp); - - omap_start_dma(info->dma_ch); + dma_async_issue_pending(info->dma); /* setup and start DMA using dma_addr */ wait_for_completion(&info->comp); + tim = 0; + limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); do { - prefetch_status = gpmc_read_status(GPMC_PREFETCH_COUNT); - } while (prefetch_status); + cpu_relax(); + val = readl(info->reg.gpmc_prefetch_status); + val = PREFETCH_STATUS_COUNT(val); + } while (val && (tim++ < limit)); + /* disable and stop the PFPW engine */ - gpmc_prefetch_reset(info->gpmc_cs); + omap_prefetch_reset(info->gpmc_cs, info); - dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); + dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); return 0; +out_copy_unmap: + dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); out_copy: if (info->nand.options & NAND_BUSWIDTH_16) is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) @@ -426,14 +535,6 @@ out_copy: : omap_write_buf8(mtd, (u_char *) addr, len); return 0; } -#else -static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {} -static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, - unsigned int len, int is_write) -{ - return 0; -} -#endif /** * omap_read_buf_dma_pref - read data from NAND controller into buffer @@ -466,28 +567,157 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd, omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); } -/** - * omap_verify_buf - Verify chip data against buffer +/* + * omap_nand_irq - GPMC irq handler + * @this_irq: gpmc irq number + * @dev: omap_nand_info structure pointer is passed here + */ +static irqreturn_t omap_nand_irq(int this_irq, void *dev) +{ + struct omap_nand_info *info = (struct omap_nand_info *) dev; + u32 bytes; + + bytes = readl(info->reg.gpmc_prefetch_status); + bytes = PREFETCH_STATUS_FIFO_CNT(bytes); + bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */ + if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */ + if (this_irq == info->gpmc_irq_count) + goto done; + + if (info->buf_len && (info->buf_len < bytes)) + bytes = info->buf_len; + else if (!info->buf_len) + bytes = 0; + iowrite32_rep(info->nand.IO_ADDR_W, + (u32 *)info->buf, bytes >> 2); + info->buf = info->buf + bytes; + info->buf_len -= bytes; + + } else { + ioread32_rep(info->nand.IO_ADDR_R, + (u32 *)info->buf, bytes >> 2); + info->buf = info->buf + bytes; + + if (this_irq == info->gpmc_irq_count) + goto done; + } + + return IRQ_HANDLED; + +done: + complete(&info->comp); + + disable_irq_nosync(info->gpmc_irq_fifo); + disable_irq_nosync(info->gpmc_irq_count); + + return IRQ_HANDLED; +} + +/* + * omap_read_buf_irq_pref - read data from NAND controller into buffer * @mtd: MTD device structure - * @buf: buffer containing the data to compare - * @len: number of bytes to compare + * @buf: buffer to store date + * @len: number of bytes to read */ -static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) +static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len) { - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - u16 *p = (u16 *) buf; + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + int ret = 0; - len >>= 1; - while (len--) { - if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R))) - return -EFAULT; + if (len <= mtd->oobsize) { + omap_read_buf_pref(mtd, buf, len); + return; } - return 0; + info->iomode = OMAP_NAND_IO_READ; + info->buf = buf; + init_completion(&info->comp); + + /* configure and start prefetch transfer */ + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); + if (ret) + /* PFPW engine is busy, use cpu copy method */ + goto out_copy; + + info->buf_len = len; + + enable_irq(info->gpmc_irq_count); + enable_irq(info->gpmc_irq_fifo); + + /* waiting for read to complete */ + wait_for_completion(&info->comp); + + /* disable and stop the PFPW engine */ + omap_prefetch_reset(info->gpmc_cs, info); + return; + +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len); + else + omap_read_buf8(mtd, buf, len); } -#ifdef CONFIG_MTD_NAND_OMAP_HWECC +/* + * omap_write_buf_irq_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_irq_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + int ret = 0; + unsigned long tim, limit; + u32 val; + + if (len <= mtd->oobsize) { + omap_write_buf_pref(mtd, buf, len); + return; + } + + info->iomode = OMAP_NAND_IO_WRITE; + info->buf = (u_char *) buf; + init_completion(&info->comp); + + /* configure and start prefetch transfer : size=24 */ + ret = omap_prefetch_enable(info->gpmc_cs, + (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); + if (ret) + /* PFPW engine is busy, use cpu copy method */ + goto out_copy; + + info->buf_len = len; + + enable_irq(info->gpmc_irq_count); + enable_irq(info->gpmc_irq_fifo); + + /* waiting for write to complete */ + wait_for_completion(&info->comp); + + /* wait for data to flushed-out before reset the prefetch */ + tim = 0; + limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); + do { + val = readl(info->reg.gpmc_prefetch_status); + val = PREFETCH_STATUS_COUNT(val); + cpu_relax(); + } while (val && (tim++ < limit)); + + /* disable and stop the PFPW engine */ + omap_prefetch_reset(info->gpmc_cs, info); + return; + +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + omap_write_buf16(mtd, buf, len); + else + omap_write_buf8(mtd, buf, len); +} /** * gen_true_ecc - This function will generate true ECC value @@ -517,6 +747,8 @@ static void gen_true_ecc(u8 *ecc_buf) * * This function compares two ECC's and indicates if there is an error. * If the error can be corrected it will be corrected to the buffer. + * If there is no error, %0 is returned. If there is an error but it + * was corrected, %1 is returned. Otherwise, %-1 is returned. */ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ u8 *ecc_data2, /* read from register */ @@ -595,12 +827,12 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ case 1: /* Uncorrectable error */ - DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); + pr_debug("ECC UNCORRECTED_ERROR 1\n"); return -1; case 11: /* UN-Correctable error */ - DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n"); + pr_debug("ECC UNCORRECTED_ERROR B\n"); return -1; case 12: @@ -617,12 +849,12 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; - DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at " - "offset: %d, bit: %d\n", find_byte, find_bit); + pr_debug("Correcting single bit ECC error at offset: " + "%d, bit: %d\n", find_byte, find_bit); page_data[find_byte] ^= (1 << find_bit); - return 0; + return 1; default: if (isEccFF) { if (ecc_data2[0] == 0 && @@ -630,7 +862,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ ecc_data2[2] == 0) return 0; } - DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n"); + pr_debug("UNCORRECTED_ERROR default\n"); return -1; } } @@ -643,8 +875,11 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ * @calc_ecc: ecc read from HW ECC registers * * Compares the ecc read from nand spare area with ECC registers values - * and if ECC's mismached, it will call 'omap_compare_ecc' for error detection - * and correction. + * and if ECC's mismatched, it will call 'omap_compare_ecc' for error + * detection and correction. If there are no errors, %0 is returned. If + * there were errors and all of the errors were corrected, the number of + * corrected errors is returned. If uncorrectable errors exist, %-1 is + * returned. */ static int omap_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) @@ -652,6 +887,7 @@ static int omap_correct_data(struct mtd_info *mtd, u_char *dat, struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); int blockCnt = 0, i = 0, ret = 0; + int stat = 0; /* Ex NAND_ECC_HW12_2048 */ if ((info->nand.ecc.mode == NAND_ECC_HW) && @@ -665,12 +901,14 @@ static int omap_correct_data(struct mtd_info *mtd, u_char *dat, ret = omap_compare_ecc(read_ecc, calc_ecc, dat); if (ret < 0) return ret; + /* keep track of the number of corrected errors */ + stat += ret; } read_ecc += 3; calc_ecc += 3; dat += 512; } - return 0; + return stat; } /** @@ -690,7 +928,20 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - return gpmc_calculate_ecc(info->gpmc_cs, dat, ecc_code); + u32 val; + + val = readl(info->reg.gpmc_ecc_config); + if (((val >> ECC_CONFIG_CS_SHIFT) & ~CS_MASK) != info->gpmc_cs) + return -EINVAL; + + /* read ecc result */ + val = readl(info->reg.gpmc_ecc1_result); + *ecc_code++ = val; /* P128e, ..., P1e */ + *ecc_code++ = val >> 16; /* P128o, ..., P1o */ + /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ + *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); + + return 0; } /** @@ -704,12 +955,36 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode) mtd); struct nand_chip *chip = mtd->priv; unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + u32 val; + + /* clear ecc and enable bits */ + val = ECCCLEAR | ECC1; + writel(val, info->reg.gpmc_ecc_control); + + /* program ecc and result sizes */ + val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) | + ECC1RESULTSIZE); + writel(val, info->reg.gpmc_ecc_size_config); + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); + break; + case NAND_ECC_READSYN: + writel(ECCCLEAR, info->reg.gpmc_ecc_control); + break; + default: + dev_info(&info->pdev->dev, + "error: unrecognized Mode[%d]!\n", mode); + break; + } - gpmc_enable_hwecc(info->gpmc_cs, mode, dev_width, info->nand.ecc.size); + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + writel(val, info->reg.gpmc_ecc_config); } -#endif - /** * omap_wait - wait until the command is done * @mtd: MTD device structure @@ -728,21 +1003,22 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); unsigned long timeo = jiffies; - int status = NAND_STATUS_FAIL, state = this->state; + int status, state = this->state; if (state == FL_ERASING) - timeo += (HZ * 400) / 1000; + timeo += msecs_to_jiffies(400); else - timeo += (HZ * 20) / 1000; + timeo += msecs_to_jiffies(20); - gpmc_nand_write(info->gpmc_cs, - GPMC_NAND_COMMAND, (NAND_CMD_STATUS & 0xFF)); + writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command); while (time_before(jiffies, timeo)) { - status = gpmc_nand_read(info->gpmc_cs, GPMC_NAND_DATA); + status = readb(info->reg.gpmc_nand_data); if (status & NAND_STATUS_READY) break; cond_resched(); } + + status = readb(info->reg.gpmc_nand_data); return status; } @@ -756,37 +1032,618 @@ static int omap_dev_ready(struct mtd_info *mtd) struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - val = gpmc_read_status(GPMC_GET_IRQ_STATUS); + val = readl(info->reg.gpmc_status); + if ((val & 0x100) == 0x100) { - /* Clear IRQ Interrupt */ - val |= 0x100; - val &= ~(0x0); - gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, val); + return 1; } else { - unsigned int cnt = 0; - while (cnt++ < 0x1FF) { - if ((val & 0x100) == 0x100) - return 0; - val = gpmc_read_status(GPMC_GET_IRQ_STATUS); + return 0; + } +} + +/** + * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation + * @mtd: MTD device structure + * @mode: Read/Write mode + * + * When using BCH, sector size is hardcoded to 512 bytes. + * Using wrapping mode 6 both for reading and writing if ELM module not uses + * for error correction. + * On writing, + * eccsize0 = 0 (no additional protected byte in spare area) + * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ +static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) +{ + unsigned int bch_type; + unsigned int dev_width, nsectors; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + enum omap_ecc ecc_opt = info->ecc_opt; + struct nand_chip *chip = mtd->priv; + u32 val, wr_mode; + unsigned int ecc_size1, ecc_size0; + + /* GPMC configurations for calculating ECC */ + switch (ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + bch_type = 0; + nsectors = 1; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + case OMAP_ECC_BCH4_CODE_HW: + bch_type = 0; + nsectors = chip->ecc.steps; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_1; + ecc_size0 = BCH4R_ECC_SIZE0; + ecc_size1 = BCH4R_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + bch_type = 1; + nsectors = 1; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + case OMAP_ECC_BCH8_CODE_HW: + bch_type = 1; + nsectors = chip->ecc.steps; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_1; + ecc_size0 = BCH8R_ECC_SIZE0; + ecc_size1 = BCH8R_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; } + break; + case OMAP_ECC_BCH16_CODE_HW: + bch_type = 0x2; + nsectors = chip->ecc.steps; + if (mode == NAND_ECC_READ) { + wr_mode = 0x01; + ecc_size0 = 52; /* ECC bits in nibbles per sector */ + ecc_size1 = 0; /* non-ECC bits in nibbles per sector */ + } else { + wr_mode = 0x01; + ecc_size0 = 0; /* extra bits in nibbles per sector */ + ecc_size1 = 52; /* OOB bits in nibbles per sector */ + } + break; + default: + return; } - return 1; + writel(ECC1, info->reg.gpmc_ecc_control); + + /* Configure ecc size for BCH */ + val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT); + writel(val, info->reg.gpmc_ecc_size_config); + + dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + + /* BCH configuration */ + val = ((1 << 16) | /* enable BCH */ + (bch_type << 12) | /* BCH4/BCH8/BCH16 */ + (wr_mode << 8) | /* wrap mode */ + (dev_width << 7) | /* bus width */ + (((nsectors-1) & 0x7) << 4) | /* number of sectors */ + (info->gpmc_cs << 1) | /* ECC CS */ + (0x1)); /* enable ECC */ + + writel(val, info->reg.gpmc_ecc_config); + + /* Clear ecc and enable bits */ + writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); } -static int __devinit omap_nand_probe(struct platform_device *pdev) +static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; +static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, + 0x97, 0x79, 0xe5, 0x24, 0xb5}; + +/** + * omap_calculate_ecc_bch - Generate bytes of ECC bytes + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Support calculating of BCH4/8 ecc vectors for the page + */ +static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + int eccbytes = info->nand.ecc.bytes; + struct gpmc_nand_regs *gpmc_regs = &info->reg; + u8 *ecc_code; + unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4; + u32 val; + int i, j; + + nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; + for (i = 0; i < nsectors; i++) { + ecc_code = ecc_calc; + switch (info->ecc_opt) { + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH8_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); + bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); + bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); + *ecc_code++ = (bch_val4 & 0xFF); + *ecc_code++ = ((bch_val3 >> 24) & 0xFF); + *ecc_code++ = ((bch_val3 >> 16) & 0xFF); + *ecc_code++ = ((bch_val3 >> 8) & 0xFF); + *ecc_code++ = (bch_val3 & 0xFF); + *ecc_code++ = ((bch_val2 >> 24) & 0xFF); + *ecc_code++ = ((bch_val2 >> 16) & 0xFF); + *ecc_code++ = ((bch_val2 >> 8) & 0xFF); + *ecc_code++ = (bch_val2 & 0xFF); + *ecc_code++ = ((bch_val1 >> 24) & 0xFF); + *ecc_code++ = ((bch_val1 >> 16) & 0xFF); + *ecc_code++ = ((bch_val1 >> 8) & 0xFF); + *ecc_code++ = (bch_val1 & 0xFF); + break; + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH4_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); + *ecc_code++ = ((bch_val2 >> 12) & 0xFF); + *ecc_code++ = ((bch_val2 >> 4) & 0xFF); + *ecc_code++ = ((bch_val2 & 0xF) << 4) | + ((bch_val1 >> 28) & 0xF); + *ecc_code++ = ((bch_val1 >> 20) & 0xFF); + *ecc_code++ = ((bch_val1 >> 12) & 0xFF); + *ecc_code++ = ((bch_val1 >> 4) & 0xFF); + *ecc_code++ = ((bch_val1 & 0xF) << 4); + break; + case OMAP_ECC_BCH16_CODE_HW: + val = readl(gpmc_regs->gpmc_bch_result6[i]); + ecc_code[0] = ((val >> 8) & 0xFF); + ecc_code[1] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result5[i]); + ecc_code[2] = ((val >> 24) & 0xFF); + ecc_code[3] = ((val >> 16) & 0xFF); + ecc_code[4] = ((val >> 8) & 0xFF); + ecc_code[5] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result4[i]); + ecc_code[6] = ((val >> 24) & 0xFF); + ecc_code[7] = ((val >> 16) & 0xFF); + ecc_code[8] = ((val >> 8) & 0xFF); + ecc_code[9] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result3[i]); + ecc_code[10] = ((val >> 24) & 0xFF); + ecc_code[11] = ((val >> 16) & 0xFF); + ecc_code[12] = ((val >> 8) & 0xFF); + ecc_code[13] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result2[i]); + ecc_code[14] = ((val >> 24) & 0xFF); + ecc_code[15] = ((val >> 16) & 0xFF); + ecc_code[16] = ((val >> 8) & 0xFF); + ecc_code[17] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result1[i]); + ecc_code[18] = ((val >> 24) & 0xFF); + ecc_code[19] = ((val >> 16) & 0xFF); + ecc_code[20] = ((val >> 8) & 0xFF); + ecc_code[21] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result0[i]); + ecc_code[22] = ((val >> 24) & 0xFF); + ecc_code[23] = ((val >> 16) & 0xFF); + ecc_code[24] = ((val >> 8) & 0xFF); + ecc_code[25] = ((val >> 0) & 0xFF); + break; + default: + return -EINVAL; + } + + /* ECC scheme specific syndrome customizations */ + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back */ + for (j = 0; j < eccbytes; j++) + ecc_calc[j] ^= bch4_polynomial[j]; + break; + case OMAP_ECC_BCH4_CODE_HW: + /* Set 8th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back */ + for (j = 0; j < eccbytes; j++) + ecc_calc[j] ^= bch8_polynomial[j]; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* Set 14th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + case OMAP_ECC_BCH16_CODE_HW: + break; + default: + return -EINVAL; + } + + ecc_calc += eccbytes; + } + + return 0; +} + +#ifdef CONFIG_MTD_NAND_OMAP_BCH +/** + * erased_sector_bitflips - count bit flips + * @data: data sector buffer + * @oob: oob buffer + * @info: omap_nand_info + * + * Check the bit flips in erased page falls below correctable level. + * If falls below, report the page as erased with correctable bit + * flip, else report as uncorrectable page. + */ +static int erased_sector_bitflips(u_char *data, u_char *oob, + struct omap_nand_info *info) +{ + int flip_bits = 0, i; + + for (i = 0; i < info->nand.ecc.size; i++) { + flip_bits += hweight8(~data[i]); + if (flip_bits > info->nand.ecc.strength) + return 0; + } + + for (i = 0; i < info->nand.ecc.bytes - 1; i++) { + flip_bits += hweight8(~oob[i]); + if (flip_bits > info->nand.ecc.strength) + return 0; + } + + /* + * Bit flips falls in correctable level. + * Fill data area with 0xFF + */ + if (flip_bits) { + memset(data, 0xFF, info->nand.ecc.size); + memset(oob, 0xFF, info->nand.ecc.bytes); + } + + return flip_bits; +} + +/** + * omap_elm_correct_data - corrects page data area in case error reported + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + * + * Calculated ecc vector reported as zero in case of non-error pages. + * In case of non-zero ecc vector, first filter out erased-pages, and + * then process data via ELM to detect bit-flips. + */ +static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_ecc_ctrl *ecc = &info->nand.ecc; + int eccsteps = info->nand.ecc.steps; + int i , j, stat = 0; + int eccflag, actual_eccbytes; + struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; + u_char *ecc_vec = calc_ecc; + u_char *spare_ecc = read_ecc; + u_char *erased_ecc_vec; + u_char *buf; + int bitflip_count; + bool is_error_reported = false; + u32 bit_pos, byte_pos, error_max, pos; + int err; + + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + /* omit 7th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; + erased_ecc_vec = bch4_vector; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* omit 14th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; + erased_ecc_vec = bch8_vector; + break; + case OMAP_ECC_BCH16_CODE_HW: + actual_eccbytes = ecc->bytes; + erased_ecc_vec = bch16_vector; + break; + default: + pr_err("invalid driver configuration\n"); + return -EINVAL; + } + + /* Initialize elm error vector to zero */ + memset(err_vec, 0, sizeof(err_vec)); + + for (i = 0; i < eccsteps ; i++) { + eccflag = 0; /* initialize eccflag */ + + /* + * Check any error reported, + * In case of error, non zero ecc reported. + */ + for (j = 0; j < actual_eccbytes; j++) { + if (calc_ecc[j] != 0) { + eccflag = 1; /* non zero ecc, error present */ + break; + } + } + + if (eccflag == 1) { + if (memcmp(calc_ecc, erased_ecc_vec, + actual_eccbytes) == 0) { + /* + * calc_ecc[] matches pattern for ECC(all 0xff) + * so this is definitely an erased-page + */ + } else { + buf = &data[info->nand.ecc.size * i]; + /* + * count number of 0-bits in read_buf. + * This check can be removed once a similar + * check is introduced in generic NAND driver + */ + bitflip_count = erased_sector_bitflips( + buf, read_ecc, info); + if (bitflip_count) { + /* + * number of 0-bits within ECC limits + * So this may be an erased-page + */ + stat += bitflip_count; + } else { + /* + * Too many 0-bits. It may be a + * - programmed-page, OR + * - erased-page with many bit-flips + * So this page requires check by ELM + */ + err_vec[i].error_reported = true; + is_error_reported = true; + } + } + } + + /* Update the ecc vector */ + calc_ecc += ecc->bytes; + read_ecc += ecc->bytes; + } + + /* Check if any error reported */ + if (!is_error_reported) + return stat; + + /* Decode BCH error using ELM module */ + elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); + + err = 0; + for (i = 0; i < eccsteps; i++) { + if (err_vec[i].error_uncorrectable) { + pr_err("nand: uncorrectable bit-flips found\n"); + err = -EBADMSG; + } else if (err_vec[i].error_reported) { + for (j = 0; j < err_vec[i].error_count; j++) { + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + /* Add 4 bits to take care of padding */ + pos = err_vec[i].error_loc[j] + + BCH4_BIT_PAD; + break; + case OMAP_ECC_BCH8_CODE_HW: + case OMAP_ECC_BCH16_CODE_HW: + pos = err_vec[i].error_loc[j]; + break; + default: + return -EINVAL; + } + error_max = (ecc->size + actual_eccbytes) * 8; + /* Calculate bit position of error */ + bit_pos = pos % 8; + + /* Calculate byte position of error */ + byte_pos = (error_max - pos - 1) / 8; + + if (pos < error_max) { + if (byte_pos < 512) { + pr_debug("bitflip@dat[%d]=%x\n", + byte_pos, data[byte_pos]); + data[byte_pos] ^= 1 << bit_pos; + } else { + pr_debug("bitflip@oob[%d]=%x\n", + (byte_pos - 512), + spare_ecc[byte_pos - 512]); + spare_ecc[byte_pos - 512] ^= + 1 << bit_pos; + } + } else { + pr_err("invalid bit-flip @ %d:%d\n", + byte_pos, bit_pos); + err = -EBADMSG; + } + } + } + + /* Update number of correctable errors */ + stat += err_vec[i].error_count; + + /* Update page data with sector size */ + data += ecc->size; + spare_ecc += ecc->bytes; + } + + return (err) ? err : stat; +} + +/** + * omap_write_page_bch - BCH ecc based write page function for entire page + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * + * Custom write page method evolved to support multi sector writing in one shot + */ +static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + int i; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + /* Enable GPMC ecc engine */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* Write data */ + chip->write_buf(mtd, buf, mtd->writesize); + + /* Update ecc vector from GPMC result registers */ + chip->ecc.calculate(mtd, buf, &ecc_calc[0]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + /* Write ecc vector to OOB area */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/** + * omap_read_page_bch - BCH ecc based page read function for entire page + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module + * used for error correction. + * Custom method evolved to support ELM error correction & multi sector + * reading. On reading page data area is read along with OOB data with + * ecc engine enabled. ecc vector updated after read of OOB data. + * For non error pages ecc vector reported as zero. + */ +static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *oob = &chip->oob_poi[eccpos[0]]; + uint32_t oob_pos = mtd->writesize + chip->ecc.layout->eccpos[0]; + int stat; + unsigned int max_bitflips = 0; + + /* Enable GPMC ecc engine */ + chip->ecc.hwctl(mtd, NAND_ECC_READ); + + /* Read data */ + chip->read_buf(mtd, buf, mtd->writesize); + + /* Read oob bytes */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1); + chip->read_buf(mtd, oob, chip->ecc.total); + + /* Calculate ecc bytes */ + chip->ecc.calculate(mtd, buf, ecc_calc); + + memcpy(ecc_code, &chip->oob_poi[eccpos[0]], chip->ecc.total); + + stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc); + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + + return max_bitflips; +} + +/** + * is_elm_present - checks for presence of ELM module by scanning DT nodes + * @omap_nand_info: NAND device structure containing platform data + * @bch_type: 0x0=BCH4, 0x1=BCH8, 0x2=BCH16 + */ +static int is_elm_present(struct omap_nand_info *info, + struct device_node *elm_node, enum bch_ecc bch_type) +{ + struct platform_device *pdev; + struct nand_ecc_ctrl *ecc = &info->nand.ecc; + int err; + /* check whether elm-id is passed via DT */ + if (!elm_node) { + pr_err("nand: error: ELM DT node not found\n"); + return -ENODEV; + } + pdev = of_find_device_by_node(elm_node); + /* check whether ELM device is registered */ + if (!pdev) { + pr_err("nand: error: ELM device not found\n"); + return -ENODEV; + } + /* ELM module available, now configure it */ + info->elm_dev = &pdev->dev; + err = elm_config(info->elm_dev, bch_type, + (info->mtd.writesize / ecc->size), ecc->size, ecc->bytes); + + return err; +} +#endif /* CONFIG_MTD_NAND_ECC_BCH */ + +static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; struct omap_nand_platform_data *pdata; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + struct nand_ecclayout *ecclayout; int err; - - pdata = pdev->dev.platform_data; + int i; + dma_cap_mask_t mask; + unsigned sig; + unsigned oob_index; + struct resource *res; + struct mtd_part_parser_data ppdata = {}; + + pdata = dev_get_platdata(&pdev->dev); if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENODEV; } - info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), + GFP_KERNEL); if (!info) return -ENOMEM; @@ -795,144 +1652,413 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); - info->pdev = pdev; - + info->pdev = pdev; info->gpmc_cs = pdata->cs; - info->phys_base = pdata->phys_base; - - info->mtd.priv = &info->nand; - info->mtd.name = dev_name(&pdev->dev); - info->mtd.owner = THIS_MODULE; + info->reg = pdata->reg; + info->of_node = pdata->of_node; + info->ecc_opt = pdata->ecc_opt; + mtd = &info->mtd; + mtd->priv = &info->nand; + mtd->name = dev_name(&pdev->dev); + mtd->owner = THIS_MODULE; + nand_chip = &info->nand; + nand_chip->ecc.priv = NULL; + nand_chip->options |= NAND_SKIP_BBTSCAN; - info->nand.options |= pdata->devsize ? NAND_BUSWIDTH_16 : 0; - info->nand.options |= NAND_SKIP_BBTSCAN; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nand_chip->IO_ADDR_R)) + return PTR_ERR(nand_chip->IO_ADDR_R); - /* NAND write protect off */ - gpmc_cs_configure(info->gpmc_cs, GPMC_CONFIG_WP, 0); + info->phys_base = res->start; - if (!request_mem_region(info->phys_base, NAND_IO_SIZE, - pdev->dev.driver->name)) { - err = -EBUSY; - goto out_free_info; - } - - info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE); - if (!info->nand.IO_ADDR_R) { - err = -ENOMEM; - goto out_release_mem_region; - } + nand_chip->controller = &info->controller; - info->nand.controller = &info->controller; - - info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; - info->nand.cmd_ctrl = omap_hwcontrol; + nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; + nand_chip->cmd_ctrl = omap_hwcontrol; /* * If RDY/BSY line is connected to OMAP then use the omap ready - * funcrtion and the generic nand_wait function which reads the status - * register after monitoring the RDY/BSY line.Otherwise use a standard + * function and the generic nand_wait function which reads the status + * register after monitoring the RDY/BSY line. Otherwise use a standard * chip delay which is slightly more than tR (AC Timing) of the NAND * device and read status register until you get a failure or success */ if (pdata->dev_ready) { - info->nand.dev_ready = omap_dev_ready; - info->nand.chip_delay = 0; + nand_chip->dev_ready = omap_dev_ready; + nand_chip->chip_delay = 0; } else { - info->nand.waitfunc = omap_wait; - info->nand.chip_delay = 50; + nand_chip->waitfunc = omap_wait; + nand_chip->chip_delay = 50; } - if (use_prefetch) { + /* scan NAND device connected to chip controller */ + nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16; + if (nand_scan_ident(mtd, 1, NULL)) { + pr_err("nand device scan failed, may be bus-width mismatch\n"); + err = -ENXIO; + goto return_error; + } - info->nand.read_buf = omap_read_buf_pref; - info->nand.write_buf = omap_write_buf_pref; - if (use_dma) { - err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", - omap_nand_dma_cb, &info->comp, &info->dma_ch); - if (err < 0) { - info->dma_ch = -1; - printk(KERN_WARNING "DMA request failed." - " Non-dma data transfer mode\n"); - } else { - omap_set_dma_dest_burst_mode(info->dma_ch, - OMAP_DMA_DATA_BURST_16); - omap_set_dma_src_burst_mode(info->dma_ch, - OMAP_DMA_DATA_BURST_16); + /* check for small page devices */ + if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) { + pr_err("small page devices are not supported\n"); + err = -EINVAL; + goto return_error; + } - info->nand.read_buf = omap_read_buf_dma_pref; - info->nand.write_buf = omap_write_buf_dma_pref; + /* re-populate low-level callbacks based on xfer modes */ + switch (pdata->xfer_type) { + case NAND_OMAP_PREFETCH_POLLED: + nand_chip->read_buf = omap_read_buf_pref; + nand_chip->write_buf = omap_write_buf_pref; + break; + + case NAND_OMAP_POLLED: + /* Use nand_base defaults for {read,write}_buf */ + break; + + case NAND_OMAP_PREFETCH_DMA: + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + sig = OMAP24XX_DMA_GPMC; + info->dma = dma_request_channel(mask, omap_dma_filter_fn, &sig); + if (!info->dma) { + dev_err(&pdev->dev, "DMA engine request failed\n"); + err = -ENXIO; + goto return_error; + } else { + struct dma_slave_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.src_addr = info->phys_base; + cfg.dst_addr = info->phys_base; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = 16; + cfg.dst_maxburst = 16; + err = dmaengine_slave_config(info->dma, &cfg); + if (err) { + dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", + err); + goto return_error; } + nand_chip->read_buf = omap_read_buf_dma_pref; + nand_chip->write_buf = omap_write_buf_dma_pref; } - } else { - if (info->nand.options & NAND_BUSWIDTH_16) { - info->nand.read_buf = omap_read_buf16; - info->nand.write_buf = omap_write_buf16; - } else { - info->nand.read_buf = omap_read_buf8; - info->nand.write_buf = omap_write_buf8; + break; + + case NAND_OMAP_PREFETCH_IRQ: + info->gpmc_irq_fifo = platform_get_irq(pdev, 0); + if (info->gpmc_irq_fifo <= 0) { + dev_err(&pdev->dev, "error getting fifo irq\n"); + err = -ENODEV; + goto return_error; + } + err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-fifo", info); + if (err) { + dev_err(&pdev->dev, "requesting irq(%d) error:%d", + info->gpmc_irq_fifo, err); + info->gpmc_irq_fifo = 0; + goto return_error; + } + + info->gpmc_irq_count = platform_get_irq(pdev, 1); + if (info->gpmc_irq_count <= 0) { + dev_err(&pdev->dev, "error getting count irq\n"); + err = -ENODEV; + goto return_error; + } + err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-count", info); + if (err) { + dev_err(&pdev->dev, "requesting irq(%d) error:%d", + info->gpmc_irq_count, err); + info->gpmc_irq_count = 0; + goto return_error; } + + nand_chip->read_buf = omap_read_buf_irq_pref; + nand_chip->write_buf = omap_write_buf_irq_pref; + + break; + + default: + dev_err(&pdev->dev, + "xfer_type(%d) not supported!\n", pdata->xfer_type); + err = -EINVAL; + goto return_error; } - info->nand.verify_buf = omap_verify_buf; -#ifdef CONFIG_MTD_NAND_OMAP_HWECC - info->nand.ecc.bytes = 3; - info->nand.ecc.size = 512; - info->nand.ecc.calculate = omap_calculate_ecc; - info->nand.ecc.hwctl = omap_enable_hwecc; - info->nand.ecc.correct = omap_correct_data; - info->nand.ecc.mode = NAND_ECC_HW; + /* populate MTD interface based on ECC scheme */ + nand_chip->ecc.layout = &omap_oobinfo; + ecclayout = &omap_oobinfo; + switch (info->ecc_opt) { + case OMAP_ECC_HAM1_CODE_HW: + pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.bytes = 3; + nand_chip->ecc.size = 512; + nand_chip->ecc.strength = 1; + nand_chip->ecc.calculate = omap_calculate_ecc; + nand_chip->ecc.hwctl = omap_enable_hwecc; + nand_chip->ecc.correct = omap_correct_data; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + if (nand_chip->options & NAND_BUSWIDTH_16) + oob_index = BADBLOCK_MARKER_LENGTH; + else + oob_index = 1; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) + ecclayout->eccpos[i] = oob_index; + /* no reserved-marker in ecclayout for this ecc-scheme */ + ecclayout->oobfree->offset = + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + break; + + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 7; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { + ecclayout->eccpos[i] = oob_index; + if (((i + 1) % nand_chip->ecc.bytes) == 0) + oob_index++; + } + /* include reserved-marker in ecclayout->oobfree calculation */ + ecclayout->oobfree->offset = 1 + + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd, + nand_chip->ecc.size, + nand_chip->ecc.bytes, + &nand_chip->ecc.layout); + if (!nand_chip->ecc.priv) { + pr_err("nand: error: unable to use s/w BCH library\n"); + err = -EINVAL; + } + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif + case OMAP_ECC_BCH4_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 7 + 1; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) + ecclayout->eccpos[i] = oob_index; + /* reserved marker already included in ecclayout->eccbytes */ + ecclayout->oobfree->offset = + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + /* This ECC scheme requires ELM H/W block */ + if (is_elm_present(info, pdata->elm_of_node, BCH4_ECC) < 0) { + pr_err("nand: error: could not initialize ELM\n"); + err = -ENODEV; + goto return_error; + } + break; #else - info->nand.ecc.mode = NAND_ECC_SOFT; + pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + err = -EINVAL; + goto return_error; #endif - /* DIP switches on some boards change between 8 and 16 bit - * bus widths for flash. Try the other width if the first try fails. - */ - if (nand_scan(&info->mtd, 1)) { - info->nand.options ^= NAND_BUSWIDTH_16; - if (nand_scan(&info->mtd, 1)) { - err = -ENXIO; - goto out_release_mem_region; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 13; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { + ecclayout->eccpos[i] = oob_index; + if (((i + 1) % nand_chip->ecc.bytes) == 0) + oob_index++; } - } + /* include reserved-marker in ecclayout->oobfree calculation */ + ecclayout->oobfree->offset = 1 + + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd, + nand_chip->ecc.size, + nand_chip->ecc.bytes, + &nand_chip->ecc.layout); + if (!nand_chip->ecc.priv) { + pr_err("nand: error: unable to use s/w BCH library\n"); + err = -EINVAL; + goto return_error; + } + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif -#ifdef CONFIG_MTD_PARTITIONS - err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); - if (err > 0) - add_mtd_partitions(&info->mtd, info->parts, err); - else if (pdata->parts) - add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); - else + case OMAP_ECC_BCH8_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 13 + 1; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + /* This ECC scheme requires ELM H/W block */ + err = is_elm_present(info, pdata->elm_of_node, BCH8_ECC); + if (err < 0) { + pr_err("nand: error: could not initialize ELM\n"); + goto return_error; + } + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) + ecclayout->eccpos[i] = oob_index; + /* reserved marker already included in ecclayout->eccbytes */ + ecclayout->oobfree->offset = + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + err = -EINVAL; + goto return_error; #endif - add_mtd_device(&info->mtd); - platform_set_drvdata(pdev, &info->mtd); + case OMAP_ECC_BCH16_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH + pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 26; + nand_chip->ecc.strength = 16; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + /* This ECC scheme requires ELM H/W block */ + err = is_elm_present(info, pdata->elm_of_node, BCH16_ECC); + if (err < 0) { + pr_err("ELM is required for this ECC scheme\n"); + goto return_error; + } + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + oob_index = BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) + ecclayout->eccpos[i] = oob_index; + /* reserved marker already included in ecclayout->eccbytes */ + ecclayout->oobfree->offset = + ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif + default: + pr_err("nand: error: invalid or unsupported ECC scheme\n"); + err = -EINVAL; + goto return_error; + } - return 0; + /* all OOB bytes from oobfree->offset till end off OOB are free */ + ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset; + /* check if NAND device's OOB is enough to store ECC signatures */ + if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) { + pr_err("not enough OOB bytes required = %d, available=%d\n", + ecclayout->eccbytes, mtd->oobsize); + err = -EINVAL; + goto return_error; + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { + err = -ENXIO; + goto return_error; + } + + ppdata.of_node = pdata->of_node; + mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts, + pdata->nr_parts); -out_release_mem_region: - release_mem_region(info->phys_base, NAND_IO_SIZE); -out_free_info: - kfree(info); + platform_set_drvdata(pdev, mtd); + return 0; + +return_error: + if (info->dma) + dma_release_channel(info->dma); + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } return err; } static int omap_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd->priv; struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - - platform_set_drvdata(pdev, NULL); - if (use_dma) - omap_free_dma(info->dma_ch); - - /* Release NAND device, its internal structures and partitions */ - nand_release(&info->mtd); - iounmap(info->nand.IO_ADDR_R); - kfree(&info->mtd); + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } + if (info->dma) + dma_release_channel(info->dma); + nand_release(mtd); return 0; } @@ -945,29 +2071,8 @@ static struct platform_driver omap_nand_driver = { }, }; -static int __init omap_nand_init(void) -{ - printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); - - /* This check is required if driver is being - * loaded run time as a module - */ - if ((1 == use_dma) && (0 == use_prefetch)) { - printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 " - "without use_prefetch'. Prefetch will not be" - " used in either mode (mpu or dma)\n"); - } - return platform_driver_register(&omap_nand_driver); -} - -static void __exit omap_nand_exit(void) -{ - platform_driver_unregister(&omap_nand_driver); -} - -module_init(omap_nand_init); -module_exit(omap_nand_exit); +module_platform_driver(omap_nand_driver); -MODULE_ALIAS(DRIVER_NAME); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index da6e7534305..471b4df3a5a 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -13,17 +13,15 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/clk.h> +#include <linux/err.h> #include <asm/io.h> #include <asm/sizes.h> -#include <mach/hardware.h> -#include <plat/orion_nand.h> - -#ifdef CONFIG_MTD_CMDLINE_PARTS -static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif +#include <linux/platform_data/mtd-orion_nand.h> static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { @@ -78,19 +76,17 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) static int __init orion_nand_probe(struct platform_device *pdev) { struct mtd_info *mtd; + struct mtd_part_parser_data ppdata = {}; struct nand_chip *nc; struct orion_nand_data *board; struct resource *res; + struct clk *clk; void __iomem *io_base; int ret = 0; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *partitions = NULL; - int num_part = 0; -#endif + u32 val = 0; nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL); if (!nc) { - printk(KERN_ERR "orion_nand: failed to allocate device structure.\n"); ret = -ENOMEM; goto no_res; } @@ -104,12 +100,37 @@ static int __init orion_nand_probe(struct platform_device *pdev) io_base = ioremap(res->start, resource_size(res)); if (!io_base) { - printk(KERN_ERR "orion_nand: ioremap failed\n"); + dev_err(&pdev->dev, "ioremap failed\n"); ret = -EIO; goto no_res; } - board = pdev->dev.platform_data; + if (pdev->dev.of_node) { + board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data), + GFP_KERNEL); + if (!board) { + ret = -ENOMEM; + goto no_res; + } + if (!of_property_read_u32(pdev->dev.of_node, "cle", &val)) + board->cle = (u8)val; + else + board->cle = 0; + if (!of_property_read_u32(pdev->dev.of_node, "ale", &val)) + board->ale = (u8)val; + else + board->ale = 1; + if (!of_property_read_u32(pdev->dev.of_node, + "bank-width", &val)) + board->width = (u8)val * 8; + else + board->width = 8; + if (!of_property_read_u32(pdev->dev.of_node, + "chip-delay", &val)) + board->chip_delay = (u8)val; + } else { + board = dev_get_platdata(&pdev->dev); + } mtd->priv = nc; mtd->owner = THIS_MODULE; @@ -123,6 +144,10 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (board->chip_delay) nc->chip_delay = board->chip_delay; + WARN(board->width > 16, + "%d bit bus width out of range", + board->width); + if (board->width == 16) nc->options |= NAND_BUSWIDTH_16; @@ -131,30 +156,23 @@ static int __init orion_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mtd); + /* Not all platforms can gate the clock, so it is not + an error if the clock does not exists. */ + clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) { + clk_prepare_enable(clk); + clk_put(clk); + } + if (nand_scan(mtd, 1)) { ret = -ENXIO; goto no_dev; } -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS mtd->name = "orion_nand"; - num_part = parse_mtd_partitions(mtd, part_probes, &partitions, 0); -#endif - /* If cmdline partitions have been passed, let them be used */ - if (num_part <= 0) { - num_part = board->nr_parts; - partitions = board->parts; - } - - if (partitions && num_part > 0) - ret = add_mtd_partitions(mtd, partitions, num_part); - else - ret = add_mtd_device(mtd); -#else - ret = add_mtd_device(mtd); -#endif - + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, + board->parts, board->nr_parts); if (ret) { nand_release(mtd); goto no_dev; @@ -163,7 +181,10 @@ static int __init orion_nand_probe(struct platform_device *pdev) return 0; no_dev: - platform_set_drvdata(pdev, NULL); + if (!IS_ERR(clk)) { + clk_disable_unprepare(clk); + clk_put(clk); + } iounmap(io_base); no_res: kfree(nc); @@ -171,10 +192,11 @@ no_res: return ret; } -static int __devexit orion_nand_remove(struct platform_device *pdev) +static int orion_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); struct nand_chip *nc = mtd->priv; + struct clk *clk; nand_release(mtd); @@ -182,29 +204,32 @@ static int __devexit orion_nand_remove(struct platform_device *pdev) kfree(nc); + clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) { + clk_disable_unprepare(clk); + clk_put(clk); + } + return 0; } +#ifdef CONFIG_OF +static const struct of_device_id orion_nand_of_match_table[] = { + { .compatible = "marvell,orion-nand", }, + {}, +}; +#endif + static struct platform_driver orion_nand_driver = { - .remove = __devexit_p(orion_nand_remove), + .remove = orion_nand_remove, .driver = { .name = "orion_nand", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(orion_nand_of_match_table), }, }; -static int __init orion_nand_init(void) -{ - return platform_driver_probe(&orion_nand_driver, orion_nand_probe); -} - -static void __exit orion_nand_exit(void) -{ - platform_driver_unregister(&orion_nand_driver); -} - -module_init(orion_nand_init); -module_exit(orion_nand_exit); +module_platform_driver_probe(orion_nand_driver, orion_nand_probe); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tzachi Perelstein"); diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index bb277a54986..2c98f9da747 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -23,11 +23,12 @@ #undef DEBUG #include <linux/slab.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pci.h> @@ -89,8 +90,7 @@ int pasemi_device_ready(struct mtd_info *mtd) return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); } -static int __devinit pasemi_nand_probe(struct platform_device *ofdev, - const struct of_device_id *match) +static int pasemi_nand_probe(struct platform_device *ofdev) { struct pci_dev *pdev; struct device_node *np = ofdev->dev.of_node; @@ -156,15 +156,15 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev, chip->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - chip->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR; + chip->bbt_options = NAND_BBT_USE_FLASH; - /* Scan to find existance of the device */ + /* Scan to find existence of the device */ if (nand_scan(pasemi_nand_mtd, 1)) { err = -ENXIO; goto out_lpc; } - if (add_mtd_device(pasemi_nand_mtd)) { + if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) { printk(KERN_ERR "pasemi_nand: Unable to register MTD device\n"); err = -ENODEV; goto out_lpc; @@ -185,7 +185,7 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev, return err; } -static int __devexit pasemi_nand_remove(struct platform_device *ofdev) +static int pasemi_nand_remove(struct platform_device *ofdev) { struct nand_chip *chip; @@ -219,10 +219,10 @@ static const struct of_device_id pasemi_nand_match[] = MODULE_DEVICE_TABLE(of, pasemi_nand_match); -static struct of_platform_driver pasemi_nand_driver = +static struct platform_driver pasemi_nand_driver = { .driver = { - .name = (char*)driver_name, + .name = driver_name, .owner = THIS_MODULE, .of_match_table = pasemi_nand_match, }, @@ -230,17 +230,7 @@ static struct of_platform_driver pasemi_nand_driver = .remove = pasemi_nand_remove, }; -static int __init pasemi_nand_init(void) -{ - return of_register_platform_driver(&pasemi_nand_driver); -} -module_init(pasemi_nand_init); - -static void __exit pasemi_nand_exit(void) -{ - of_unregister_platform_driver(&pasemi_nand_driver); -} -module_exit(pasemi_nand_exit); +module_platform_driver(pasemi_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>"); diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 317aff428e4..0b068a5c0bf 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -9,6 +9,7 @@ * */ +#include <linux/err.h> #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -21,51 +22,42 @@ struct plat_nand_data { struct nand_chip chip; struct mtd_info mtd; void __iomem *io_base; -#ifdef CONFIG_MTD_PARTITIONS - int nr_parts; - struct mtd_partition *parts; -#endif }; +static const char *part_probe_types[] = { "cmdlinepart", NULL }; + /* * Probe for the NAND device. */ -static int __devinit plat_nand_probe(struct platform_device *pdev) +static int plat_nand_probe(struct platform_device *pdev) { - struct platform_nand_data *pdata = pdev->dev.platform_data; + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); + struct mtd_part_parser_data ppdata; struct plat_nand_data *data; struct resource *res; + const char **part_types; int err = 0; + if (!pdata) { + dev_err(&pdev->dev, "platform_nand_data is missing\n"); + return -EINVAL; + } + if (pdata->chip.nr_chips < 1) { dev_err(&pdev->dev, "invalid number of chips specified\n"); return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - /* Allocate memory for the device structure (and zero it) */ - data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL); - if (!data) { - dev_err(&pdev->dev, "failed to allocate device structure.\n"); + data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), + GFP_KERNEL); + if (!data) return -ENOMEM; - } - - if (!request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev))) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - err = -EBUSY; - goto out_free; - } - data->io_base = ioremap(res->start, resource_size(res)); - if (data->io_base == NULL) { - dev_err(&pdev->dev, "ioremap failed\n"); - err = -EIO; - goto out_release_io; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->io_base)) + return PTR_ERR(data->io_base); data->chip.priv = &data; data->mtd.priv = &data->chip; @@ -79,8 +71,10 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) data->chip.select_chip = pdata->ctrl.select_chip; data->chip.write_buf = pdata->ctrl.write_buf; data->chip.read_buf = pdata->ctrl.read_buf; + data->chip.read_byte = pdata->ctrl.read_byte; data->chip.chip_delay = pdata->chip.chip_delay; data->chip.options |= pdata->chip.options; + data->chip.bbt_options |= pdata->chip.bbt_options; data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; data->chip.ecc.layout = pdata->chip.ecclayout; @@ -95,31 +89,18 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) goto out; } - /* Scan to find existance of the device */ + /* Scan to find existence of the device */ if (nand_scan(&data->mtd, pdata->chip.nr_chips)) { err = -ENXIO; goto out; } -#ifdef CONFIG_MTD_PARTITIONS - if (pdata->chip.part_probe_types) { - err = parse_mtd_partitions(&data->mtd, - pdata->chip.part_probe_types, - &data->parts, 0); - if (err > 0) { - add_mtd_partitions(&data->mtd, data->parts, err); - return 0; - } - } - if (pdata->chip.set_parts) - pdata->chip.set_parts(data->mtd.size, &pdata->chip); - if (pdata->chip.partitions) { - data->parts = pdata->chip.partitions; - err = add_mtd_partitions(&data->mtd, data->parts, - pdata->chip.nr_partitions); - } else -#endif - err = add_mtd_device(&data->mtd); + part_types = pdata->chip.part_probe_types ? : part_probe_types; + + ppdata.of_node = pdev->dev.of_node; + err = mtd_device_parse_register(&data->mtd, part_types, &ppdata, + pdata->chip.partitions, + pdata->chip.nr_partitions); if (!err) return err; @@ -128,61 +109,41 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) out: if (pdata->ctrl.remove) pdata->ctrl.remove(pdev); - platform_set_drvdata(pdev, NULL); - iounmap(data->io_base); -out_release_io: - release_mem_region(res->start, resource_size(res)); -out_free: - kfree(data); return err; } /* * Remove a NAND device. */ -static int __devexit plat_nand_remove(struct platform_device *pdev) +static int plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); - struct platform_nand_data *pdata = pdev->dev.platform_data; - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); nand_release(&data->mtd); -#ifdef CONFIG_MTD_PARTITIONS - if (data->parts && data->parts != pdata->chip.partitions) - kfree(data->parts); -#endif if (pdata->ctrl.remove) pdata->ctrl.remove(pdev); - iounmap(data->io_base); - release_mem_region(res->start, resource_size(res)); - kfree(data); return 0; } +static const struct of_device_id plat_nand_match[] = { + { .compatible = "gen_nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, plat_nand_match); + static struct platform_driver plat_nand_driver = { - .probe = plat_nand_probe, - .remove = __devexit_p(plat_nand_remove), - .driver = { - .name = "gen_nand", - .owner = THIS_MODULE, + .probe = plat_nand_probe, + .remove = plat_nand_remove, + .driver = { + .name = "gen_nand", + .owner = THIS_MODULE, + .of_match_table = plat_nand_match, }, }; -static int __init plat_nand_init(void) -{ - return platform_driver_register(&plat_nand_driver); -} - -static void __exit plat_nand_exit(void) -{ - platform_driver_unregister(&plat_nand_driver); -} - -module_init(plat_nand_init); -module_exit(plat_nand_exit); +module_platform_driver(plat_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vitaly Wool"); diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c deleted file mode 100644 index cc865843185..00000000000 --- a/drivers/mtd/nand/ppchameleonevb.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * drivers/mtd/nand/ppchameleonevb.c - * - * Copyright (C) 2003 DAVE Srl (info@wawnet.biz) - * - * Derived from drivers/mtd/nand/edb7312.c - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash devices found on the - * PPChameleon/PPChameleonEVB system. - * PPChameleon options (autodetected): - * - BA model: no NAND - * - ME model: 32MB (Samsung K9F5608U0B) - * - HI model: 128MB (Samsung K9F1G08UOM) - * PPChameleonEVB options: - * - 32MB (Samsung K9F5608U0B) - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <platforms/PPChameleonEVB.h> - -#undef USE_READY_BUSY_PIN -#define USE_READY_BUSY_PIN -/* see datasheets (tR) */ -#define NAND_BIG_DELAY_US 25 -#define NAND_SMALL_DELAY_US 10 - -/* handy sizes */ -#define SZ_4M 0x00400000 -#define NAND_SMALL_SIZE 0x02000000 -#define NAND_MTD_NAME "ppchameleon-nand" -#define NAND_EVB_MTD_NAME "ppchameleonevb-nand" - -/* GPIO pins used to drive NAND chip mounted on processor module */ -#define NAND_nCE_GPIO_PIN (0x80000000 >> 1) -#define NAND_CLE_GPIO_PIN (0x80000000 >> 2) -#define NAND_ALE_GPIO_PIN (0x80000000 >> 3) -#define NAND_RB_GPIO_PIN (0x80000000 >> 4) -/* GPIO pins used to drive NAND chip mounted on EVB */ -#define NAND_EVB_nCE_GPIO_PIN (0x80000000 >> 14) -#define NAND_EVB_CLE_GPIO_PIN (0x80000000 >> 15) -#define NAND_EVB_ALE_GPIO_PIN (0x80000000 >> 16) -#define NAND_EVB_RB_GPIO_PIN (0x80000000 >> 31) - -/* - * MTD structure for PPChameleonEVB board - */ -static struct mtd_info *ppchameleon_mtd = NULL; -static struct mtd_info *ppchameleonevb_mtd = NULL; - -/* - * Module stuff - */ -static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR; -static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR; - -#ifdef MODULE -module_param(ppchameleon_fio_pbase, ulong, 0); -module_param(ppchameleonevb_fio_pbase, ulong, 0); -#else -__setup("ppchameleon_fio_pbase=", ppchameleon_fio_pbase); -__setup("ppchameleonevb_fio_pbase=", ppchameleonevb_fio_pbase); -#endif - -#ifdef CONFIG_MTD_PARTITIONS -/* - * Define static partitions for flash devices - */ -static struct mtd_partition partition_info_hi[] = { - { .name = "PPChameleon HI Nand Flash", - .offset = 0, - .size = 128 * 1024 * 1024 - } -}; - -static struct mtd_partition partition_info_me[] = { - { .name = "PPChameleon ME Nand Flash", - .offset = 0, - .size = 32 * 1024 * 1024 - } -}; - -static struct mtd_partition partition_info_evb[] = { - { .name = "PPChameleonEVB Nand Flash", - .offset = 0, - .size = 32 * 1024 * 1024 - } -}; - -#define NUM_PARTITIONS 1 - -extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, const char *mtd_id); -#endif - -/* - * hardware specific access to control-lines - */ -static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { -#error Missing headerfiles. No way to fix this. -tglx - switch (cmd) { - case NAND_CTL_SETCLE: - MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_CLRCLE: - MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_SETALE: - MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_CLRALE: - MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_SETNCE: - MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_CLRNCE: - MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR); - break; - } - } - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { -#error Missing headerfiles. No way to fix this. -tglx - switch (cmd) { - case NAND_CTL_SETCLE: - MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_CLRCLE: - MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_SETALE: - MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_CLRALE: - MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_SETNCE: - MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_CLRNCE: - MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR); - break; - } - } - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -#ifdef USE_READY_BUSY_PIN -/* - * read device ready pin - */ -static int ppchameleon_device_ready(struct mtd_info *minfo) -{ - if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_RB_GPIO_PIN) - return 1; - return 0; -} - -static int ppchameleonevb_device_ready(struct mtd_info *minfo) -{ - if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN) - return 1; - return 0; -} -#endif - -#ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; -const char *part_probes_evb[] = { "cmdlinepart", NULL }; -#endif - -/* - * Main initialization routine - */ -static int __init ppchameleonevb_init(void) -{ - struct nand_chip *this; - const char *part_type = 0; - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - void __iomem *ppchameleon_fio_base; - void __iomem *ppchameleonevb_fio_base; - - /********************************* - * Processor module NAND (if any) * - *********************************/ - /* Allocate memory for MTD device structure and private data */ - ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!ppchameleon_mtd) { - printk("Unable to allocate PPChameleon NAND MTD device structure.\n"); - return -ENOMEM; - } - - /* map physical address */ - ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M); - if (!ppchameleon_fio_base) { - printk("ioremap PPChameleon NAND flash failed\n"); - kfree(ppchameleon_mtd); - return -EIO; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&ppchameleon_mtd[1]); - - /* Initialize structures */ - memset(ppchameleon_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - ppchameleon_mtd->priv = this; - ppchameleon_mtd->owner = THIS_MODULE; - - /* Initialize GPIOs */ - /* Pin mapping for NAND chip */ - /* - CE GPIO_01 - CLE GPIO_02 - ALE GPIO_03 - R/B GPIO_04 - */ - /* output select */ - out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xC0FFFFFF); - /* three-state select */ - out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xC0FFFFFF); - /* enable output driver */ - out_be32((volatile unsigned *)GPIO0_TCR, - in_be32((volatile unsigned *)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN); -#ifdef USE_READY_BUSY_PIN - /* three-state select */ - out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFF3FFFFF); - /* high-impedecence */ - out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_RB_GPIO_PIN)); - /* input select */ - out_be32((volatile unsigned *)GPIO0_ISR1H, - (in_be32((volatile unsigned *)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000); -#endif - - /* insert callbacks */ - this->IO_ADDR_R = ppchameleon_fio_base; - this->IO_ADDR_W = ppchameleon_fio_base; - this->cmd_ctrl = ppchameleon_hwcontrol; -#ifdef USE_READY_BUSY_PIN - this->dev_ready = ppchameleon_device_ready; -#endif - this->chip_delay = NAND_BIG_DELAY_US; - /* ECC mode */ - this->ecc.mode = NAND_ECC_SOFT; - - /* Scan to find existence of the device (it could not be mounted) */ - if (nand_scan(ppchameleon_mtd, 1)) { - iounmap((void *)ppchameleon_fio_base); - ppchameleon_fio_base = NULL; - kfree(ppchameleon_mtd); - goto nand_evb_init; - } -#ifndef USE_READY_BUSY_PIN - /* Adjust delay if necessary */ - if (ppchameleon_mtd->size == NAND_SMALL_SIZE) - this->chip_delay = NAND_SMALL_DELAY_US; -#endif - -#ifdef CONFIG_MTD_PARTITIONS - ppchameleon_mtd->name = "ppchameleon-nand"; - mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (mtd_parts_nb == 0) { - if (ppchameleon_mtd->size == NAND_SMALL_SIZE) - mtd_parts = partition_info_me; - else - mtd_parts = partition_info_hi; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - - /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb); - - nand_evb_init: - /**************************** - * EVB NAND (always present) * - ****************************/ - /* Allocate memory for MTD device structure and private data */ - ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!ppchameleonevb_mtd) { - printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n"); - if (ppchameleon_fio_base) - iounmap(ppchameleon_fio_base); - return -ENOMEM; - } - - /* map physical address */ - ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M); - if (!ppchameleonevb_fio_base) { - printk("ioremap PPChameleonEVB NAND flash failed\n"); - kfree(ppchameleonevb_mtd); - if (ppchameleon_fio_base) - iounmap(ppchameleon_fio_base); - return -EIO; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&ppchameleonevb_mtd[1]); - - /* Initialize structures */ - memset(ppchameleonevb_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - ppchameleonevb_mtd->priv = this; - - /* Initialize GPIOs */ - /* Pin mapping for NAND chip */ - /* - CE GPIO_14 - CLE GPIO_15 - ALE GPIO_16 - R/B GPIO_31 - */ - /* output select */ - out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xFFFFFFF0); - out_be32((volatile unsigned *)GPIO0_OSRL, in_be32((volatile unsigned *)GPIO0_OSRL) & 0x3FFFFFFF); - /* three-state select */ - out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFFFFFFF0); - out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0x3FFFFFFF); - /* enable output driver */ - out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | - NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN); -#ifdef USE_READY_BUSY_PIN - /* three-state select */ - out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0xFFFFFFFC); - /* high-impedecence */ - out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN)); - /* input select */ - out_be32((volatile unsigned *)GPIO0_ISR1L, - (in_be32((volatile unsigned *)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001); -#endif - - /* insert callbacks */ - this->IO_ADDR_R = ppchameleonevb_fio_base; - this->IO_ADDR_W = ppchameleonevb_fio_base; - this->cmd_ctrl = ppchameleonevb_hwcontrol; -#ifdef USE_READY_BUSY_PIN - this->dev_ready = ppchameleonevb_device_ready; -#endif - this->chip_delay = NAND_SMALL_DELAY_US; - - /* ECC mode */ - this->ecc.mode = NAND_ECC_SOFT; - - /* Scan to find existence of the device */ - if (nand_scan(ppchameleonevb_mtd, 1)) { - iounmap((void *)ppchameleonevb_fio_base); - kfree(ppchameleonevb_mtd); - if (ppchameleon_fio_base) - iounmap(ppchameleon_fio_base); - return -ENXIO; - } -#ifdef CONFIG_MTD_PARTITIONS - ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME; - mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (mtd_parts_nb == 0) { - mtd_parts = partition_info_evb; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - - /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb); - - /* Return happy */ - return 0; -} - -module_init(ppchameleonevb_init); - -/* - * Clean up routine - */ -static void __exit ppchameleonevb_cleanup(void) -{ - struct nand_chip *this; - - /* Release resources, unregister device(s) */ - nand_release(ppchameleon_mtd); - nand_release(ppchameleonevb_mtd); - - /* Release iomaps */ - this = (struct nand_chip *) &ppchameleon_mtd[1]; - iounmap((void *) this->IO_ADDR_R); - this = (struct nand_chip *) &ppchameleonevb_mtd[1]; - iounmap((void *) this->IO_ADDR_R); - - /* Free the MTD device structure */ - kfree (ppchameleon_mtd); - kfree (ppchameleonevb_mtd); -} -module_exit(ppchameleonevb_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("DAVE Srl <support-ppchameleon@dave-tech.it>"); -MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board"); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ea2c288df3f..96b0b1d27df 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -7,6 +7,8 @@ * 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. + * + * See Documentation/mtd/nand/pxa3xx-nand.txt for more details. */ #include <linux/kernel.h> @@ -22,11 +24,30 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_mtd.h> + +#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP) +#define ARCH_HAS_DMA +#endif +#ifdef ARCH_HAS_DMA #include <mach/dma.h> -#include <plat/pxa3xx_nand.h> +#endif + +#include <linux/platform_data/mtd-nand-pxa3xx.h> #define CHIP_DELAY_TIMEOUT (2 * HZ/10) +#define NAND_STOP_DELAY (2 * HZ/50) +#define PAGE_CHUNK_SIZE (2048) + +/* + * Define a buffer size for the initial command that detects the flash device: + * STATUS, READID and PARAM. The largest of these is the PARAM command, + * needing 256 bytes. + */ +#define INIT_BUFFER_SIZE 256 /* registers and bit definitions */ #define NDCR (0x00) /* Control register */ @@ -36,6 +57,7 @@ #define NDPCR (0x18) /* Page Count Register */ #define NDBDR0 (0x1C) /* Bad Block Register 0 */ #define NDBDR1 (0x20) /* Bad Block Register 1 */ +#define NDECCCTRL (0x28) /* ECC control */ #define NDDB (0x40) /* Data Buffer */ #define NDCB0 (0x48) /* Command Buffer0 */ #define NDCB1 (0x4C) /* Command Buffer1 */ @@ -52,30 +74,39 @@ #define NDCR_ND_MODE (0x3 << 21) #define NDCR_NAND_MODE (0x0) #define NDCR_CLR_PG_CNT (0x1 << 20) -#define NDCR_CLR_ECC (0x1 << 19) +#define NDCR_STOP_ON_UNCOR (0x1 << 19) #define NDCR_RD_ID_CNT_MASK (0x7 << 16) #define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) #define NDCR_RA_START (0x1 << 15) #define NDCR_PG_PER_BLK (0x1 << 14) #define NDCR_ND_ARB_EN (0x1 << 12) +#define NDCR_INT_MASK (0xFFF) #define NDSR_MASK (0xfff) -#define NDSR_RDY (0x1 << 11) +#define NDSR_ERR_CNT_OFF (16) +#define NDSR_ERR_CNT_MASK (0x1f) +#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) +#define NDSR_RDY (0x1 << 12) +#define NDSR_FLASH_RDY (0x1 << 11) #define NDSR_CS0_PAGED (0x1 << 10) #define NDSR_CS1_PAGED (0x1 << 9) #define NDSR_CS0_CMDD (0x1 << 8) #define NDSR_CS1_CMDD (0x1 << 7) #define NDSR_CS0_BBD (0x1 << 6) #define NDSR_CS1_BBD (0x1 << 5) -#define NDSR_DBERR (0x1 << 4) -#define NDSR_SBERR (0x1 << 3) +#define NDSR_UNCORERR (0x1 << 4) +#define NDSR_CORERR (0x1 << 3) #define NDSR_WRDREQ (0x1 << 2) #define NDSR_RDDREQ (0x1 << 1) #define NDSR_WRCMDREQ (0x1) +#define NDCB0_LEN_OVRD (0x1 << 28) +#define NDCB0_ST_ROW_EN (0x1 << 26) #define NDCB0_AUTO_RS (0x1 << 25) #define NDCB0_CSEL (0x1 << 24) +#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) +#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) #define NDCB0_CMD_TYPE_MASK (0x7 << 21) #define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) #define NDCB0_NC (0x1 << 20) @@ -86,45 +117,79 @@ #define NDCB0_CMD1_MASK (0xff) #define NDCB0_ADDR_CYC_SHIFT (16) +#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ +#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ +#define EXT_CMD_TYPE_READ 4 /* Read */ +#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ +#define EXT_CMD_TYPE_FINAL 3 /* Final command */ +#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ +#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ + /* macros for registers read/write */ #define nand_writel(info, off, val) \ - __raw_writel((val), (info)->mmio_base + (off)) + writel_relaxed((val), (info)->mmio_base + (off)) #define nand_readl(info, off) \ - __raw_readl((info)->mmio_base + (off)) + readl_relaxed((info)->mmio_base + (off)) /* error code and state */ enum { ERR_NONE = 0, ERR_DMABUSERR = -1, ERR_SENDCMD = -2, - ERR_DBERR = -3, + ERR_UNCORERR = -3, ERR_BBERR = -4, - ERR_SBERR = -5, + ERR_CORERR = -5, }; enum { - STATE_READY = 0, + STATE_IDLE = 0, + STATE_PREPARED, STATE_CMD_HANDLE, STATE_DMA_READING, STATE_DMA_WRITING, STATE_DMA_DONE, STATE_PIO_READING, STATE_PIO_WRITING, + STATE_CMD_DONE, + STATE_READY, }; -struct pxa3xx_nand_info { - struct nand_chip nand_chip; +enum pxa3xx_nand_variant { + PXA3XX_NAND_VARIANT_PXA, + PXA3XX_NAND_VARIANT_ARMADA370, +}; + +struct pxa3xx_nand_host { + struct nand_chip chip; + struct mtd_info *mtd; + void *info_data; + + /* page size of attached chip */ + int use_ecc; + int cs; + + /* calculated from pxa3xx_nand_flash data */ + unsigned int col_addr_cycles; + unsigned int row_addr_cycles; + size_t read_id_bytes; +}; + +struct pxa3xx_nand_info { + struct nand_hw_control controller; struct platform_device *pdev; - struct pxa3xx_nand_cmdset *cmdset; struct clk *clk; void __iomem *mmio_base; unsigned long mmio_phys; + struct completion cmd_complete, dev_ready; unsigned int buf_start; unsigned int buf_count; + unsigned int buf_size; + unsigned int data_buff_pos; + unsigned int oob_buff_pos; /* DMA information */ int drcmr_dat; @@ -133,65 +198,50 @@ struct pxa3xx_nand_info { unsigned char *data_buff; unsigned char *oob_buff; dma_addr_t data_buff_phys; - size_t data_buff_size; int data_dma_ch; struct pxa_dma_desc *data_desc; dma_addr_t data_desc_addr; - uint32_t reg_ndcr; - - /* saved column/page_addr during CMD_SEQIN */ - int seqin_column; - int seqin_page_addr; - - /* relate to the command */ + struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; unsigned int state; + /* + * This driver supports NFCv1 (as found in PXA SoC) + * and NFCv2 (as found in Armada 370/XP SoC). + */ + enum pxa3xx_nand_variant variant; + + int cs; int use_ecc; /* use HW ECC ? */ + int ecc_bch; /* using BCH ECC? */ int use_dma; /* use DMA ? */ - - unsigned int page_size; /* page size of attached chip */ - unsigned int data_size; /* data size in FIFO */ + int use_spare; /* use spare ? */ + int need_wait; + + unsigned int data_size; /* data to be read from FIFO */ + unsigned int chunk_size; /* split commands chunk size */ + unsigned int oob_size; + unsigned int spare_size; + unsigned int ecc_size; + unsigned int ecc_err_cnt; + unsigned int max_bitflips; int retcode; - struct completion cmd_complete; + + /* cached register value */ + uint32_t reg_ndcr; + uint32_t ndtr0cs0; + uint32_t ndtr1cs0; /* generated NDCBx register values */ uint32_t ndcb0; uint32_t ndcb1; uint32_t ndcb2; - - /* timing calcuted from setting */ - uint32_t ndtr0cs0; - uint32_t ndtr1cs0; - - /* calculated from pxa3xx_nand_flash data */ - size_t oob_size; - size_t read_id_bytes; - - unsigned int col_addr_cycles; - unsigned int row_addr_cycles; + uint32_t ndcb3; }; -static int use_dma = 1; +static bool use_dma = 1; module_param(use_dma, bool, 0444); -MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW"); - -/* - * Default NAND flash controller configuration setup by the - * bootloader. This configuration is used only when pdata->keep_config is set - */ -static struct pxa3xx_nand_cmdset default_cmdset = { - .read1 = 0x3000, - .read2 = 0x0050, - .program = 0x1080, - .read_status = 0x0070, - .read_id = 0x0090, - .erase = 0xD060, - .reset = 0x00FF, - .lock = 0x002A, - .unlock = 0x2423, - .lock_status = 0x007A, -}; +MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW"); static struct pxa3xx_nand_timing timing[] = { { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, @@ -201,15 +251,73 @@ static struct pxa3xx_nand_timing timing[] = { }; static struct pxa3xx_nand_flash builtin_flash_types[] = { - { 0, 0, 2048, 8, 8, 0, &default_cmdset, &timing[0] }, - { 0x46ec, 32, 512, 16, 16, 4096, &default_cmdset, &timing[1] }, - { 0xdaec, 64, 2048, 8, 8, 2048, &default_cmdset, &timing[1] }, - { 0xd7ec, 128, 4096, 8, 8, 8192, &default_cmdset, &timing[1] }, - { 0xa12c, 64, 2048, 8, 8, 1024, &default_cmdset, &timing[2] }, - { 0xb12c, 64, 2048, 16, 16, 1024, &default_cmdset, &timing[2] }, - { 0xdc2c, 64, 2048, 8, 8, 4096, &default_cmdset, &timing[2] }, - { 0xcc2c, 64, 2048, 16, 16, 4096, &default_cmdset, &timing[2] }, - { 0xba20, 64, 2048, 16, 16, 2048, &default_cmdset, &timing[3] }, +{ "DEFAULT FLASH", 0, 0, 2048, 8, 8, 0, &timing[0] }, +{ "64MiB 16-bit", 0x46ec, 32, 512, 16, 16, 4096, &timing[1] }, +{ "256MiB 8-bit", 0xdaec, 64, 2048, 8, 8, 2048, &timing[1] }, +{ "4GiB 8-bit", 0xd7ec, 128, 4096, 8, 8, 8192, &timing[1] }, +{ "128MiB 8-bit", 0xa12c, 64, 2048, 8, 8, 1024, &timing[2] }, +{ "128MiB 16-bit", 0xb12c, 64, 2048, 16, 16, 1024, &timing[2] }, +{ "512MiB 8-bit", 0xdc2c, 64, 2048, 8, 8, 4096, &timing[2] }, +{ "512MiB 16-bit", 0xcc2c, 64, 2048, 16, 16, 4096, &timing[2] }, +{ "256MiB 16-bit", 0xba20, 64, 2048, 16, 16, 2048, &timing[3] }, +}; + +static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; +static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_mirror_pattern +}; + +static struct nand_ecclayout ecc_layout_2KB_bch4bit = { + .eccbytes = 32, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 30} } +}; + +static struct nand_ecclayout ecc_layout_4KB_bch4bit = { + .eccbytes = 64, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + /* Bootrom looks in bytes 0 & 5 for bad blocks */ + .oobfree = { {6, 26}, { 64, 32} } +}; + +static struct nand_ecclayout ecc_layout_4KB_bch8bit = { + .eccbytes = 128, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { } }; /* Define a default flash type setting serve as flash detecting only */ @@ -229,9 +337,33 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = { /* convert nano-seconds to nand flash controller clock cycles */ #define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, +static const struct of_device_id pxa3xx_nand_dt_ids[] = { + { + .compatible = "marvell,pxa3xx-nand", + .data = (void *)PXA3XX_NAND_VARIANT_PXA, + }, + { + .compatible = "marvell,armada370-nand", + .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, + }, + {} +}; +MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); + +static enum pxa3xx_nand_variant +pxa3xx_nand_get_variant(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); + if (!of_id) + return PXA3XX_NAND_VARIANT_PXA; + return (enum pxa3xx_nand_variant)of_id->data; +} + +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, const struct pxa3xx_nand_timing *t) { + struct pxa3xx_nand_info *info = host->info_data; unsigned long nand_clk = clk_get_rate(info->clk); uint32_t ndtr0, ndtr1; @@ -252,111 +384,87 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, nand_writel(info, NDTR1CS0, ndtr1); } -#define WAIT_EVENT_TIMEOUT 10 - -static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) +/* + * Set the data and OOB size, depending on the selected + * spare and ECC configuration. + * Only applicable to READ0, READOOB and PAGEPROG commands. + */ +static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info, + struct mtd_info *mtd) { - int timeout = WAIT_EVENT_TIMEOUT; - uint32_t ndsr; + int oob_enable = info->reg_ndcr & NDCR_SPARE_EN; - while (timeout--) { - ndsr = nand_readl(info, NDSR) & NDSR_MASK; - if (ndsr & event) { - nand_writel(info, NDSR, ndsr); - return 0; - } - udelay(10); - } + info->data_size = mtd->writesize; + if (!oob_enable) + return; - return -ETIMEDOUT; + info->oob_size = info->spare_size; + if (!info->use_ecc) + info->oob_size += info->ecc_size; } -static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) +/** + * NOTE: it is a must to set ND_RUN firstly, then write + * command buffer, otherwise, it does not work. + * We enable all the interrupt at the same time, and + * let pxa3xx_nand_irq to handle all logic. + */ +static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) { - int oob_enable = info->reg_ndcr & NDCR_SPARE_EN; + uint32_t ndcr; - info->data_size = info->page_size; - if (!oob_enable) { - info->oob_size = 0; - return; - } + ndcr = info->reg_ndcr; - switch (info->page_size) { - case 2048: - info->oob_size = (info->use_ecc) ? 40 : 64; - break; - case 512: - info->oob_size = (info->use_ecc) ? 8 : 16; - break; + if (info->use_ecc) { + ndcr |= NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x1); + } else { + ndcr &= ~NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x0); } -} -static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, - uint16_t cmd, int column, int page_addr) -{ - const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; - pxa3xx_set_datasize(info); - - /* generate values for NDCBx registers */ - info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); - info->ndcb1 = 0; - info->ndcb2 = 0; - info->ndcb0 |= NDCB0_ADDR_CYC(info->row_addr_cycles + info->col_addr_cycles); - - if (info->col_addr_cycles == 2) { - /* large block, 2 cycles for column address - * row address starts from 3rd cycle - */ - info->ndcb1 |= page_addr << 16; - if (info->row_addr_cycles == 3) - info->ndcb2 = (page_addr >> 16) & 0xff; - } else - /* small block, 1 cycles for column address - * row address starts from 2nd cycle - */ - info->ndcb1 = page_addr << 8; + if (info->use_dma) + ndcr |= NDCR_DMA_EN; + else + ndcr &= ~NDCR_DMA_EN; - if (cmd == cmdset->program) - info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; + if (info->use_spare) + ndcr |= NDCR_SPARE_EN; + else + ndcr &= ~NDCR_SPARE_EN; - return 0; -} + ndcr |= NDCR_ND_RUN; -static int prepare_erase_cmd(struct pxa3xx_nand_info *info, - uint16_t cmd, int page_addr) -{ - info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); - info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); - info->ndcb1 = page_addr; - info->ndcb2 = 0; - return 0; + /* clear status bits and run */ + nand_writel(info, NDCR, 0); + nand_writel(info, NDSR, NDSR_MASK); + nand_writel(info, NDCR, ndcr); } -static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) +static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) { - const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; - - info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); - info->ndcb1 = 0; - info->ndcb2 = 0; + uint32_t ndcr; + int timeout = NAND_STOP_DELAY; - info->oob_size = 0; - if (cmd == cmdset->read_id) { - info->ndcb0 |= NDCB0_CMD_TYPE(3); - info->data_size = 8; - } else if (cmd == cmdset->read_status) { - info->ndcb0 |= NDCB0_CMD_TYPE(4); - info->data_size = 8; - } else if (cmd == cmdset->reset || cmd == cmdset->lock || - cmd == cmdset->unlock) { - info->ndcb0 |= NDCB0_CMD_TYPE(5); - } else - return -EINVAL; + /* wait RUN bit in NDCR become 0 */ + ndcr = nand_readl(info, NDCR); + while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) { + ndcr = nand_readl(info, NDCR); + udelay(1); + } - return 0; + if (timeout <= 0) { + ndcr &= ~NDCR_ND_RUN; + nand_writel(info, NDCR, ndcr); + } + /* clear status bits */ + nand_writel(info, NDSR, NDSR_MASK); } -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +static void __maybe_unused +enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) { uint32_t ndcr; @@ -372,73 +480,45 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) nand_writel(info, NDCR, ndcr | int_mask); } -/* NOTE: it is a must to set ND_RUN firstly, then write command buffer - * otherwise, it does not work - */ -static int write_cmd(struct pxa3xx_nand_info *info) +static void handle_data_pio(struct pxa3xx_nand_info *info) { - uint32_t ndcr; - - /* clear status bits and run */ - nand_writel(info, NDSR, NDSR_MASK); - - ndcr = info->reg_ndcr; - - ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; - ndcr |= info->use_dma ? NDCR_DMA_EN : 0; - ndcr |= NDCR_ND_RUN; - - nand_writel(info, NDCR, ndcr); - - if (wait_for_event(info, NDSR_WRCMDREQ)) { - printk(KERN_ERR "timed out writing command\n"); - return -ETIMEDOUT; - } - - nand_writel(info, NDCB0, info->ndcb0); - nand_writel(info, NDCB0, info->ndcb1); - nand_writel(info, NDCB0, info->ndcb2); - return 0; -} - -static int handle_data_pio(struct pxa3xx_nand_info *info) -{ - int ret, timeout = CHIP_DELAY_TIMEOUT; + unsigned int do_bytes = min(info->data_size, info->chunk_size); switch (info->state) { case STATE_PIO_WRITING: - __raw_writesl(info->mmio_base + NDDB, info->data_buff, - DIV_ROUND_UP(info->data_size, 4)); - if (info->oob_size > 0) - __raw_writesl(info->mmio_base + NDDB, info->oob_buff, - DIV_ROUND_UP(info->oob_size, 4)); - - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + __raw_writesl(info->mmio_base + NDDB, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(do_bytes, 4)); - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); - if (!ret) { - printk(KERN_ERR "program command time out\n"); - return -1; - } + if (info->oob_size > 0) + __raw_writesl(info->mmio_base + NDDB, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->oob_size, 4)); break; case STATE_PIO_READING: - __raw_readsl(info->mmio_base + NDDB, info->data_buff, - DIV_ROUND_UP(info->data_size, 4)); + __raw_readsl(info->mmio_base + NDDB, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(do_bytes, 4)); + if (info->oob_size > 0) - __raw_readsl(info->mmio_base + NDDB, info->oob_buff, - DIV_ROUND_UP(info->oob_size, 4)); + __raw_readsl(info->mmio_base + NDDB, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->oob_size, 4)); break; default: - printk(KERN_ERR "%s: invalid state %d\n", __func__, + dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, info->state); - return -EINVAL; + BUG(); } - info->state = STATE_READY; - return 0; + /* Update buffer pointers for multi-page read/write */ + info->data_buff_pos += do_bytes; + info->oob_buff_pos += info->oob_size; + info->data_size -= do_bytes; } -static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) +#ifdef ARCH_HAS_DMA +static void start_data_dma(struct pxa3xx_nand_info *info) { struct pxa_dma_desc *desc = info->data_desc; int dma_len = ALIGN(info->data_size + info->oob_size, 32); @@ -446,14 +526,21 @@ static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) desc->ddadr = DDADR_STOP; desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; - if (dir_out) { + switch (info->state) { + case STATE_DMA_WRITING: desc->dsadr = info->data_buff_phys; desc->dtadr = info->mmio_phys + NDDB; desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; - } else { + break; + case STATE_DMA_READING: desc->dtadr = info->data_buff_phys; desc->dsadr = info->mmio_phys + NDDB; desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; + break; + default: + dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, + info->state); + BUG(); } DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch; @@ -471,99 +558,105 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data) if (dcsr & DCSR_BUSERR) { info->retcode = ERR_DMABUSERR; - complete(&info->cmd_complete); } - if (info->state == STATE_DMA_WRITING) { - info->state = STATE_DMA_DONE; - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - } else { - info->state = STATE_READY; - complete(&info->cmd_complete); - } + info->state = STATE_DMA_DONE; + enable_int(info, NDCR_INT_MASK); + nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); } +#else +static void start_data_dma(struct pxa3xx_nand_info *info) +{} +#endif static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) { struct pxa3xx_nand_info *info = devid; - unsigned int status; + unsigned int status, is_completed = 0, is_ready = 0; + unsigned int ready, cmd_done; - status = nand_readl(info, NDSR); + if (info->cs == 0) { + ready = NDSR_FLASH_RDY; + cmd_done = NDSR_CS0_CMDD; + } else { + ready = NDSR_RDY; + cmd_done = NDSR_CS1_CMDD; + } - if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { - if (status & NDSR_DBERR) - info->retcode = ERR_DBERR; - else if (status & NDSR_SBERR) - info->retcode = ERR_SBERR; + status = nand_readl(info, NDSR); - disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); + if (status & NDSR_UNCORERR) + info->retcode = ERR_UNCORERR; + if (status & NDSR_CORERR) { + info->retcode = ERR_CORERR; + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 && + info->ecc_bch) + info->ecc_err_cnt = NDSR_ERR_CNT(status); + else + info->ecc_err_cnt = 1; + /* + * Each chunk composing a page is corrected independently, + * and we need to store maximum number of corrected bitflips + * to return it to the MTD layer in ecc.read_page(). + */ + info->max_bitflips = max_t(unsigned int, + info->max_bitflips, + info->ecc_err_cnt); + } + if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { + /* whether use dma to transfer data */ if (info->use_dma) { - info->state = STATE_DMA_READING; - start_data_dma(info, 0); - } else { - info->state = STATE_PIO_READING; - complete(&info->cmd_complete); - } - } else if (status & NDSR_WRDREQ) { - disable_int(info, NDSR_WRDREQ); - if (info->use_dma) { - info->state = STATE_DMA_WRITING; - start_data_dma(info, 1); + disable_int(info, NDCR_INT_MASK); + info->state = (status & NDSR_RDDREQ) ? + STATE_DMA_READING : STATE_DMA_WRITING; + start_data_dma(info); + goto NORMAL_IRQ_EXIT; } else { - info->state = STATE_PIO_WRITING; - complete(&info->cmd_complete); + info->state = (status & NDSR_RDDREQ) ? + STATE_PIO_READING : STATE_PIO_WRITING; + handle_data_pio(info); } - } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { - if (status & NDSR_CS0_BBD) - info->retcode = ERR_BBERR; - - disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - info->state = STATE_READY; - complete(&info->cmd_complete); } - nand_writel(info, NDSR, status); - return IRQ_HANDLED; -} - -static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) -{ - uint32_t ndcr; - int ret, timeout = CHIP_DELAY_TIMEOUT; - - if (write_cmd(info)) { - info->retcode = ERR_SENDCMD; - goto fail_stop; + if (status & cmd_done) { + info->state = STATE_CMD_DONE; + is_completed = 1; } - - info->state = STATE_CMD_HANDLE; - - enable_int(info, event); - - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); - if (!ret) { - printk(KERN_ERR "command execution timed out\n"); - info->retcode = ERR_SENDCMD; - goto fail_stop; + if (status & ready) { + info->state = STATE_READY; + is_ready = 1; } - if (info->use_dma == 0 && info->data_size > 0) - if (handle_data_pio(info)) - goto fail_stop; - - return 0; + if (status & NDSR_WRCMDREQ) { + nand_writel(info, NDSR, NDSR_WRCMDREQ); + status &= ~NDSR_WRCMDREQ; + info->state = STATE_CMD_HANDLE; + + /* + * Command buffer registers NDCB{0-2} (and optionally NDCB3) + * must be loaded by writing directly either 12 or 16 + * bytes directly to NDCB0, four bytes at a time. + * + * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored + * but each NDCBx register can be read. + */ + nand_writel(info, NDCB0, info->ndcb0); + nand_writel(info, NDCB0, info->ndcb1); + nand_writel(info, NDCB0, info->ndcb2); -fail_stop: - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); - udelay(10); - return -ETIMEDOUT; -} + /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDCB0, info->ndcb3); + } -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) -{ - struct pxa3xx_nand_info *info = mtd->priv; - return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0; + /* clear NDSR to let the controller exit the IRQ */ + nand_writel(info, NDSR, status); + if (is_completed) + complete(&info->cmd_complete); + if (is_ready) + complete(&info->dev_ready); +NORMAL_IRQ_EXIT: + return IRQ_HANDLED; } static inline int is_buf_blank(uint8_t *buf, size_t len) @@ -574,130 +667,449 @@ static inline int is_buf_blank(uint8_t *buf, size_t len) return 1; } -static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) +static void set_command_address(struct pxa3xx_nand_info *info, + unsigned int page_size, uint16_t column, int page_addr) { - struct pxa3xx_nand_info *info = mtd->priv; - const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; - int ret; + /* small page addr setting */ + if (page_size < PAGE_CHUNK_SIZE) { + info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) + | (column & 0xFF); + + info->ndcb2 = 0; + } else { + info->ndcb1 = ((page_addr & 0xFFFF) << 16) + | (column & 0xFFFF); - info->use_dma = (use_dma) ? 1 : 0; - info->use_ecc = 0; - info->data_size = 0; - info->state = STATE_READY; + if (page_addr & 0xFF0000) + info->ndcb2 = (page_addr & 0xFF0000) >> 16; + else + info->ndcb2 = 0; + } +} - init_completion(&info->cmd_complete); +static void prepare_start_command(struct pxa3xx_nand_info *info, int command) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct mtd_info *mtd = host->mtd; + + /* reset data and oob column point to handle data */ + info->buf_start = 0; + info->buf_count = 0; + info->oob_size = 0; + info->data_buff_pos = 0; + info->oob_buff_pos = 0; + info->use_ecc = 0; + info->use_spare = 1; + info->retcode = ERR_NONE; + info->ecc_err_cnt = 0; + info->ndcb3 = 0; + info->need_wait = 0; switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_PAGEPROG: + info->use_ecc = 1; case NAND_CMD_READOOB: - /* disable HW ECC to get all the OOB data */ + pxa3xx_set_datasize(info, mtd); + break; + case NAND_CMD_PARAM: + info->use_spare = 0; + break; + default: + info->ndcb1 = 0; + info->ndcb2 = 0; + break; + } + + /* + * If we are about to issue a read command, or about to set + * the write address, then clean the data buffer. + */ + if (command == NAND_CMD_READ0 || + command == NAND_CMD_READOOB || + command == NAND_CMD_SEQIN) { + info->buf_count = mtd->writesize + mtd->oobsize; - info->buf_start = mtd->writesize + column; memset(info->data_buff, 0xFF, info->buf_count); + } - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) - break; +} - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); +static int prepare_set_command(struct pxa3xx_nand_info *info, int command, + int ext_cmd_type, uint16_t column, int page_addr) +{ + int addr_cycle, exec_cmd; + struct pxa3xx_nand_host *host; + struct mtd_info *mtd; - /* We only are OOB, so if the data has error, does not matter */ - if (info->retcode == ERR_DBERR) - info->retcode = ERR_NONE; - break; + host = info->host[info->cs]; + mtd = host->mtd; + addr_cycle = 0; + exec_cmd = 1; + if (info->cs != 0) + info->ndcb0 = NDCB0_CSEL; + else + info->ndcb0 = 0; + + if (command == NAND_CMD_SEQIN) + exec_cmd = 0; + + addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles + + host->col_addr_cycles); + + switch (command) { + case NAND_CMD_READOOB: case NAND_CMD_READ0: - info->use_ecc = 1; - info->retcode = ERR_NONE; info->buf_start = column; - info->buf_count = mtd->writesize + mtd->oobsize; - memset(info->data_buff, 0xFF, info->buf_count); - - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) - break; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | addr_cycle + | NAND_CMD_READ0; - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); + if (command == NAND_CMD_READOOB) + info->buf_start += mtd->writesize; - if (info->retcode == ERR_DBERR) { - /* for blank page (all 0xff), HW will calculate its ECC as - * 0, which is different from the ECC information within - * OOB, ignore such double bit errors - */ - if (is_buf_blank(info->data_buff, mtd->writesize)) - info->retcode = ERR_NONE; + /* + * Multiple page read needs an 'extended command type' field, + * which is either naked-read or last-read according to the + * state. + */ + if (mtd->writesize == PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); + } else if (mtd->writesize > PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->chunk_size + + info->oob_size; } + + set_command_address(info, mtd->writesize, column, page_addr); break; + case NAND_CMD_SEQIN: + info->buf_start = column; - info->buf_count = mtd->writesize + mtd->oobsize; - memset(info->data_buff, 0xff, info->buf_count); + set_command_address(info, mtd->writesize, 0, page_addr); - /* save column/page_addr for next CMD_PAGEPROG */ - info->seqin_column = column; - info->seqin_page_addr = page_addr; + /* + * Multiple page programming needs to execute the initial + * SEQIN command that sets the page address. + */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | addr_cycle + | command; + /* No data transfer in this case */ + info->data_size = 0; + exec_cmd = 1; + } break; - case NAND_CMD_PAGEPROG: - info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; - if (prepare_read_prog_cmd(info, cmdset->program, - info->seqin_column, info->seqin_page_addr)) + case NAND_CMD_PAGEPROG: + if (is_buf_blank(info->data_buff, + (mtd->writesize + mtd->oobsize))) { + exec_cmd = 0; break; + } - pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); - break; - case NAND_CMD_ERASE1: - if (prepare_erase_cmd(info, cmdset->erase, page_addr)) - break; + /* Second command setting for large pages */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + /* + * Multiple page write uses the 'extended command' + * field. This can be used to issue a command dispatch + * or a naked-write depending on the current stage. + */ + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->chunk_size + + info->oob_size; - pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + /* + * This is the command dispatch that completes a chunked + * page program operation. + */ + if (info->data_size == 0) { + info->ndcb0 = NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | command; + info->ndcb1 = 0; + info->ndcb2 = 0; + info->ndcb3 = 0; + } + } else { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_AUTO_RS + | NDCB0_ST_ROW_EN + | NDCB0_DBC + | (NAND_CMD_PAGEPROG << 8) + | NAND_CMD_SEQIN + | addr_cycle; + } break; - case NAND_CMD_ERASE2: + + case NAND_CMD_PARAM: + info->buf_count = 256; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | NDCB0_ADDR_CYC(1) + | NDCB0_LEN_OVRD + | command; + info->ndcb1 = (column & 0xFF); + info->ndcb3 = 256; + info->data_size = 256; break; + case NAND_CMD_READID: + info->buf_count = host->read_id_bytes; + info->ndcb0 |= NDCB0_CMD_TYPE(3) + | NDCB0_ADDR_CYC(1) + | command; + info->ndcb1 = (column & 0xFF); + + info->data_size = 8; + break; case NAND_CMD_STATUS: - info->use_dma = 0; /* force PIO read */ - info->buf_start = 0; - info->buf_count = (command == NAND_CMD_READID) ? - info->read_id_bytes : 1; + info->buf_count = 1; + info->ndcb0 |= NDCB0_CMD_TYPE(4) + | NDCB0_ADDR_CYC(1) + | command; - if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? - cmdset->read_id : cmdset->read_status)) - break; + info->data_size = 8; + break; + + case NAND_CMD_ERASE1: + info->ndcb0 |= NDCB0_CMD_TYPE(2) + | NDCB0_AUTO_RS + | NDCB0_ADDR_CYC(3) + | NDCB0_DBC + | (NAND_CMD_ERASE2 << 8) + | NAND_CMD_ERASE1; + info->ndcb1 = page_addr; + info->ndcb2 = 0; - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); break; case NAND_CMD_RESET: - if (prepare_other_cmd(info, cmdset->reset)) - break; + info->ndcb0 |= NDCB0_CMD_TYPE(5) + | command; - ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); - if (ret == 0) { - int timeout = 2; - uint32_t ndcr; + break; - while (timeout--) { - if (nand_readl(info, NDSR) & NDSR_RDY) - break; - msleep(10); - } + case NAND_CMD_ERASE2: + exec_cmd = 0; + break; - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + default: + exec_cmd = 0; + dev_err(&info->pdev->dev, "non-supported command %x\n", + command); + break; + } + + return exec_cmd; +} + +static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + int ret, exec_cmd; + + /* + * if this is a x16 device ,then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + prepare_start_command(info, command); + + info->state = STATE_PREPARED; + exec_cmd = prepare_set_command(info, command, 0, column, page_addr); + + if (exec_cmd) { + init_completion(&info->cmd_complete); + init_completion(&info->dev_ready); + info->need_wait = 1; + pxa3xx_nand_start(info); + + ret = wait_for_completion_timeout(&info->cmd_complete, + CHIP_DELAY_TIMEOUT); + if (!ret) { + dev_err(&info->pdev->dev, "Wait time out!!!\n"); + /* Stop State Machine for next command cycle */ + pxa3xx_nand_stop(info); } + } + info->state = STATE_IDLE; +} + +static void nand_cmdfunc_extended(struct mtd_info *mtd, + const unsigned command, + int column, int page_addr) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + int ret, exec_cmd, ext_cmd_type; + + /* + * if this is a x16 device then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + /* Select the extended command for the first command */ + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + ext_cmd_type = EXT_CMD_TYPE_MONO; + break; + case NAND_CMD_SEQIN: + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + break; + case NAND_CMD_PAGEPROG: + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; break; default: - printk(KERN_ERR "non-supported command.\n"); + ext_cmd_type = 0; break; } - if (info->retcode == ERR_DBERR) { - printk(KERN_ERR "double bit error @ page %08x\n", page_addr); - info->retcode = ERR_NONE; + prepare_start_command(info, command); + + /* + * Prepare the "is ready" completion before starting a command + * transaction sequence. If the command is not executed the + * completion will be completed, see below. + * + * We can do that inside the loop because the command variable + * is invariant and thus so is the exec_cmd. + */ + info->need_wait = 1; + init_completion(&info->dev_ready); + do { + info->state = STATE_PREPARED; + exec_cmd = prepare_set_command(info, command, ext_cmd_type, + column, page_addr); + if (!exec_cmd) { + info->need_wait = 0; + complete(&info->dev_ready); + break; + } + + init_completion(&info->cmd_complete); + pxa3xx_nand_start(info); + + ret = wait_for_completion_timeout(&info->cmd_complete, + CHIP_DELAY_TIMEOUT); + if (!ret) { + dev_err(&info->pdev->dev, "Wait time out!!!\n"); + /* Stop State Machine for next command cycle */ + pxa3xx_nand_stop(info); + break; + } + + /* Check if the sequence is complete */ + if (info->data_size == 0 && command != NAND_CMD_PAGEPROG) + break; + + /* + * After a splitted program command sequence has issued + * the command dispatch, the command sequence is complete. + */ + if (info->data_size == 0 && + command == NAND_CMD_PAGEPROG && + ext_cmd_type == EXT_CMD_TYPE_DISPATCH) + break; + + if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { + /* Last read: issue a 'last naked read' */ + if (info->data_size == info->chunk_size) + ext_cmd_type = EXT_CMD_TYPE_LAST_RW; + else + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; + + /* + * If a splitted program command has no more data to transfer, + * the command dispatch must be issued to complete. + */ + } else if (command == NAND_CMD_PAGEPROG && + info->data_size == 0) { + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + } + } while (1); + + info->state = STATE_IDLE; +} + +static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (info->retcode == ERR_CORERR && info->use_ecc) { + mtd->ecc_stats.corrected += info->ecc_err_cnt; + + } else if (info->retcode == ERR_UNCORERR) { + /* + * for blank page (all 0xff), HW will calculate its ECC as + * 0, which is different from the ECC information within + * OOB, ignore such uncorrectable errors + */ + if (is_buf_blank(buf, mtd->writesize)) + info->retcode = ERR_NONE; + else + mtd->ecc_stats.failed++; } + + return info->max_bitflips; } static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) { - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; char retval = 0xFF; if (info->buf_start < info->buf_count) @@ -709,7 +1121,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) { - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; u16 retval = 0xFFFF; if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { @@ -721,7 +1134,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; int real_len = min_t(size_t, len, info->buf_count - info->buf_start); memcpy(buf, info->data_buff + info->buf_start, real_len); @@ -731,19 +1145,14 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) static void pxa3xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; int real_len = min_t(size_t, len, info->buf_count - info->buf_start); memcpy(info->data_buff + info->buf_start, buf, real_len); info->buf_start += real_len; } -static int pxa3xx_nand_verify_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - return 0; -} - static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) { return; @@ -751,238 +1160,121 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) { - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + int ret; + + if (info->need_wait) { + ret = wait_for_completion_timeout(&info->dev_ready, + CHIP_DELAY_TIMEOUT); + info->need_wait = 0; + if (!ret) { + dev_err(&info->pdev->dev, "Ready time out!!!\n"); + return NAND_STATUS_FAIL; + } + } /* pxa3xx_nand_send_command has waited for command complete */ if (this->state == FL_WRITING || this->state == FL_ERASING) { if (info->retcode == ERR_NONE) return 0; - else { - /* - * any error make it return 0x01 which will tell - * the caller the erase and write fail - */ - return 0x01; - } - } - - return 0; -} - -static void pxa3xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode) -{ - return; -} - -static int pxa3xx_nand_ecc_calculate(struct mtd_info *mtd, - const uint8_t *dat, uint8_t *ecc_code) -{ - return 0; -} - -static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, - uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) -{ - struct pxa3xx_nand_info *info = mtd->priv; - /* - * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we - * consider it as a ecc error which will tell the caller the - * read fail We have distinguish all the errors, but the - * nand_read_ecc only check this function return value - * - * Corrected (single-bit) errors must also be noted. - */ - if (info->retcode == ERR_SBERR) - return 1; - else if (info->retcode != ERR_NONE) - return -1; - - return 0; -} - -static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) -{ - const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; - uint32_t ndcr; - uint8_t id_buff[8]; - - if (prepare_other_cmd(info, cmdset->read_id)) { - printk(KERN_ERR "failed to prepare command\n"); - return -EINVAL; + else + return NAND_STATUS_FAIL; } - /* Send command */ - if (write_cmd(info)) - goto fail_timeout; - - /* Wait for CMDDM(command done successfully) */ - if (wait_for_event(info, NDSR_RDDREQ)) - goto fail_timeout; - - __raw_readsl(info->mmio_base + NDDB, id_buff, 2); - *id = id_buff[0] | (id_buff[1] << 8); - return 0; - -fail_timeout: - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); - udelay(10); - return -ETIMEDOUT; + return NAND_STATUS_READY; } static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_flash *f) { struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; - uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct pxa3xx_nand_host *host = info->host[info->cs]; + uint32_t ndcr = 0x0; /* enable all interrupts */ - if (f->page_size != 2048 && f->page_size != 512) + if (f->page_size != 2048 && f->page_size != 512) { + dev_err(&pdev->dev, "Current only support 2048 and 512 size\n"); return -EINVAL; + } - if (f->flash_width != 16 && f->flash_width != 8) + if (f->flash_width != 16 && f->flash_width != 8) { + dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n"); return -EINVAL; + } /* calculate flash information */ - info->cmdset = f->cmdset; - info->page_size = f->page_size; - info->oob_buff = info->data_buff + f->page_size; - info->read_id_bytes = (f->page_size == 2048) ? 4 : 2; + host->read_id_bytes = (f->page_size == 2048) ? 4 : 2; /* calculate addressing information */ - info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; + host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; if (f->num_blocks * f->page_per_block > 65536) - info->row_addr_cycles = 3; + host->row_addr_cycles = 3; else - info->row_addr_cycles = 2; + host->row_addr_cycles = 2; ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0; + ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0; ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0; ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0; ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; - ndcr |= NDCR_RD_ID_CNT(info->read_id_bytes); + ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes); ndcr |= NDCR_SPARE_EN; /* enable spare by default */ info->reg_ndcr = ndcr; - pxa3xx_nand_set_timing(info, f->timing); + pxa3xx_nand_set_timing(host, f->timing); return 0; } static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) { + /* + * We set 0 by hard coding here, for we don't support keep_config + * when there is more than one chip attached to the controller + */ + struct pxa3xx_nand_host *host = info->host[0]; uint32_t ndcr = nand_readl(info, NDCR); - struct nand_flash_dev *type = NULL; - uint32_t id = -1, page_per_block, num_blocks; - int i; - - page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32; - info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; - /* set info fields needed to __readid */ - info->read_id_bytes = (info->page_size == 2048) ? 4 : 2; - info->reg_ndcr = ndcr; - info->cmdset = &default_cmdset; - if (__readid(info, &id)) - return -ENODEV; - - /* Lookup the flash id */ - id = (id >> 8) & 0xff; /* device id is byte 2 */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (id == nand_flash_ids[i].id) { - type = &nand_flash_ids[i]; - break; - } + if (ndcr & NDCR_PAGE_SZ) { + /* Controller's FIFO size */ + info->chunk_size = 2048; + host->read_id_bytes = 4; + } else { + info->chunk_size = 512; + host->read_id_bytes = 2; } - if (!type) - return -ENODEV; - - /* fill the missing flash information */ - i = __ffs(page_per_block * info->page_size); - num_blocks = type->chipsize << (20 - i); - - /* calculate addressing information */ - info->col_addr_cycles = (info->page_size == 2048) ? 2 : 1; - - if (num_blocks * page_per_block > 65536) - info->row_addr_cycles = 3; - else - info->row_addr_cycles = 2; - + /* Set an initial chunk size */ + info->reg_ndcr = ndcr & ~NDCR_INT_MASK; info->ndtr0cs0 = nand_readl(info, NDTR0CS0); info->ndtr1cs0 = nand_readl(info, NDTR1CS0); - return 0; } -static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, - const struct pxa3xx_nand_platform_data *pdata) -{ - const struct pxa3xx_nand_flash *f; - uint32_t id = -1; - int i; - - if (pdata->keep_config) - if (pxa3xx_nand_detect_config(info) == 0) - return 0; - - /* we use default timing to detect id */ - f = DEFAULT_FLASH_TYPE; - pxa3xx_nand_config_flash(info, f); - if (__readid(info, &id)) - goto fail_detect; - - for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) { - /* we first choose the flash definition from platfrom */ - if (i < pdata->num_flash) - f = pdata->flash + i; - else - f = &builtin_flash_types[i - pdata->num_flash + 1]; - if (f->chip_id == id) { - dev_info(&info->pdev->dev, "detect chip id: 0x%x\n", id); - pxa3xx_nand_config_flash(info, f); - return 0; - } - } - - dev_warn(&info->pdev->dev, - "failed to detect configured nand flash; found %04x instead of\n", - id); -fail_detect: - return -ENODEV; -} - -/* the maximum possible buffer size for large page with OOB data - * is: 2048 + 64 = 2112 bytes, allocate a page here for both the - * data buffer and the DMA descriptor - */ -#define MAX_BUFF_SIZE PAGE_SIZE - +#ifdef ARCH_HAS_DMA static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) { struct platform_device *pdev = info->pdev; - int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc); + int data_desc_offset = info->buf_size - sizeof(struct pxa_dma_desc); if (use_dma == 0) { - info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL); + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); if (info->data_buff == NULL) return -ENOMEM; return 0; } - info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE, + info->data_buff = dma_alloc_coherent(&pdev->dev, info->buf_size, &info->data_buff_phys, GFP_KERNEL); if (info->data_buff == NULL) { dev_err(&pdev->dev, "failed to allocate dma buffer\n"); return -ENOMEM; } - info->data_buff_size = MAX_BUFF_SIZE; info->data_desc = (void *)info->data_buff + data_desc_offset; info->data_desc_addr = info->data_buff_phys + data_desc_offset; @@ -990,268 +1282,587 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) pxa3xx_nand_data_dma_irq, info); if (info->data_dma_ch < 0) { dev_err(&pdev->dev, "failed to request data dma\n"); - dma_free_coherent(&pdev->dev, info->data_buff_size, + dma_free_coherent(&pdev->dev, info->buf_size, info->data_buff, info->data_buff_phys); return info->data_dma_ch; } + /* + * Now that DMA buffers are allocated we turn on + * DMA proper for I/O operations. + */ + info->use_dma = 1; return 0; } -static struct nand_ecclayout hw_smallpage_ecclayout = { - .eccbytes = 6, - .eccpos = {8, 9, 10, 11, 12, 13 }, - .oobfree = { {2, 6} } -}; +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + if (info->use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_coherent(&pdev->dev, info->buf_size, + info->data_buff, info->data_buff_phys); + } else { + kfree(info->data_buff); + } +} +#else +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + return 0; +} -static struct nand_ecclayout hw_largepage_ecclayout = { - .eccbytes = 24, - .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { {2, 38} } -}; +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + kfree(info->data_buff); +} +#endif -static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, - struct pxa3xx_nand_info *info) +static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info) { - struct nand_chip *this = &info->nand_chip; - - this->options = (info->reg_ndcr & NDCR_DWIDTH_C) ? NAND_BUSWIDTH_16: 0; - - this->waitfunc = pxa3xx_nand_waitfunc; - this->select_chip = pxa3xx_nand_select_chip; - this->dev_ready = pxa3xx_nand_dev_ready; - this->cmdfunc = pxa3xx_nand_cmdfunc; - this->read_word = pxa3xx_nand_read_word; - this->read_byte = pxa3xx_nand_read_byte; - this->read_buf = pxa3xx_nand_read_buf; - this->write_buf = pxa3xx_nand_write_buf; - this->verify_buf = pxa3xx_nand_verify_buf; - - this->ecc.mode = NAND_ECC_HW; - this->ecc.hwctl = pxa3xx_nand_ecc_hwctl; - this->ecc.calculate = pxa3xx_nand_ecc_calculate; - this->ecc.correct = pxa3xx_nand_ecc_correct; - this->ecc.size = info->page_size; - - if (info->page_size == 2048) - this->ecc.layout = &hw_largepage_ecclayout; - else - this->ecc.layout = &hw_smallpage_ecclayout; + struct mtd_info *mtd; + struct nand_chip *chip; + int ret; + + mtd = info->host[info->cs]->mtd; + chip = mtd->priv; + + /* use the common timing to make a try */ + ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]); + if (ret) + return ret; - this->chip_delay = 25; + chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0); + ret = chip->waitfunc(mtd, chip); + if (ret & NAND_STATUS_FAIL) + return -ENODEV; + + return 0; } -static int pxa3xx_nand_probe(struct platform_device *pdev) +static int pxa_ecc_init(struct pxa3xx_nand_info *info, + struct nand_ecc_ctrl *ecc, + int strength, int ecc_stepsize, int page_size) { - struct pxa3xx_nand_platform_data *pdata; - struct pxa3xx_nand_info *info; - struct nand_chip *this; - struct mtd_info *mtd; - struct resource *r; - int ret = 0, irq; + if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { + info->chunk_size = 2048; + info->spare_size = 40; + info->ecc_size = 24; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; + + } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { + info->chunk_size = 512; + info->spare_size = 8; + info->ecc_size = 8; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; - pdata = pdev->dev.platform_data; + /* + * Required ECC: 4-bit correction per 512 bytes + * Select: 16-bit correction per 2048 bytes + */ + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { + info->ecc_bch = 1; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_2KB_bch4bit; + ecc->strength = 16; + + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_4KB_bch4bit; + ecc->strength = 16; - if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); + /* + * Required ECC: 8-bit correction per 512 bytes + * Select: 16-bit correction per 1024 bytes + */ + } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->chunk_size = 1024; + info->spare_size = 0; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_4KB_bch8bit; + ecc->strength = 16; + } else { + dev_err(&info->pdev->dev, + "ECC strength %d at page size %d is not supported\n", + strength, page_size); return -ENODEV; } - mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info), - GFP_KERNEL); - if (!mtd) { - dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; + dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n", + ecc->strength, ecc->size); + return 0; +} + +static int pxa3xx_nand_scan(struct mtd_info *mtd) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + struct platform_device *pdev = info->pdev; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL; + const struct pxa3xx_nand_flash *f = NULL; + struct nand_chip *chip = mtd->priv; + uint32_t id = -1; + uint64_t chipsize; + int i, ret, num; + uint16_t ecc_strength, ecc_step; + + if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) + goto KEEP_CONFIG; + + ret = pxa3xx_nand_sensing(info); + if (ret) { + dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", + info->cs); + + return ret; } - info = (struct pxa3xx_nand_info *)(&mtd[1]); - info->pdev = pdev; + chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0); + id = *((uint16_t *)(info->data_buff)); + if (id != 0) + dev_info(&info->pdev->dev, "Detect a flash id %x\n", id); + else { + dev_warn(&info->pdev->dev, + "Read out ID 0, potential timing set wrong!!\n"); - this = &info->nand_chip; - mtd->priv = info; - mtd->owner = THIS_MODULE; + return -EINVAL; + } - info->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed to get nand clock\n"); - ret = PTR_ERR(info->clk); - goto fail_free_mtd; + num = ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; + for (i = 0; i < num; i++) { + if (i < pdata->num_flash) + f = pdata->flash + i; + else + f = &builtin_flash_types[i - pdata->num_flash + 1]; + + /* find the chip in default list */ + if (f->chip_id == id) + break; } - clk_enable(info->clk); - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for data DMA\n"); - ret = -ENXIO; - goto fail_put_clk; + if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) { + dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n"); + + return -EINVAL; } - info->drcmr_dat = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for command DMA\n"); - ret = -ENXIO; - goto fail_put_clk; + ret = pxa3xx_nand_config_flash(info, f); + if (ret) { + dev_err(&info->pdev->dev, "ERROR! Configure failed\n"); + return ret; } - info->drcmr_cmd = r->start; - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - ret = -ENXIO; - goto fail_put_clk; + pxa3xx_flash_ids[0].name = f->name; + pxa3xx_flash_ids[0].dev_id = (f->chip_id >> 8) & 0xffff; + pxa3xx_flash_ids[0].pagesize = f->page_size; + chipsize = (uint64_t)f->num_blocks * f->page_per_block * f->page_size; + pxa3xx_flash_ids[0].chipsize = chipsize >> 20; + pxa3xx_flash_ids[0].erasesize = f->page_size * f->page_per_block; + if (f->flash_width == 16) + pxa3xx_flash_ids[0].options = NAND_BUSWIDTH_16; + pxa3xx_flash_ids[1].name = NULL; + def = pxa3xx_flash_ids; +KEEP_CONFIG: + if (info->reg_ndcr & NDCR_DWIDTH_M) + chip->options |= NAND_BUSWIDTH_16; + + /* Device detection must be done with ECC disabled */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDECCCTRL, 0x0); + + if (nand_scan_ident(mtd, 1, def)) + return -ENODEV; + + if (pdata->flash_bbt) { + /* + * We'll use a bad block table stored in-flash and don't + * allow writing the bad block marker to the flash. + */ + chip->bbt_options |= NAND_BBT_USE_FLASH | + NAND_BBT_NO_OOB_BBM; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; } - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IO memory resource defined\n"); - ret = -ENODEV; - goto fail_put_clk; + /* + * If the page size is bigger than the FIFO size, let's check + * we are given the right variant and then switch to the extended + * (aka splitted) command handling, + */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) { + chip->cmdfunc = nand_cmdfunc_extended; + } else { + dev_err(&info->pdev->dev, + "unsupported page size on this variant\n"); + return -ENODEV; + } } - r = request_mem_region(r->start, resource_size(r), pdev->name); - if (r == NULL) { - dev_err(&pdev->dev, "failed to request memory resource\n"); - ret = -EBUSY; - goto fail_put_clk; + if (pdata->ecc_strength && pdata->ecc_step_size) { + ecc_strength = pdata->ecc_strength; + ecc_step = pdata->ecc_step_size; + } else { + ecc_strength = chip->ecc_strength_ds; + ecc_step = chip->ecc_step_ds; } - info->mmio_base = ioremap(r->start, resource_size(r)); - if (info->mmio_base == NULL) { - dev_err(&pdev->dev, "ioremap() failed\n"); - ret = -ENODEV; - goto fail_free_res; + /* Set default ECC strength requirements on non-ONFI devices */ + if (ecc_strength < 1 && ecc_step < 1) { + ecc_strength = 1; + ecc_step = 512; } - info->mmio_phys = r->start; + ret = pxa_ecc_init(info, &chip->ecc, ecc_strength, + ecc_step, mtd->writesize); + if (ret) + return ret; + + /* calculate addressing information */ + if (mtd->writesize >= 2048) + host->col_addr_cycles = 2; + else + host->col_addr_cycles = 1; + + /* release the initial buffer */ + kfree(info->data_buff); + + /* allocate the real data + oob buffer */ + info->buf_size = mtd->writesize + mtd->oobsize; ret = pxa3xx_nand_init_buff(info); if (ret) - goto fail_free_io; + return ret; + info->oob_buff = info->data_buff + mtd->writesize; - /* initialize all interrupts to be disabled */ - disable_int(info, NDSR_MASK); + if ((mtd->size >> chip->page_shift) > 65536) + host->row_addr_cycles = 3; + else + host->row_addr_cycles = 2; + return nand_scan_tail(mtd); +} - ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED, - pdev->name, info); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ\n"); - goto fail_free_buf; - } +static int alloc_nand_resource(struct platform_device *pdev) +{ + struct pxa3xx_nand_platform_data *pdata; + struct pxa3xx_nand_info *info; + struct pxa3xx_nand_host *host; + struct nand_chip *chip = NULL; + struct mtd_info *mtd; + struct resource *r; + int ret, irq, cs; - ret = pxa3xx_nand_detect_flash(info, pdata); - if (ret) { - dev_err(&pdev->dev, "failed to detect flash\n"); - ret = -ENODEV; - goto fail_free_irq; + pdata = dev_get_platdata(&pdev->dev); + info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) + + sizeof(*host)) * pdata->num_cs, GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdev = pdev; + info->variant = pxa3xx_nand_get_variant(pdev); + for (cs = 0; cs < pdata->num_cs; cs++) { + mtd = (struct mtd_info *)((unsigned int)&info[1] + + (sizeof(*mtd) + sizeof(*host)) * cs); + chip = (struct nand_chip *)(&mtd[1]); + host = (struct pxa3xx_nand_host *)chip; + info->host[cs] = host; + host->mtd = mtd; + host->cs = cs; + host->info_data = info; + mtd->priv = host; + mtd->owner = THIS_MODULE; + + chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; + chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; + chip->controller = &info->controller; + chip->waitfunc = pxa3xx_nand_waitfunc; + chip->select_chip = pxa3xx_nand_select_chip; + chip->read_word = pxa3xx_nand_read_word; + chip->read_byte = pxa3xx_nand_read_byte; + chip->read_buf = pxa3xx_nand_read_buf; + chip->write_buf = pxa3xx_nand_write_buf; + chip->options |= NAND_NO_SUBPAGE_WRITE; + chip->cmdfunc = nand_cmdfunc; } - pxa3xx_nand_init_mtd(mtd, info); + spin_lock_init(&chip->controller->lock); + init_waitqueue_head(&chip->controller->wq); + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed to get nand clock\n"); + return PTR_ERR(info->clk); + } + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; - platform_set_drvdata(pdev, mtd); + if (use_dma) { + /* + * This is a dirty hack to make this driver work from + * devicetree bindings. It can be removed once we have + * a prober DMA controller framework for DT. + */ + if (pdev->dev.of_node && + of_machine_is_compatible("marvell,pxa3xx")) { + info->drcmr_dat = 97; + info->drcmr_cmd = 99; + } else { + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r == NULL) { + dev_err(&pdev->dev, + "no resource defined for data DMA\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + info->drcmr_dat = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r == NULL) { + dev_err(&pdev->dev, + "no resource defined for cmd DMA\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + info->drcmr_cmd = r->start; + } + } - if (nand_scan(mtd, 1)) { - dev_err(&pdev->dev, "failed to scan nand\n"); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); ret = -ENXIO; - goto fail_free_irq; + goto fail_disable_clk; } -#ifdef CONFIG_MTD_PARTITIONS - if (mtd_has_cmdlinepart()) { - static const char *probes[] = { "cmdlinepart", NULL }; - struct mtd_partition *parts; - int nr_parts; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(info->mmio_base)) { + ret = PTR_ERR(info->mmio_base); + goto fail_disable_clk; + } + info->mmio_phys = r->start; - nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0); + /* Allocate a buffer to allow flash detection */ + info->buf_size = INIT_BUFFER_SIZE; + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) { + ret = -ENOMEM; + goto fail_disable_clk; + } - if (nr_parts) - return add_mtd_partitions(mtd, parts, nr_parts); + /* initialize all interrupts to be disabled */ + disable_int(info, NDSR_MASK); + + ret = request_irq(irq, pxa3xx_nand_irq, 0, pdev->name, info); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto fail_free_buf; } - return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); -#else + platform_set_drvdata(pdev, info); + return 0; -#endif -fail_free_irq: - free_irq(irq, info); fail_free_buf: - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_coherent(&pdev->dev, info->data_buff_size, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); -fail_free_io: - iounmap(info->mmio_base); -fail_free_res: - release_mem_region(r->start, resource_size(r)); -fail_put_clk: - clk_disable(info->clk); - clk_put(info->clk); -fail_free_mtd: - kfree(mtd); + free_irq(irq, info); + kfree(info->data_buff); +fail_disable_clk: + clk_disable_unprepare(info->clk); return ret; } static int pxa3xx_nand_remove(struct platform_device *pdev) { - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct pxa3xx_nand_info *info = mtd->priv; - struct resource *r; - int irq; + struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); + struct pxa3xx_nand_platform_data *pdata; + int irq, cs; - platform_set_drvdata(pdev, NULL); + if (!info) + return 0; + + pdata = dev_get_platdata(&pdev->dev); - del_mtd_device(mtd); -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(mtd); -#endif irq = platform_get_irq(pdev, 0); if (irq >= 0) free_irq(irq, info); + pxa3xx_nand_free_buff(info); + + clk_disable_unprepare(info->clk); + + for (cs = 0; cs < pdata->num_cs; cs++) + nand_release(info->host[cs]->mtd); + return 0; +} + +static int pxa3xx_nand_probe_dt(struct platform_device *pdev) +{ + struct pxa3xx_nand_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); + + if (!of_id) + return 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (of_get_property(np, "marvell,nand-enable-arbiter", NULL)) + pdata->enable_arbiter = 1; + if (of_get_property(np, "marvell,nand-keep-config", NULL)) + pdata->keep_config = 1; + of_property_read_u32(np, "num-cs", &pdata->num_cs); + pdata->flash_bbt = of_get_nand_on_flash_bbt(np); + + pdata->ecc_strength = of_get_nand_ecc_strength(np); + if (pdata->ecc_strength < 0) + pdata->ecc_strength = 0; + + pdata->ecc_step_size = of_get_nand_ecc_step_size(np); + if (pdata->ecc_step_size < 0) + pdata->ecc_step_size = 0; + + pdev->dev.platform_data = pdata; + + return 0; +} + +static int pxa3xx_nand_probe(struct platform_device *pdev) +{ + struct pxa3xx_nand_platform_data *pdata; + struct mtd_part_parser_data ppdata = {}; + struct pxa3xx_nand_info *info; + int ret, cs, probe_success; + +#ifndef ARCH_HAS_DMA if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_writecombine(&pdev->dev, info->data_buff_size, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); + use_dma = 0; + dev_warn(&pdev->dev, + "This platform can't do DMA on this device\n"); + } +#endif + ret = pxa3xx_nand_probe_dt(pdev); + if (ret) + return ret; - iounmap(info->mmio_base); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -ENODEV; + } - clk_disable(info->clk); - clk_put(info->clk); + ret = alloc_nand_resource(pdev); + if (ret) { + dev_err(&pdev->dev, "alloc nand resource failed\n"); + return ret; + } + + info = platform_get_drvdata(pdev); + probe_success = 0; + for (cs = 0; cs < pdata->num_cs; cs++) { + struct mtd_info *mtd = info->host[cs]->mtd; + + /* + * The mtd name matches the one used in 'mtdparts' kernel + * parameter. This name cannot be changed or otherwise + * user's mtd partitions configuration would get broken. + */ + mtd->name = "pxa3xx_nand-0"; + info->cs = cs; + ret = pxa3xx_nand_scan(mtd); + if (ret) { + dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", + cs); + continue; + } + + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(mtd, NULL, + &ppdata, pdata->parts[cs], + pdata->nr_parts[cs]); + if (!ret) + probe_success = 1; + } + + if (!probe_success) { + pxa3xx_nand_remove(pdev); + return -ENODEV; + } - kfree(mtd); return 0; } #ifdef CONFIG_PM static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) { - struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); + struct pxa3xx_nand_platform_data *pdata; + struct mtd_info *mtd; + int cs; - if (info->state != STATE_READY) { + pdata = dev_get_platdata(&pdev->dev); + if (info->state) { dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); return -EAGAIN; } + for (cs = 0; cs < pdata->num_cs; cs++) { + mtd = info->host[cs]->mtd; + mtd_suspend(mtd); + } + return 0; } static int pxa3xx_nand_resume(struct platform_device *pdev) { - struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); + struct pxa3xx_nand_platform_data *pdata; + struct mtd_info *mtd; + int cs; - nand_writel(info, NDTR0CS0, info->ndtr0cs0); - nand_writel(info, NDTR1CS0, info->ndtr1cs0); - clk_enable(info->clk); + pdata = dev_get_platdata(&pdev->dev); + /* We don't want to handle interrupt without calling mtd routine */ + disable_int(info, NDCR_INT_MASK); + + /* + * Directly set the chip select to a invalid value, + * then the driver would reset the timing according + * to current chip select at the beginning of cmdfunc + */ + info->cs = 0xff; + + /* + * As the spec says, the NDSR would be updated to 0x1800 when + * doing the nand_clk disable/enable. + * To prevent it damaging state machine of the driver, clear + * all status before resume + */ + nand_writel(info, NDSR, NDSR_MASK); + for (cs = 0; cs < pdata->num_cs; cs++) { + mtd = info->host[cs]->mtd; + mtd_resume(mtd); + } return 0; } @@ -1263,6 +1874,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev) static struct platform_driver pxa3xx_nand_driver = { .driver = { .name = "pxa3xx-nand", + .of_match_table = pxa3xx_nand_dt_ids, }, .probe = pxa3xx_nand_probe, .remove = pxa3xx_nand_remove, @@ -1270,17 +1882,7 @@ static struct platform_driver pxa3xx_nand_driver = { .resume = pxa3xx_nand_resume, }; -static int __init pxa3xx_nand_init(void) -{ - return platform_driver_register(&pxa3xx_nand_driver); -} -module_init(pxa3xx_nand_init); - -static void __exit pxa3xx_nand_exit(void) -{ - platform_driver_unregister(&pxa3xx_nand_driver); -} -module_exit(pxa3xx_nand_exit); +module_platform_driver(pxa3xx_nand_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PXA3xx NAND controller driver"); diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 6322d1fb5d6..baea83f4dea 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -22,7 +22,7 @@ #include "r852.h" -static int r852_enable_dma = 1; +static bool r852_enable_dma = 1; module_param(r852_enable_dma, bool, S_IRUGO); MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)"); @@ -181,11 +181,11 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) /* Set dma direction */ dev->dma_dir = do_read; dev->dma_stage = 1; - INIT_COMPLETION(dev->dma_done); + reinit_completion(&dev->dma_done); dbg_verbose("doing dma %s ", do_read ? "read" : "write"); - /* Set intial dma state: for reading first fill on board buffer, + /* Set initial dma state: for reading first fill on board buffer, from device, for writes first fill the buffer from memory*/ dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY; @@ -229,7 +229,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) /* * Program data lines of the nand chip to send data to it */ -void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct r852_device *dev = r852_get_dev(mtd); uint32_t reg; @@ -245,7 +245,7 @@ void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) } /* write DWORD chinks - faster */ - while (len) { + while (len >= 4) { reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; r852_write_reg_dword(dev, R852_DATALINE, reg); buf += 4; @@ -254,14 +254,16 @@ void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) } /* write rest */ - while (len) + while (len > 0) { r852_write_reg(dev, R852_DATALINE, *buf++); + len--; + } } /* * Read data lines of the nand chip to retrieve data */ -void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct r852_device *dev = r852_get_dev(mtd); uint32_t reg; @@ -309,31 +311,10 @@ static uint8_t r852_read_byte(struct mtd_info *mtd) return r852_read_reg(dev, R852_DATALINE); } - -/* - * Readback the buffer to verify it - */ -int r852_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct r852_device *dev = r852_get_dev(mtd); - - /* We can't be sure about anything here... */ - if (dev->card_unstable) - return -1; - - /* This will never happen, unless you wired up a nand chip - with > 512 bytes page size to the reader */ - if (len > SM_SECTOR_SIZE) - return 0; - - r852_read_buf(mtd, dev->tmp_buffer, len); - return memcmp(buf, dev->tmp_buffer, len); -} - /* * Control several chip lines & send commands */ -void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) +static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) { struct r852_device *dev = r852_get_dev(mtd); @@ -378,7 +359,7 @@ void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) * Wait till card is ready. * based on nand_wait, but returns errors on DMA error */ -int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) +static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct r852_device *dev = chip->priv; @@ -407,7 +388,7 @@ int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) * Check if card is ready */ -int r852_ready(struct mtd_info *mtd) +static int r852_ready(struct mtd_info *mtd) { struct r852_device *dev = r852_get_dev(mtd); return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); @@ -418,7 +399,7 @@ int r852_ready(struct mtd_info *mtd) * Set ECC engine mode */ -void r852_ecc_hwctl(struct mtd_info *mtd, int mode) +static void r852_ecc_hwctl(struct mtd_info *mtd, int mode) { struct r852_device *dev = r852_get_dev(mtd); @@ -450,7 +431,7 @@ void r852_ecc_hwctl(struct mtd_info *mtd, int mode) * Calculate ECC, only used for writes */ -int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, +static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) { struct r852_device *dev = r852_get_dev(mtd); @@ -482,7 +463,7 @@ int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, * Correct the data using ECC, hw did almost everything for us */ -int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, +static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { uint16_t ecc_reg; @@ -539,21 +520,18 @@ exit: * nand_read_oob_syndrome assumes we can send column address - we can't */ static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /* * Start the nand engine */ -void r852_engine_enable(struct r852_device *dev) +static void r852_engine_enable(struct r852_device *dev) { if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); @@ -571,7 +549,7 @@ void r852_engine_enable(struct r852_device *dev) * Stop the nand engine */ -void r852_engine_disable(struct r852_device *dev) +static void r852_engine_disable(struct r852_device *dev) { r852_write_reg_dword(dev, R852_HW, 0); r852_write_reg(dev, R852_CTL, R852_CTL_RESET); @@ -581,7 +559,7 @@ void r852_engine_disable(struct r852_device *dev) * Test if card is present */ -void r852_card_update_present(struct r852_device *dev) +static void r852_card_update_present(struct r852_device *dev) { unsigned long flags; uint8_t reg; @@ -596,7 +574,7 @@ void r852_card_update_present(struct r852_device *dev) * Update card detection IRQ state according to current card state * which is read in r852_card_update_present */ -void r852_update_card_detect(struct r852_device *dev) +static void r852_update_card_detect(struct r852_device *dev) { int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); dev->card_unstable = 0; @@ -610,8 +588,8 @@ void r852_update_card_detect(struct r852_device *dev) r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); } -ssize_t r852_media_type_show(struct device *sys_dev, - struct device_attribute *attr, char *buf) +static ssize_t r852_media_type_show(struct device *sys_dev, + struct device_attribute *attr, char *buf) { struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); struct r852_device *dev = r852_get_dev(mtd); @@ -621,11 +599,11 @@ ssize_t r852_media_type_show(struct device *sys_dev, return strlen(data); } -DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); +static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); /* Detect properties of card in slot */ -void r852_update_media_status(struct r852_device *dev) +static void r852_update_media_status(struct r852_device *dev) { uint8_t reg; unsigned long flags; @@ -654,7 +632,7 @@ void r852_update_media_status(struct r852_device *dev) * Register the nand device * Called when the card is detected */ -int r852_register_nand_device(struct r852_device *dev) +static int r852_register_nand_device(struct r852_device *dev) { dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); @@ -692,7 +670,7 @@ error1: * Unregister the card */ -void r852_unregister_nand_device(struct r852_device *dev) +static void r852_unregister_nand_device(struct r852_device *dev) { if (!dev->card_registred) return; @@ -706,7 +684,7 @@ void r852_unregister_nand_device(struct r852_device *dev) } /* Card state updater */ -void r852_card_detect_work(struct work_struct *work) +static void r852_card_detect_work(struct work_struct *work) { struct r852_device *dev = container_of(work, struct r852_device, card_detect_work.work); @@ -766,7 +744,7 @@ static irqreturn_t r852_irq(int irq, void *data) ret = IRQ_HANDLED; dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT); - /* we shouldn't recieve any interrupts if we wait for card + /* we shouldn't receive any interrupts if we wait for card to settle */ WARN_ON(dev->card_unstable); @@ -794,13 +772,13 @@ static irqreturn_t r852_irq(int irq, void *data) ret = IRQ_HANDLED; if (dma_status & R852_DMA_IRQ_ERROR) { - dbg("recieved dma error IRQ"); + dbg("received dma error IRQ"); r852_dma_done(dev, -EIO); complete(&dev->dma_done); goto out; } - /* recieved DMA interrupt out of nowhere? */ + /* received DMA interrupt out of nowhere? */ WARN_ON_ONCE(dev->dma_stage == 0); if (dev->dma_stage == 0) @@ -845,7 +823,7 @@ out: return ret; } -int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { int error; struct nand_chip *chip; @@ -885,12 +863,12 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) chip->read_byte = r852_read_byte; chip->read_buf = r852_read_buf; chip->write_buf = r852_write_buf; - chip->verify_buf = r852_verify_buf; /* ecc */ chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->ecc.size = R852_DMA_LEN; chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.strength = 2; chip->ecc.hwctl = r852_ecc_hwctl; chip->ecc.calculate = r852_ecc_calculate; chip->ecc.correct = r852_ecc_correct; @@ -960,7 +938,7 @@ 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 succesfully\n"); + printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n"); return 0; error10: @@ -985,7 +963,7 @@ error1: return error; } -void r852_remove(struct pci_dev *pci_dev) +static void r852_remove(struct pci_dev *pci_dev) { struct r852_device *dev = pci_get_drvdata(pci_dev); @@ -1016,7 +994,7 @@ void r852_remove(struct pci_dev *pci_dev) pci_disable_device(pci_dev); } -void r852_shutdown(struct pci_dev *pci_dev) +static void r852_shutdown(struct pci_dev *pci_dev) { struct r852_device *dev = pci_get_drvdata(pci_dev); @@ -1026,8 +1004,8 @@ void r852_shutdown(struct pci_dev *pci_dev) pci_disable_device(pci_dev); } -#ifdef CONFIG_PM -int r852_suspend(struct device *device) +#ifdef CONFIG_PM_SLEEP +static int r852_suspend(struct device *device) { struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); @@ -1048,7 +1026,7 @@ int r852_suspend(struct device *device) return 0; } -int r852_resume(struct device *device) +static int r852_resume(struct device *device) { struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); @@ -1079,9 +1057,6 @@ int r852_resume(struct device *device) r852_update_card_detect(dev); return 0; } -#else -#define r852_suspend NULL -#define r852_resume NULL #endif static const struct pci_device_id r852_pci_id_tbl[] = { @@ -1092,7 +1067,7 @@ static const struct pci_device_id r852_pci_id_tbl[] = { MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); -SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); +static SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); static struct pci_driver r852_pci_driver = { .name = DRV_NAME, @@ -1103,18 +1078,7 @@ static struct pci_driver r852_pci_driver = { .driver.pm = &r852_pm_ops, }; -static __init int r852_module_init(void) -{ - return pci_register_driver(&r852_pci_driver); -} - -static void __exit r852_module_exit(void) -{ - pci_unregister_driver(&r852_pci_driver); -} - -module_init(r852_module_init); -module_exit(r852_module_exit); +module_pci_driver(r852_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c deleted file mode 100644 index 67440b5beef..00000000000 --- a/drivers/mtd/nand/rtc_from4.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * drivers/mtd/nand/rtc_from4.c - * - * Copyright (C) 2004 Red Hat, Inc. - * - * Derived from drivers/mtd/nand/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.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 AG-AND flash device found on the - * Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4), - * which utilizes the Renesas HN29V1G91T-30 part. - * This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device. - */ - -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/rslib.h> -#include <linux/bitrev.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> - -/* - * MTD structure for Renesas board - */ -static struct mtd_info *rtc_from4_mtd = NULL; - -#define RTC_FROM4_MAX_CHIPS 2 - -/* HS77x9 processor register defines */ -#define SH77X9_BCR1 ((volatile unsigned short *)(0xFFFFFF60)) -#define SH77X9_BCR2 ((volatile unsigned short *)(0xFFFFFF62)) -#define SH77X9_WCR1 ((volatile unsigned short *)(0xFFFFFF64)) -#define SH77X9_WCR2 ((volatile unsigned short *)(0xFFFFFF66)) -#define SH77X9_MCR ((volatile unsigned short *)(0xFFFFFF68)) -#define SH77X9_PCR ((volatile unsigned short *)(0xFFFFFF6C)) -#define SH77X9_FRQCR ((volatile unsigned short *)(0xFFFFFF80)) - -/* - * Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor) - */ -/* Address where flash is mapped */ -#define RTC_FROM4_FIO_BASE 0x14000000 - -/* CLE and ALE are tied to address lines 5 & 4, respectively */ -#define RTC_FROM4_CLE (1 << 5) -#define RTC_FROM4_ALE (1 << 4) - -/* address lines A24-A22 used for chip selection */ -#define RTC_FROM4_NAND_ADDR_SLOT3 (0x00800000) -#define RTC_FROM4_NAND_ADDR_SLOT4 (0x00C00000) -#define RTC_FROM4_NAND_ADDR_FPGA (0x01000000) -/* mask address lines A24-A22 used for chip selection */ -#define RTC_FROM4_NAND_ADDR_MASK (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA) - -/* FPGA status register for checking device ready (bit zero) */ -#define RTC_FROM4_FPGA_SR (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002) -#define RTC_FROM4_DEVICE_READY 0x0001 - -/* FPGA Reed-Solomon ECC Control register */ - -#define RTC_FROM4_RS_ECC_CTL (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050) -#define RTC_FROM4_RS_ECC_CTL_CLR (1 << 7) -#define RTC_FROM4_RS_ECC_CTL_GEN (1 << 6) -#define RTC_FROM4_RS_ECC_CTL_FD_E (1 << 5) - -/* FPGA Reed-Solomon ECC code base */ -#define RTC_FROM4_RS_ECC (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060) -#define RTC_FROM4_RS_ECCN (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080) - -/* FPGA Reed-Solomon ECC check register */ -#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070) -#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7) - -#define ERR_STAT_ECC_AVAILABLE 0x20 - -/* Undefine for software ECC */ -#define RTC_FROM4_HWECC 1 - -/* Define as 1 for no virtual erase blocks (in JFFS2) */ -#define RTC_FROM4_NO_VIRTBLOCKS 0 - -/* - * Module stuff - */ -static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE); - -static const struct mtd_partition partition_info[] = { - { - .name = "Renesas flash partition 1", - .offset = 0, - .size = MTDPART_SIZ_FULL}, -}; - -#define NUM_PARTITIONS 1 - -/* - * hardware specific flash bbt decriptors - * Note: this is to allow debugging by disabling - * NAND_BBT_CREATE and/or NAND_BBT_WRITE - * - */ -static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; -static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; - -static struct nand_bbt_descr rtc_from4_bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 40, - .len = 4, - .veroffs = 44, - .maxblocks = 4, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 40, - .len = 4, - .veroffs = 44, - .maxblocks = 4, - .pattern = mirror_pattern -}; - -#ifdef RTC_FROM4_HWECC - -/* the Reed Solomon control structure */ -static struct rs_control *rs_decoder; - -/* - * hardware specific Out Of Band information - */ -static struct nand_ecclayout rtc_from4_nand_oobinfo = { - .eccbytes = 32, - .eccpos = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31}, - .oobfree = {{32, 32}} -}; - -#endif - -/* - * rtc_from4_hwcontrol - hardware specific access to control-lines - * @mtd: MTD device structure - * @cmd: hardware control command - * - * Address lines (A5 and A4) are used to control Command and Address Latch - * Enable on this board, so set the read/write address appropriately. - * - * Chip Enable is also controlled by the Chip Select (CS5) and - * Address lines (A24-A22), so no action is required here. - * - */ -static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = (mtd->priv); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_CLE); - else - writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_ALE); -} - -/* - * rtc_from4_nand_select_chip - hardware specific chip select - * @mtd: MTD device structure - * @chip: Chip to select (0 == slot 3, 1 == slot 4) - * - * The chip select is based on address lines A24-A22. - * This driver uses flash slots 3 and 4 (A23-A22). - * - */ -static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *this = mtd->priv; - - this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK); - this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK); - - switch (chip) { - - case 0: /* select slot 3 chip */ - this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3); - this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3); - break; - case 1: /* select slot 4 chip */ - this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4); - this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4); - break; - - } -} - -/* - * rtc_from4_nand_device_ready - hardware specific ready/busy check - * @mtd: MTD device structure - * - * This board provides the Ready/Busy state in the status register - * of the FPGA. Bit zero indicates the RDY(1)/BSY(0) signal. - * - */ -static int rtc_from4_nand_device_ready(struct mtd_info *mtd) -{ - unsigned short status; - - status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR)); - - return (status & RTC_FROM4_DEVICE_READY); - -} - -/* - * deplete - code to perform device recovery in case there was a power loss - * @mtd: MTD device structure - * @chip: Chip to select (0 == slot 3, 1 == slot 4) - * - * If there was a sudden loss of power during an erase operation, a - * "device recovery" operation must be performed when power is restored - * to ensure correct operation. This routine performs the required steps - * for the requested chip. - * - * See page 86 of the data sheet for details. - * - */ -static void deplete(struct mtd_info *mtd, int chip) -{ - struct nand_chip *this = mtd->priv; - - /* wait until device is ready */ - while (!this->dev_ready(mtd)) ; - - this->select_chip(mtd, chip); - - /* Send the commands for device recovery, phase 1 */ - this->cmdfunc(mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000); - this->cmdfunc(mtd, NAND_CMD_DEPLETE2, -1, -1); - - /* Send the commands for device recovery, phase 2 */ - this->cmdfunc(mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004); - this->cmdfunc(mtd, NAND_CMD_DEPLETE2, -1, -1); - -} - -#ifdef RTC_FROM4_HWECC -/* - * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function - * @mtd: MTD device structure - * @mode: I/O mode; read or write - * - * enable hardware ECC for data read or write - * - */ -static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode) -{ - volatile unsigned short *rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL); - unsigned short status; - - switch (mode) { - case NAND_ECC_READ: - status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_FD_E; - - *rs_ecc_ctl = status; - break; - - case NAND_ECC_READSYN: - status = 0x00; - - *rs_ecc_ctl = status; - break; - - case NAND_ECC_WRITE: - status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_GEN | RTC_FROM4_RS_ECC_CTL_FD_E; - - *rs_ecc_ctl = status; - break; - - default: - BUG(); - break; - } - -} - -/* - * rtc_from4_calculate_ecc - hardware specific code to read ECC code - * @mtd: MTD device structure - * @dat: buffer containing the data to generate ECC codes - * @ecc_code ECC codes calculated - * - * The ECC code is calculated by the FPGA. All we have to do is read the values - * from the FPGA registers. - * - * Note: We read from the inverted registers, since data is inverted before - * the code is calculated. So all 0xff data (blank page) results in all 0xff rs code - * - */ -static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) -{ - volatile unsigned short *rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN); - unsigned short value; - int i; - - for (i = 0; i < 8; i++) { - value = *rs_eccn; - ecc_code[i] = (unsigned char)value; - rs_eccn++; - } - ecc_code[7] |= 0x0f; /* set the last four bits (not used) */ -} - -/* - * rtc_from4_correct_data - hardware specific code to correct data using ECC code - * @mtd: MTD device structure - * @buf: buffer containing the data to generate ECC codes - * @ecc1 ECC codes read - * @ecc2 ECC codes calculated - * - * The FPGA tells us fast, if there's an error or not. If no, we go back happy - * else we read the ecc results from the fpga and call the rs library to decode - * and hopefully correct the error. - * - */ -static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2) -{ - int i, j, res; - unsigned short status; - uint16_t par[6], syn[6]; - uint8_t ecc[8]; - volatile unsigned short *rs_ecc; - - status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK)); - - if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) { - return 0; - } - - /* Read the syndrom pattern from the FPGA and correct the bitorder */ - rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC); - for (i = 0; i < 8; i++) { - ecc[i] = bitrev8(*rs_ecc); - rs_ecc++; - } - - /* convert into 6 10bit syndrome fields */ - par[5] = rs_decoder->index_of[(((uint16_t) ecc[0] >> 0) & 0x0ff) | (((uint16_t) ecc[1] << 8) & 0x300)]; - par[4] = rs_decoder->index_of[(((uint16_t) ecc[1] >> 2) & 0x03f) | (((uint16_t) ecc[2] << 6) & 0x3c0)]; - par[3] = rs_decoder->index_of[(((uint16_t) ecc[2] >> 4) & 0x00f) | (((uint16_t) ecc[3] << 4) & 0x3f0)]; - par[2] = rs_decoder->index_of[(((uint16_t) ecc[3] >> 6) & 0x003) | (((uint16_t) ecc[4] << 2) & 0x3fc)]; - par[1] = rs_decoder->index_of[(((uint16_t) ecc[5] >> 0) & 0x0ff) | (((uint16_t) ecc[6] << 8) & 0x300)]; - par[0] = (((uint16_t) ecc[6] >> 2) & 0x03f) | (((uint16_t) ecc[7] << 6) & 0x3c0); - - /* Convert to computable syndrome */ - for (i = 0; i < 6; i++) { - syn[i] = par[0]; - for (j = 1; j < 6; j++) - if (par[j] != rs_decoder->nn) - syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)]; - - /* Convert to index form */ - syn[i] = rs_decoder->index_of[syn[i]]; - } - - /* Let the library code do its magic. */ - res = decode_rs8(rs_decoder, (uint8_t *) buf, par, 512, syn, 0, NULL, 0xff, NULL); - if (res > 0) { - DEBUG(MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " "ECC corrected %d errors on read\n", res); - } - return res; -} - -/** - * rtc_from4_errstat - perform additional error status checks - * @mtd: MTD device structure - * @this: NAND chip structure - * @state: state or the operation - * @status: status code returned from read status - * @page: startpage inside the chip, must be called with (page & this->pagemask) - * - * Perform additional error status checks on erase and write failures - * to determine if errors are correctable. For this device, correctable - * 1-bit errors on erase and write are considered acceptable. - * - * note: see pages 34..37 of data sheet for details. - * - */ -static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, - int state, int status, int page) -{ - int er_stat = 0; - int rtn, retlen; - size_t len; - uint8_t *buf; - int i; - - this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1); - - if (state == FL_ERASING) { - - for (i = 0; i < 4; i++) { - if (!(status & 1 << (i + 1))) - continue; - this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), - -1, -1); - rtn = this->read_byte(mtd); - this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); - - /* err_ecc_not_avail */ - if (!(rtn & ERR_STAT_ECC_AVAILABLE)) - er_stat |= 1 << (i + 1); - } - - } else if (state == FL_WRITING) { - - unsigned long corrected = mtd->ecc_stats.corrected; - - /* single bank write logic */ - this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1); - rtn = this->read_byte(mtd); - this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); - - if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { - /* err_ecc_not_avail */ - er_stat |= 1 << 1; - goto out; - } - - len = mtd->writesize; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n"); - er_stat = 1; - goto out; - } - - /* recovery read */ - rtn = nand_do_read(mtd, page, len, &retlen, buf); - - /* if read failed or > 1-bit error corrected */ - if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) - er_stat |= 1 << 1; - kfree(buf); - } -out: - rtn = status; - if (er_stat == 0) { /* if ECC is available */ - rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */ - } - - return rtn; -} -#endif - -/* - * Main initialization routine - */ -static int __init rtc_from4_init(void) -{ - struct nand_chip *this; - unsigned short bcr1, bcr2, wcr2; - int i; - int ret; - - /* Allocate memory for MTD device structure and private data */ - rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!rtc_from4_mtd) { - printk("Unable to allocate Renesas NAND MTD device structure.\n"); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&rtc_from4_mtd[1]); - - /* Initialize structures */ - memset(rtc_from4_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - rtc_from4_mtd->priv = this; - rtc_from4_mtd->owner = THIS_MODULE; - - /* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */ - bcr1 = *SH77X9_BCR1 & ~0x0002; - bcr1 |= 0x0002; - *SH77X9_BCR1 = bcr1; - - /* set */ - bcr2 = *SH77X9_BCR2 & ~0x0c00; - bcr2 |= 0x0800; - *SH77X9_BCR2 = bcr2; - - /* set area 5 wait states */ - wcr2 = *SH77X9_WCR2 & ~0x1c00; - wcr2 |= 0x1c00; - *SH77X9_WCR2 = wcr2; - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = rtc_from4_fio_base; - this->IO_ADDR_W = rtc_from4_fio_base; - /* Set address of hardware control function */ - this->cmd_ctrl = rtc_from4_hwcontrol; - /* Set address of chip select function */ - this->select_chip = rtc_from4_nand_select_chip; - /* command delay time (in us) */ - this->chip_delay = 100; - /* return the status of the Ready/Busy line */ - this->dev_ready = rtc_from4_nand_device_ready; - -#ifdef RTC_FROM4_HWECC - printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n"); - - this->ecc.mode = NAND_ECC_HW_SYNDROME; - this->ecc.size = 512; - this->ecc.bytes = 8; - /* return the status of extra status and ECC checks */ - this->errstat = rtc_from4_errstat; - /* set the nand_oobinfo to support FPGA H/W error detection */ - this->ecc.layout = &rtc_from4_nand_oobinfo; - this->ecc.hwctl = rtc_from4_enable_hwecc; - this->ecc.calculate = rtc_from4_calculate_ecc; - this->ecc.correct = rtc_from4_correct_data; - - /* We could create the decoder on demand, if memory is a concern. - * This way we have it handy, if an error happens - * - * Symbolsize is 10 (bits) - * Primitve polynomial is x^10+x^3+1 - * first consecutive root is 0 - * primitve element to generate roots = 1 - * generator polinomial degree = 6 - */ - rs_decoder = init_rs(10, 0x409, 0, 1, 6); - if (!rs_decoder) { - printk(KERN_ERR "Could not create a RS decoder\n"); - ret = -ENOMEM; - goto err_1; - } -#else - printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n"); - - this->ecc.mode = NAND_ECC_SOFT; -#endif - - /* set the bad block tables to support debugging */ - this->bbt_td = &rtc_from4_bbt_main_descr; - this->bbt_md = &rtc_from4_bbt_mirror_descr; - - /* Scan to find existence of the device */ - if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) { - ret = -ENXIO; - goto err_2; - } - - /* Perform 'device recovery' for each chip in case there was a power loss. */ - for (i = 0; i < this->numchips; i++) { - deplete(rtc_from4_mtd, i); - } - -#if RTC_FROM4_NO_VIRTBLOCKS - /* use a smaller erase block to minimize wasted space when a block is bad */ - /* note: this uses eight times as much RAM as using the default and makes */ - /* mounts take four times as long. */ - rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS; -#endif - - /* Register the partitions */ - ret = add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); - if (ret) - goto err_3; - - /* Return happy */ - return 0; -err_3: - nand_release(rtc_from4_mtd); -err_2: - free_rs(rs_decoder); -err_1: - kfree(rtc_from4_mtd); - return ret; -} - -module_init(rtc_from4_init); - -/* - * Clean up routine - */ -static void __exit rtc_from4_cleanup(void) -{ - /* Release resource, unregister partitions */ - nand_release(rtc_from4_mtd); - - /* Free the MTD device structure */ - kfree(rtc_from4_mtd); - -#ifdef RTC_FROM4_HWECC - /* Free the reed solomon resources */ - if (rs_decoder) { - free_rs(rs_decoder); - } -#endif -} - -module_exit(rtc_from4_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("d.marlin <dmarlin@redhat.com"); -MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4"); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 33d832dddfd..79acbb8691b 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -21,15 +21,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) "nand-s3c2410: " fmt + #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG #define DEBUG #endif #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> +#include <linux/io.h> #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/delay.h> @@ -43,23 +45,42 @@ #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> -#include <asm/io.h> - -#include <plat/regs-nand.h> -#include <plat/nand.h> - -#ifdef CONFIG_MTD_NAND_S3C2410_HWECC -static int hardware_ecc = 1; -#else -static int hardware_ecc = 0; -#endif - -#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP -static int clock_stop = 1; -#else -static const int clock_stop = 0; -#endif - +#include <linux/platform_data/mtd-nand-s3c2410.h> + +#define S3C2410_NFREG(x) (x) + +#define S3C2410_NFCONF S3C2410_NFREG(0x00) +#define S3C2410_NFCMD S3C2410_NFREG(0x04) +#define S3C2410_NFADDR S3C2410_NFREG(0x08) +#define S3C2410_NFDATA S3C2410_NFREG(0x0C) +#define S3C2410_NFSTAT S3C2410_NFREG(0x10) +#define S3C2410_NFECC S3C2410_NFREG(0x14) +#define S3C2440_NFCONT S3C2410_NFREG(0x04) +#define S3C2440_NFCMD S3C2410_NFREG(0x08) +#define S3C2440_NFADDR S3C2410_NFREG(0x0C) +#define S3C2440_NFDATA S3C2410_NFREG(0x10) +#define S3C2440_NFSTAT S3C2410_NFREG(0x20) +#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) +#define S3C2412_NFSTAT S3C2410_NFREG(0x28) +#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) +#define S3C2410_NFCONF_EN (1<<15) +#define S3C2410_NFCONF_INITECC (1<<12) +#define S3C2410_NFCONF_nFCE (1<<11) +#define S3C2410_NFCONF_TACLS(x) ((x)<<8) +#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) +#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) +#define S3C2410_NFSTAT_BUSY (1<<0) +#define S3C2440_NFCONF_TACLS(x) ((x)<<12) +#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) +#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) +#define S3C2440_NFCONT_INITECC (1<<4) +#define S3C2440_NFCONT_nFCE (1<<1) +#define S3C2440_NFCONT_ENABLE (1<<0) +#define S3C2440_NFSTAT_READY (1<<0) +#define S3C2412_NFCONF_NANDBOOT (1<<31) +#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) +#define S3C2412_NFCONT_nFCE0 (1<<1) +#define S3C2412_NFSTAT_READY (1<<0) /* new oob placement block for use with hardware ecc generation */ @@ -96,6 +117,12 @@ enum s3c_cpu_type { TYPE_S3C2440, }; +enum s3c_nand_clk_state { + CLOCK_DISABLE = 0, + CLOCK_ENABLE, + CLOCK_SUSPEND, +}; + /* overview of the s3c2410 nand state */ /** @@ -103,14 +130,14 @@ enum s3c_cpu_type { * @mtds: An array of MTD instances on this controoler. * @platform: The platform data for this board. * @device: The platform device we bound to. - * @area: The IO area resource that came from request_mem_region(). * @clk: The clock resource for this controller. - * @regs: The area mapped for the hardware registers described by @area. + * @regs: The area mapped for the hardware registers. * @sel_reg: Pointer to the register controlling the NAND selection. * @sel_bit: The bit in @sel_reg to select the NAND chip. * @mtd_count: The number of MTDs created from this controller. * @save_sel: The contents of @sel_reg to be saved over suspend. * @clk_rate: The clock rate from @clk. + * @clk_state: The current clock state. * @cpu_type: The exact type of this controller. */ struct s3c2410_nand_info { @@ -121,7 +148,6 @@ struct s3c2410_nand_info { /* device info */ struct device *device; - struct resource *area; struct clk *clk; void __iomem *regs; void __iomem *sel_reg; @@ -129,6 +155,7 @@ struct s3c2410_nand_info { int mtd_count; unsigned long save_sel; unsigned long clk_rate; + enum s3c_nand_clk_state clk_state; enum s3c_cpu_type cpu_type; @@ -156,12 +183,38 @@ static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) { - return dev->dev.platform_data; + return dev_get_platdata(&dev->dev); } -static inline int allow_clk_stop(struct s3c2410_nand_info *info) +static inline int allow_clk_suspend(struct s3c2410_nand_info *info) { - return clock_stop; +#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP + return 1; +#else + return 0; +#endif +} + +/** + * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. + * @info: The controller instance. + * @new_state: State to which clock should be set. + */ +static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, + enum s3c_nand_clk_state new_state) +{ + if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) + return; + + if (info->clk_state == CLOCK_ENABLE) { + if (new_state != CLOCK_ENABLE) + clk_disable(info->clk); + } else { + if (new_state == CLOCK_ENABLE) + clk_enable(info->clk); + } + + info->clk_state = new_state; } /* timing calculations */ @@ -185,7 +238,8 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) pr_debug("result %d from %ld, %d\n", result, clk, wanted); if (result > max) { - printk("%d ns is too big for current clock rate %ld\n", wanted, clk); + pr_err("%d ns is too big for current clock rate %ld\n", + wanted, clk); return -1; } @@ -195,7 +249,7 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) return result; } -#define to_ns(ticks,clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) +#define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) /* controller setup */ @@ -238,7 +292,8 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) } dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", - tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); + tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), + twrph1, to_ns(twrph1, clkrate)); switch (info->cpu_type) { case TYPE_S3C2410: @@ -295,13 +350,13 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) if (ret < 0) return ret; - switch (info->cpu_type) { - case TYPE_S3C2410: + switch (info->cpu_type) { + case TYPE_S3C2410: default: break; - case TYPE_S3C2440: - case TYPE_S3C2412: + case TYPE_S3C2440: + case TYPE_S3C2412: /* enable the controller and de-assert nFCE */ writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); @@ -333,8 +388,8 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) nmtd = this->priv; info = nmtd->info; - if (chip != -1 && allow_clk_stop(info)) - clk_enable(info->clk); + if (chip != -1) + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); cur = readl(info->sel_reg); @@ -356,8 +411,8 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) writel(cur, info->sel_reg); - if (chip == -1 && allow_clk_stop(info)) - clk_disable(info->clk); + if (chip == -1) + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); } /* s3c2410_nand_hwcontrol @@ -420,6 +475,7 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) /* ECC handling functions */ +#ifdef CONFIG_MTD_NAND_S3C2410_HWECC static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { @@ -433,10 +489,8 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, diff1 = read_ecc[1] ^ calc_ecc[1]; diff2 = read_ecc[2] ^ calc_ecc[2]; - pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", - __func__, - read_ecc[0], read_ecc[1], read_ecc[2], - calc_ecc[0], calc_ecc[1], calc_ecc[2], + pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n", + __func__, 3, read_ecc, 3, calc_ecc, diff0, diff1, diff2); if (diff0 == 0 && diff1 == 0 && diff2 == 0) @@ -516,7 +570,8 @@ static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) unsigned long ctrl; ctrl = readl(info->regs + S3C2440_NFCONT); - writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); + writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, + info->regs + S3C2440_NFCONT); } static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) @@ -528,7 +583,8 @@ static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); } -static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); @@ -536,13 +592,13 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); - pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, - ecc_code[0], ecc_code[1], ecc_code[2]); + pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); return 0; } -static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); @@ -551,12 +607,13 @@ static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = ecc >> 8; ecc_code[2] = ecc >> 16; - pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); + pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); return 0; } -static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); @@ -569,6 +626,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u return 0; } +#endif /* over-ride the standard functions for a little more speed. We can * use read/write block to move the data buffers to/from the controller @@ -595,13 +653,15 @@ static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) } } -static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, + int len) { struct nand_chip *this = mtd->priv; writesb(this->IO_ADDR_W, buf, len); } -static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, + int len) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); @@ -645,7 +705,8 @@ static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) CPUFREQ_TRANSITION_NOTIFIER); } -static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +static inline void +s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) { cpufreq_unregister_notifier(&info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); @@ -657,7 +718,8 @@ static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) return 0; } -static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +static inline void +s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) { } #endif @@ -668,8 +730,6 @@ static int s3c24xx_nand_remove(struct platform_device *pdev) { struct s3c2410_nand_info *info = to_nand_info(pdev); - platform_set_drvdata(pdev, NULL); - if (info == NULL) return 0; @@ -687,67 +747,29 @@ static int s3c24xx_nand_remove(struct platform_device *pdev) pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); nand_release(&ptr->mtd); } - - kfree(info->mtds); } /* free the common resources */ - if (info->clk != NULL && !IS_ERR(info->clk)) { - if (!allow_clk_stop(info)) - clk_disable(info->clk); - clk_put(info->clk); - } - - if (info->regs != NULL) { - iounmap(info->regs); - info->regs = NULL; - } - - if (info->area != NULL) { - release_resource(info->area); - kfree(info->area); - info->area = NULL; - } - - kfree(info); + if (!IS_ERR(info->clk)) + s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); return 0; } -#ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *mtd, struct s3c2410_nand_set *set) { - struct mtd_partition *part_info; - int nr_part = 0; - - if (set == NULL) - return add_mtd_device(&mtd->mtd); - - mtd->mtd.name = set->name; - nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, &part_info, 0); + if (set) { + mtd->mtd.name = set->name; - if (nr_part <= 0 && set->nr_partitions > 0) { - nr_part = set->nr_partitions; - part_info = set->partitions; + return mtd_device_parse_register(&mtd->mtd, NULL, NULL, + set->partitions, set->nr_partitions); } - if (nr_part > 0 && part_info) - return add_mtd_partitions(&mtd->mtd, part_info, nr_part); - - return add_mtd_device(&mtd->mtd); + return -ENODEV; } -#else -static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *mtd, - struct s3c2410_nand_set *set) -{ - return add_mtd_device(&mtd->mtd); -} -#endif /** * s3c2410_nand_init_chip - initialise a single instance of an chip @@ -804,7 +826,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, dev_info(info->device, "System booted from NAND\n"); break; - } + } chip->IO_ADDR_R = chip->IO_ADDR_W; @@ -813,31 +835,31 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, nmtd->mtd.owner = THIS_MODULE; nmtd->set = set; - if (hardware_ecc) { - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - chip->ecc.correct = s3c2410_nand_correct_data; - chip->ecc.mode = NAND_ECC_HW; - - switch (info->cpu_type) { - case TYPE_S3C2410: - chip->ecc.hwctl = s3c2410_nand_enable_hwecc; - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - break; +#ifdef CONFIG_MTD_NAND_S3C2410_HWECC + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + chip->ecc.correct = s3c2410_nand_correct_data; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.strength = 1; - case TYPE_S3C2412: - chip->ecc.hwctl = s3c2412_nand_enable_hwecc; - chip->ecc.calculate = s3c2412_nand_calculate_ecc; - break; + switch (info->cpu_type) { + case TYPE_S3C2410: + chip->ecc.hwctl = s3c2410_nand_enable_hwecc; + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + break; - case TYPE_S3C2440: - chip->ecc.hwctl = s3c2440_nand_enable_hwecc; - chip->ecc.calculate = s3c2440_nand_calculate_ecc; - break; + case TYPE_S3C2412: + chip->ecc.hwctl = s3c2412_nand_enable_hwecc; + chip->ecc.calculate = s3c2412_nand_calculate_ecc; + break; - } - } else { - chip->ecc.mode = NAND_ECC_SOFT; + case TYPE_S3C2440: + chip->ecc.hwctl = s3c2440_nand_enable_hwecc; + chip->ecc.calculate = s3c2440_nand_calculate_ecc; + break; } +#else + chip->ecc.mode = NAND_ECC_SOFT; +#endif if (set->ecc_layout != NULL) chip->ecc.layout = set->ecc_layout; @@ -863,8 +885,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, /* If you use u-boot BBT creation code, specifying this flag will * let the kernel fish out the BBT from the NAND, and also skip the * full NAND scan that can take 1/2s or so. Little things... */ - if (set->flash_bbt) - chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; + if (set->flash_bbt) { + chip->bbt_options |= NAND_BBT_USE_FLASH; + chip->options |= NAND_SKIP_BBTSCAN; + } } /** @@ -889,7 +913,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, if (chip->ecc.mode != NAND_ECC_HW) return; - /* change the behaviour depending on wether we are using + /* change the behaviour depending on whether we are using * the large or small page nand device */ if (chip->page_shift > 10) { @@ -912,7 +936,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, static int s3c24xx_nand_probe(struct platform_device *pdev) { struct s3c2410_platform_nand *plat = to_nand_plat(pdev); - enum s3c_cpu_type cpu_type; + enum s3c_cpu_type cpu_type; struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; @@ -926,9 +950,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) pr_debug("s3c2410_nand_probe(%p)\n", pdev); - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) { - dev_err(&pdev->dev, "no memory for flash info\n"); err = -ENOMEM; goto exit_error; } @@ -940,37 +963,28 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* get the clock source and enable it */ - info->clk = clk_get(&pdev->dev, "nand"); + info->clk = devm_clk_get(&pdev->dev, "nand"); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get clock\n"); err = -ENOENT; goto exit_error; } - clk_enable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); /* allocate and map the resource */ /* currently we assume we have the one resource */ - res = pdev->resource; + res = pdev->resource; size = resource_size(res); - info->area = request_mem_region(res->start, size, pdev->name); + info->device = &pdev->dev; + info->platform = plat; + info->cpu_type = cpu_type; - if (info->area == NULL) { - dev_err(&pdev->dev, "cannot reserve register region\n"); - err = -ENOENT; - goto exit_error; - } - - info->device = &pdev->dev; - info->platform = plat; - info->regs = ioremap(res->start, size); - info->cpu_type = cpu_type; - - if (info->regs == NULL) { - dev_err(&pdev->dev, "cannot reserve register region\n"); - err = -EIO; + info->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->regs)) { + err = PTR_ERR(info->regs); goto exit_error; } @@ -990,9 +1004,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* allocate our information */ size = nr_sets * sizeof(*info->mtds); - info->mtds = kzalloc(size, GFP_KERNEL); + info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); if (info->mtds == NULL) { - dev_err(&pdev->dev, "failed to allocate mtd storage\n"); err = -ENOMEM; goto exit_error; } @@ -1002,7 +1015,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) nmtd = info->mtds; for (setno = 0; setno < nr_sets; setno++, nmtd++) { - pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info); + pr_debug("initialising set %d (%p, info %p)\n", + setno, nmtd, info); s3c2410_nand_init_chip(info, nmtd, sets); @@ -1026,9 +1040,9 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) goto exit_error; } - if (allow_clk_stop(info)) { + if (allow_clk_suspend(info)) { dev_info(&pdev->dev, "clock idle support enabled\n"); - clk_disable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); } pr_debug("initialised ok\n"); @@ -1059,8 +1073,7 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) writel(info->save_sel | info->sel_bit, info->sel_reg); - if (!allow_clk_stop(info)) - clk_disable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); } return 0; @@ -1072,7 +1085,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev) unsigned long sel; if (info) { - clk_enable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); s3c2410_nand_inithw(info); /* Restore the state of the nFCE line. */ @@ -1082,8 +1095,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev) sel |= info->save_sel & info->sel_bit; writel(sel, info->sel_reg); - if (allow_clk_stop(info)) - clk_disable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); } return 0; @@ -1127,20 +1139,7 @@ static struct platform_driver s3c24xx_nand_driver = { }, }; -static int __init s3c2410_nand_init(void) -{ - printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); - - return platform_driver_register(&s3c24xx_nand_driver); -} - -static void __exit s3c2410_nand_exit(void) -{ - platform_driver_unregister(&s3c24xx_nand_driver); -} - -module_init(s3c2410_nand_init); -module_exit(s3c2410_nand_exit); +module_platform_driver(s3c24xx_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 546c2f0eb2e..c0670237e7a 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -23,10 +23,20 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/completion.h> #include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_mtd.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/sh_dma.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -42,11 +52,17 @@ static struct nand_ecclayout flctl_4secc_oob_16 = { }; static struct nand_ecclayout flctl_4secc_oob_64 = { - .eccbytes = 10, - .eccpos = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, + .eccbytes = 4 * 10, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, .oobfree = { - {.offset = 60, - . length = 4} }, + {.offset = 2, .length = 4}, + {.offset = 16, .length = 6}, + {.offset = 32, .length = 6}, + {.offset = 48, .length = 6} }, }; static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; @@ -60,15 +76,15 @@ static struct nand_bbt_descr flctl_4secc_smallpage = { static struct nand_bbt_descr flctl_4secc_largepage = { .options = NAND_BBT_SCAN2NDPAGE, - .offs = 58, + .offs = 0, .len = 2, .pattern = scan_ff_pattern, }; static void empty_fifo(struct sh_flctl *flctl) { - writel(0x000c0000, FLINTDMACR(flctl)); /* FIFO Clear */ - writel(0x00000000, FLINTDMACR(flctl)); /* Clear Error flags */ + writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl)); + writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); } static void start_translation(struct sh_flctl *flctl) @@ -78,7 +94,7 @@ static void start_translation(struct sh_flctl *flctl) static void timeout_error(struct sh_flctl *flctl, const char *str) { - dev_err(&flctl->pdev->dev, "Timeout occured in %s\n", str); + dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str); } static void wait_completion(struct sh_flctl *flctl) @@ -97,6 +113,84 @@ static void wait_completion(struct sh_flctl *flctl) writeb(0x0, FLTRCR(flctl)); } +static void flctl_dma_complete(void *param) +{ + struct sh_flctl *flctl = param; + + complete(&flctl->dma_complete); +} + +static void flctl_release_dma(struct sh_flctl *flctl) +{ + if (flctl->chan_fifo0_rx) { + dma_release_channel(flctl->chan_fifo0_rx); + flctl->chan_fifo0_rx = NULL; + } + if (flctl->chan_fifo0_tx) { + dma_release_channel(flctl->chan_fifo0_tx); + flctl->chan_fifo0_tx = NULL; + } +} + +static void flctl_setup_dma(struct sh_flctl *flctl) +{ + dma_cap_mask_t mask; + struct dma_slave_config cfg; + struct platform_device *pdev = flctl->pdev; + struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); + int ret; + + if (!pdata) + return; + + if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0) + return; + + /* We can only either use DMA for both Tx and Rx or not use it at all */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter, + (void *)(uintptr_t)pdata->slave_id_fifo0_tx); + dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__, + flctl->chan_fifo0_tx); + + if (!flctl->chan_fifo0_tx) + return; + + memset(&cfg, 0, sizeof(cfg)); + cfg.slave_id = pdata->slave_id_fifo0_tx; + cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl); + cfg.src_addr = 0; + ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg); + if (ret < 0) + goto err; + + flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter, + (void *)(uintptr_t)pdata->slave_id_fifo0_rx); + dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__, + flctl->chan_fifo0_rx); + + if (!flctl->chan_fifo0_rx) + goto err; + + cfg.slave_id = pdata->slave_id_fifo0_rx; + cfg.direction = DMA_DEV_TO_MEM; + cfg.dst_addr = 0; + cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl); + ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg); + if (ret < 0) + goto err; + + init_completion(&flctl->dma_complete); + + return; + +err: + flctl_release_dma(flctl); +} + static void set_addr(struct mtd_info *mtd, int column, int page_addr) { struct sh_flctl *flctl = mtd_to_flctl(mtd); @@ -157,27 +251,56 @@ static void wait_wfifo_ready(struct sh_flctl *flctl) timeout_error(flctl, __func__); } -static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) +static enum flctl_ecc_res_t wait_recfifo_ready + (struct sh_flctl *flctl, int sector_number) { uint32_t timeout = LOOP_TIMEOUT_MAX; - int checked[4]; void __iomem *ecc_reg[4]; int i; + int state = FL_SUCCESS; uint32_t data, size; - memset(checked, 0, sizeof(checked)); - + /* + * First this loops checks in FLDTCNTR if we are ready to read out the + * oob data. This is the case if either all went fine without errors or + * if the bottom part of the loop corrected the errors or marked them as + * uncorrectable and the controller is given time to push the data into + * the FIFO. + */ while (timeout--) { + /* check if all is ok and we can read out the OOB */ size = readl(FLDTCNTR(flctl)) >> 24; - if (size & 0xFF) - return 0; /* success */ + if ((size & 0xFF) == 4) + return state; + + /* check if a correction code has been calculated */ + if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) { + /* + * either we wait for the fifo to be filled or a + * correction pattern is being generated + */ + udelay(1); + continue; + } - if (readl(FL4ECCCR(flctl)) & _4ECCFA) - return 1; /* can't correct */ + /* check for an uncorrectable error */ + if (readl(FL4ECCCR(flctl)) & _4ECCFA) { + /* check if we face a non-empty page */ + for (i = 0; i < 512; i++) { + if (flctl->done_buff[i] != 0xff) { + state = FL_ERROR; /* can't correct */ + break; + } + } - udelay(1); - if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) + if (state == FL_SUCCESS) + dev_dbg(&flctl->pdev->dev, + "reading empty sector %d, ecc error ignored\n", + sector_number); + + writel(0, FL4ECCCR(flctl)); continue; + } /* start error correction */ ecc_reg[0] = FL4ECCRESULT0(flctl); @@ -186,28 +309,26 @@ static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) ecc_reg[3] = FL4ECCRESULT3(flctl); for (i = 0; i < 3; i++) { + uint8_t org; + unsigned int index; + data = readl(ecc_reg[i]); - if (data != INIT_FL4ECCRESULT_VAL && !checked[i]) { - uint8_t org; - int index; - - if (flctl->page_size) - index = (512 * sector_number) + - (data >> 16); - else - index = data >> 16; - - org = flctl->done_buff[index]; - flctl->done_buff[index] = org ^ (data & 0xFF); - checked[i] = 1; - } - } + if (flctl->page_size) + index = (512 * sector_number) + + (data >> 16); + else + index = data >> 16; + + org = flctl->done_buff[index]; + flctl->done_buff[index] = org ^ (data & 0xFF); + } + state = FL_REPAIRABLE; writel(0, FL4ECCCR(flctl)); } timeout_error(flctl, __func__); - return 1; /* timeout */ + return FL_TIMEOUT; /* timeout */ } static void wait_wecfifo_ready(struct sh_flctl *flctl) @@ -225,6 +346,70 @@ static void wait_wecfifo_ready(struct sh_flctl *flctl) timeout_error(flctl, __func__); } +static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, + int len, enum dma_data_direction dir) +{ + struct dma_async_tx_descriptor *desc = NULL; + struct dma_chan *chan; + enum dma_transfer_direction tr_dir; + dma_addr_t dma_addr; + dma_cookie_t cookie = -EINVAL; + uint32_t reg; + int ret; + + if (dir == DMA_FROM_DEVICE) { + chan = flctl->chan_fifo0_rx; + tr_dir = DMA_DEV_TO_MEM; + } else { + chan = flctl->chan_fifo0_tx; + tr_dir = DMA_MEM_TO_DEV; + } + + dma_addr = dma_map_single(chan->device->dev, buf, len, dir); + + if (dma_addr) + desc = dmaengine_prep_slave_single(chan, dma_addr, len, + tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (desc) { + reg = readl(FLINTDMACR(flctl)); + reg |= DREQ0EN; + writel(reg, FLINTDMACR(flctl)); + + desc->callback = flctl_dma_complete; + desc->callback_param = flctl; + cookie = dmaengine_submit(desc); + + dma_async_issue_pending(chan); + } else { + /* DMA failed, fall back to PIO */ + flctl_release_dma(flctl); + dev_warn(&flctl->pdev->dev, + "DMA failed, falling back to PIO\n"); + ret = -EIO; + goto out; + } + + ret = + wait_for_completion_timeout(&flctl->dma_complete, + msecs_to_jiffies(3000)); + + if (ret <= 0) { + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n"); + } + +out: + reg = readl(FLINTDMACR(flctl)); + reg &= ~DREQ0EN; + writel(reg, FLINTDMACR(flctl)); + + dma_unmap_single(chan->device->dev, dma_addr, len, dir); + + /* ret > 0 is success */ + return ret; +} + static void read_datareg(struct sh_flctl *flctl, int offset) { unsigned long data; @@ -240,50 +425,84 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) { int i, len_4align; unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; - void *fifo_addr = (void *)FLDTFIFO(flctl); len_4align = (rlen + 3) / 4; + /* initiate DMA transfer */ + if (flctl->chan_fifo0_rx && rlen >= 32 && + flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM) > 0) + goto convert; /* DMA success */ + + /* do polling transfer */ for (i = 0; i < len_4align; i++) { wait_rfifo_ready(flctl); - buf[i] = readl(fifo_addr); - buf[i] = be32_to_cpu(buf[i]); + buf[i] = readl(FLDTFIFO(flctl)); } + +convert: + for (i = 0; i < len_4align; i++) + buf[i] = be32_to_cpu(buf[i]); } -static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector) +static enum flctl_ecc_res_t read_ecfiforeg + (struct sh_flctl *flctl, uint8_t *buff, int sector) { int i; + enum flctl_ecc_res_t res; unsigned long *ecc_buf = (unsigned long *)buff; - void *fifo_addr = (void *)FLECFIFO(flctl); - for (i = 0; i < 4; i++) { - if (wait_recfifo_ready(flctl , sector)) - return 1; - ecc_buf[i] = readl(fifo_addr); - ecc_buf[i] = be32_to_cpu(ecc_buf[i]); + res = wait_recfifo_ready(flctl , sector); + + if (res != FL_ERROR) { + for (i = 0; i < 4; i++) { + ecc_buf[i] = readl(FLECFIFO(flctl)); + ecc_buf[i] = be32_to_cpu(ecc_buf[i]); + } } - return 0; + return res; } -static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) +static void write_fiforeg(struct sh_flctl *flctl, int rlen, + unsigned int offset) { int i, len_4align; - unsigned long *data = (unsigned long *)&flctl->done_buff[offset]; - void *fifo_addr = (void *)FLDTFIFO(flctl); + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; len_4align = (rlen + 3) / 4; for (i = 0; i < len_4align; i++) { wait_wfifo_ready(flctl); - writel(cpu_to_be32(data[i]), fifo_addr); + writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl)); + } +} + +static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, + unsigned int offset) +{ + int i, len_4align; + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; + + len_4align = (rlen + 3) / 4; + + for (i = 0; i < len_4align; i++) + buf[i] = cpu_to_be32(buf[i]); + + /* initiate DMA transfer */ + if (flctl->chan_fifo0_tx && rlen >= 32 && + flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV) > 0) + return; /* DMA success */ + + /* do polling transfer */ + for (i = 0; i < len_4align; i++) { + wait_wecfifo_ready(flctl); + writel(buf[i], FLECFIFO(flctl)); } } static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT; + uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; uint32_t flcmdcr_val, addr_len_bytes = 0; /* Set SNAND bit if page size is 2048byte */ @@ -303,6 +522,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va break; case NAND_CMD_READ0: case NAND_CMD_READOOB: + case NAND_CMD_RNDOUT: addr_len_bytes = flctl->rw_ADRCNT; flcmdcr_val |= CDSRC_E; if (flctl->chip.options & NAND_BUSWIDTH_16) @@ -320,6 +540,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va break; case NAND_CMD_READID: flcmncr_val &= ~SNAND_E; + flcmdcr_val |= CDSRC_E; addr_len_bytes = ADRCNT_1; break; case NAND_CMD_STATUS: @@ -341,75 +562,67 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va } static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - struct sh_flctl *flctl = mtd_to_flctl(mtd); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->read_buf(mtd, p, eccsize); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - if (flctl->hwecc_cant_correct[i]) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += 0; - } - + chip->read_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } -static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) +static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - const uint8_t *p = buf; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->write_buf(mtd, p, eccsize); + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; } static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) { struct sh_flctl *flctl = mtd_to_flctl(mtd); int sector, page_sectors; + enum flctl_ecc_res_t ecc_result; - if (flctl->page_size) - page_sectors = 4; - else - page_sectors = 1; - - writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, - FLCMNCR(flctl)); + page_sectors = flctl->page_size ? 4 : 1; set_cmd_regs(mtd, NAND_CMD_READ0, (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); - for (sector = 0; sector < page_sectors; sector++) { - int ret; + writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, + FLCMNCR(flctl)); + writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); + writel(page_addr << 2, FLADR(flctl)); - empty_fifo(flctl); - writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl)); - writel(page_addr << 2 | sector, FLADR(flctl)); + empty_fifo(flctl); + start_translation(flctl); - start_translation(flctl); + for (sector = 0; sector < page_sectors; sector++) { read_fiforeg(flctl, 512, 512 * sector); - ret = read_ecfiforeg(flctl, + ecc_result = read_ecfiforeg(flctl, &flctl->done_buff[mtd->writesize + 16 * sector], sector); - if (ret) - flctl->hwecc_cant_correct[sector] = 1; - - writel(0x0, FL4ECCCR(flctl)); - wait_completion(flctl); + switch (ecc_result) { + case FL_REPAIRABLE: + dev_info(&flctl->pdev->dev, + "applied ecc on page 0x%x", page_addr); + flctl->mtd.ecc_stats.corrected++; + break; + case FL_ERROR: + dev_warn(&flctl->pdev->dev, + "page 0x%x contains corrupted data\n", + page_addr); + flctl->mtd.ecc_stats.failed++; + break; + default: + ; + } } + + wait_completion(flctl); + writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), FLCMNCR(flctl)); } @@ -417,30 +630,20 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) static void execmd_read_oob(struct mtd_info *mtd, int page_addr) { struct sh_flctl *flctl = mtd_to_flctl(mtd); + int page_sectors = flctl->page_size ? 4 : 1; + int i; set_cmd_regs(mtd, NAND_CMD_READ0, (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); empty_fifo(flctl); - if (flctl->page_size) { - int i; - /* In case that the page size is 2k */ - for (i = 0; i < 16 * 3; i++) - flctl->done_buff[i] = 0xFF; - set_addr(mtd, 3 * 528 + 512, page_addr); + for (i = 0; i < page_sectors; i++) { + set_addr(mtd, (512 + 16) * i + 512 , page_addr); writel(16, FLDTCNTR(flctl)); start_translation(flctl); - read_fiforeg(flctl, 16, 16 * 3); - wait_completion(flctl); - } else { - /* In case that the page size is 512b */ - set_addr(mtd, 512, page_addr); - writel(16, FLDTCNTR(flctl)); - - start_translation(flctl); - read_fiforeg(flctl, 16, 0); + read_fiforeg(flctl, 16, 16 * i); wait_completion(flctl); } } @@ -448,34 +651,26 @@ static void execmd_read_oob(struct mtd_info *mtd, int page_addr) static void execmd_write_page_sector(struct mtd_info *mtd) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - int i, page_addr = flctl->seqin_page_addr; + int page_addr = flctl->seqin_page_addr; int sector, page_sectors; - if (flctl->page_size) - page_sectors = 4; - else - page_sectors = 1; - - writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); + page_sectors = flctl->page_size ? 4 : 1; set_cmd_regs(mtd, NAND_CMD_PAGEPROG, (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); - for (sector = 0; sector < page_sectors; sector++) { - empty_fifo(flctl); - writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl)); - writel(page_addr << 2 | sector, FLADR(flctl)); + empty_fifo(flctl); + writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); + writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); + writel(page_addr << 2, FLADR(flctl)); + start_translation(flctl); - start_translation(flctl); + for (sector = 0; sector < page_sectors; sector++) { write_fiforeg(flctl, 512, 512 * sector); - - for (i = 0; i < 4; i++) { - wait_wecfifo_ready(flctl); /* wait for write ready */ - writel(0xFFFFFFFF, FLECFIFO(flctl)); - } - wait_completion(flctl); + write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector); } + wait_completion(flctl); writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); } @@ -485,18 +680,12 @@ static void execmd_write_oob(struct mtd_info *mtd) int page_addr = flctl->seqin_page_addr; int sector, page_sectors; - if (flctl->page_size) { - sector = 3; - page_sectors = 4; - } else { - sector = 0; - page_sectors = 1; - } + page_sectors = flctl->page_size ? 4 : 1; set_cmd_regs(mtd, NAND_CMD_PAGEPROG, (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); - for (; sector < page_sectors; sector++) { + for (sector = 0; sector < page_sectors; sector++) { empty_fifo(flctl); set_addr(mtd, sector * 528 + 512, page_addr); writel(16, FLDTCNTR(flctl)); /* set read size */ @@ -513,6 +702,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, struct sh_flctl *flctl = mtd_to_flctl(mtd); uint32_t read_cmd = 0; + pm_runtime_get_sync(&flctl->pdev->dev); + flctl->read_bytes = 0; if (command != NAND_CMD_PAGEPROG) flctl->index = 0; @@ -525,7 +716,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, execmd_read_page_sector(mtd, page_addr); break; } - empty_fifo(flctl); if (flctl->page_size) set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | command); @@ -547,7 +737,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, break; } - empty_fifo(flctl); if (flctl->page_size) { set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); @@ -559,15 +748,35 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, flctl->read_bytes = mtd->oobsize; goto read_normal_exit; + case NAND_CMD_RNDOUT: + if (flctl->hwecc) + break; + + if (flctl->page_size) + set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) + | command); + else + set_cmd_regs(mtd, command, command); + + set_addr(mtd, column, 0); + + flctl->read_bytes = mtd->writesize + mtd->oobsize - column; + goto read_normal_exit; + case NAND_CMD_READID: - empty_fifo(flctl); set_cmd_regs(mtd, command, command); - set_addr(mtd, 0, 0); - flctl->read_bytes = 4; + /* READID is always performed using an 8-bit bus */ + if (flctl->chip.options & NAND_BUSWIDTH_16) + column <<= 1; + set_addr(mtd, column, 0); + + flctl->read_bytes = 8; writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); start_translation(flctl); - read_datareg(flctl, 0); /* read and end */ + read_fiforeg(flctl, flctl->read_bytes, 0); + wait_completion(flctl); break; case NAND_CMD_ERASE1: @@ -650,29 +859,57 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, default: break; } - return; + goto runtime_exit; read_normal_exit: writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); start_translation(flctl); read_fiforeg(flctl, flctl->read_bytes, 0); wait_completion(flctl); +runtime_exit: + pm_runtime_put_sync(&flctl->pdev->dev); return; } static void flctl_select_chip(struct mtd_info *mtd, int chipnr) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = readl(FLCMNCR(flctl)); + int ret; switch (chipnr) { case -1: - flcmncr_val &= ~CE0_ENABLE; - writel(flcmncr_val, FLCMNCR(flctl)); + flctl->flcmncr_base &= ~CE0_ENABLE; + + pm_runtime_get_sync(&flctl->pdev->dev); + writel(flctl->flcmncr_base, FLCMNCR(flctl)); + + if (flctl->qos_request) { + dev_pm_qos_remove_request(&flctl->pm_qos); + flctl->qos_request = 0; + } + + pm_runtime_put_sync(&flctl->pdev->dev); break; case 0: - flcmncr_val |= CE0_ENABLE; - writel(flcmncr_val, FLCMNCR(flctl)); + flctl->flcmncr_base |= CE0_ENABLE; + + if (!flctl->qos_request) { + ret = dev_pm_qos_add_request(&flctl->pdev->dev, + &flctl->pm_qos, + DEV_PM_QOS_RESUME_LATENCY, + 100); + if (ret < 0) + dev_err(&flctl->pdev->dev, + "PM QoS request failed: %d\n", ret); + flctl->qos_request = 1; + } + + if (flctl->holden) { + pm_runtime_get_sync(&flctl->pdev->dev); + writel(HOLDEN, FLHOLDCR(flctl)); + pm_runtime_put_sync(&flctl->pdev->dev); + } break; default: BUG(); @@ -682,57 +919,36 @@ static void flctl_select_chip(struct mtd_info *mtd, int chipnr) static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - int i, index = flctl->index; - for (i = 0; i < len; i++) - flctl->done_buff[index + i] = buf[i]; + memcpy(&flctl->done_buff[flctl->index], buf, len); flctl->index += len; } static uint8_t flctl_read_byte(struct mtd_info *mtd) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - int index = flctl->index; uint8_t data; - data = flctl->done_buff[index]; + data = flctl->done_buff[flctl->index]; flctl->index++; return data; } static uint16_t flctl_read_word(struct mtd_info *mtd) { - struct sh_flctl *flctl = mtd_to_flctl(mtd); - int index = flctl->index; - uint16_t data; - uint16_t *buf = (uint16_t *)&flctl->done_buff[index]; + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint16_t *buf = (uint16_t *)&flctl->done_buff[flctl->index]; - data = *buf; - flctl->index += 2; - return data; + flctl->index += 2; + return *buf; } static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - int i; - - for (i = 0; i < len; i++) - buf[i] = flctl_read_byte(mtd); -} - -static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - if (buf[i] != flctl_read_byte(mtd)) - return -EFAULT; - return 0; -} + struct sh_flctl *flctl = mtd_to_flctl(mtd); -static void flctl_register_init(struct sh_flctl *flctl, unsigned long val) -{ - writel(val, FLCMNCR(flctl)); + memcpy(buf, &flctl->done_buff[flctl->index], len); + flctl->index += len; } static int flctl_chip_init_tail(struct mtd_info *mtd) @@ -781,13 +997,13 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) chip->ecc.size = 512; chip->ecc.bytes = 10; + chip->ecc.strength = 4; chip->ecc.read_page = flctl_read_page_hwecc; chip->ecc.write_page = flctl_write_page_hwecc; chip->ecc.mode = NAND_ECC_HW; /* 4 symbols ECC enabled */ - writel(readl(FLCMNCR(flctl)) | _4ECCEN | ECCPOS2 | ECCPOS_02, - FLCMNCR(flctl)); + flctl->flcmncr_base |= _4ECCEN; } else { chip->ecc.mode = NAND_ECC_SOFT; } @@ -795,37 +1011,114 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) return 0; } -static int __devinit flctl_probe(struct platform_device *pdev) +static irqreturn_t flctl_handle_flste(int irq, void *dev_id) +{ + struct sh_flctl *flctl = dev_id; + + dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl))); + writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); + + return IRQ_HANDLED; +} + +struct flctl_soc_config { + unsigned long flcmncr_val; + unsigned has_hwecc:1; + unsigned use_holden:1; +}; + +static struct flctl_soc_config flctl_sh7372_config = { + .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL, + .has_hwecc = 1, + .use_holden = 1, +}; + +static const struct of_device_id of_flctl_match[] = { + { .compatible = "renesas,shmobile-flctl-sh7372", + .data = &flctl_sh7372_config }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_flctl_match); + +static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) +{ + const struct of_device_id *match; + struct flctl_soc_config *config; + struct sh_flctl_platform_data *pdata; + struct device_node *dn = dev->of_node; + int ret; + + match = of_match_device(of_flctl_match, dev); + if (match) + config = (struct flctl_soc_config *)match->data; + else { + dev_err(dev, "%s: no OF configuration attached\n", __func__); + return NULL; + } + + pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data), + GFP_KERNEL); + if (!pdata) + return NULL; + + /* set SoC specific options */ + pdata->flcmncr_val = config->flcmncr_val; + pdata->has_hwecc = config->has_hwecc; + pdata->use_holden = config->use_holden; + + /* parse user defined options */ + ret = of_get_nand_bus_width(dn); + if (ret == 16) + pdata->flcmncr_val |= SEL_16BIT; + else if (ret != 8) { + dev_err(dev, "%s: invalid bus width\n", __func__); + return NULL; + } + + return pdata; +} + +static int flctl_probe(struct platform_device *pdev) { struct resource *res; struct sh_flctl *flctl; struct mtd_info *flctl_mtd; struct nand_chip *nand; struct sh_flctl_platform_data *pdata; - int ret = -ENXIO; + int ret; + int irq; + struct mtd_part_parser_data ppdata = {}; - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - - flctl = kzalloc(sizeof(struct sh_flctl), GFP_KERNEL); - if (!flctl) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL); + if (!flctl) return -ENOMEM; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - goto err; + flctl->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(flctl->reg)) + return PTR_ERR(flctl->reg); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get flste irq data\n"); + return -ENXIO; } - flctl->reg = ioremap(res->start, resource_size(res)); - if (flctl->reg == NULL) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - goto err; + ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, + "flste", flctl); + if (ret) { + dev_err(&pdev->dev, "request interrupt failed.\n"); + return ret; + } + + if (pdev->dev.of_node) + pdata = flctl_parse_dt(&pdev->dev); + else + pdata = dev_get_platdata(&pdev->dev); + + if (!pdata) { + dev_err(&pdev->dev, "no setup data defined\n"); + return -EINVAL; } platform_set_drvdata(pdev, flctl); @@ -834,10 +1127,9 @@ static int __devinit flctl_probe(struct platform_device *pdev) flctl_mtd->priv = nand; flctl->pdev = pdev; flctl->hwecc = pdata->has_hwecc; - - flctl_register_init(flctl, pdata->flcmncr_val); - - nand->options = NAND_NO_AUTOINCR; + flctl->holden = pdata->use_holden; + flctl->flcmncr_base = pdata->flcmncr_val; + flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE; /* Set address of hardware control function */ /* 20 us command delay time */ @@ -846,7 +1138,6 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand->read_byte = flctl_read_byte; nand->write_buf = flctl_write_buf; nand->read_buf = flctl_read_buf; - nand->verify_buf = flctl_verify_buf; nand->select_chip = flctl_select_chip; nand->cmdfunc = flctl_cmdfunc; @@ -855,33 +1146,42 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand->read_word = flctl_read_word; } + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + flctl_setup_dma(flctl); + ret = nand_scan_ident(flctl_mtd, 1, NULL); if (ret) - goto err; + goto err_chip; ret = flctl_chip_init_tail(flctl_mtd); if (ret) - goto err; + goto err_chip; ret = nand_scan_tail(flctl_mtd); if (ret) - goto err; + goto err_chip; - add_mtd_partitions(flctl_mtd, pdata->parts, pdata->nr_parts); + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(flctl_mtd, NULL, &ppdata, pdata->parts, + pdata->nr_parts); return 0; -err: - kfree(flctl); +err_chip: + flctl_release_dma(flctl); + pm_runtime_disable(&pdev->dev); return ret; } -static int __devexit flctl_remove(struct platform_device *pdev) +static int flctl_remove(struct platform_device *pdev) { struct sh_flctl *flctl = platform_get_drvdata(pdev); + flctl_release_dma(flctl); nand_release(&flctl->mtd); - kfree(flctl); + pm_runtime_disable(&pdev->dev); return 0; } @@ -891,21 +1191,11 @@ static struct platform_driver flctl_driver = { .driver = { .name = "sh_flctl", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_flctl_match), }, }; -static int __init flctl_nand_init(void) -{ - return platform_driver_probe(&flctl_driver, flctl_probe); -} - -static void __exit flctl_nand_cleanup(void) -{ - platform_driver_unregister(&flctl_driver); -} - -module_init(flctl_nand_init); -module_exit(flctl_nand_cleanup); +module_platform_driver_probe(flctl_driver, flctl_probe); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yoshihiro Shimoda"); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 54ec7542a7b..e81059b5838 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -103,24 +103,16 @@ static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, return readb(sharpsl->io + ECCCNTR) != 0; } -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - /* * Main initialization routine */ -static int __devinit sharpsl_nand_probe(struct platform_device *pdev) +static int sharpsl_nand_probe(struct platform_device *pdev) { struct nand_chip *this; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *sharpsl_partition_info; - int nr_partitions; -#endif struct resource *r; int err = 0; struct sharpsl_nand *sharpsl; - struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; + struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); if (!data) { dev_err(&pdev->dev, "no platform data!\n"); @@ -129,10 +121,8 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) /* Allocate memory for MTD device structure and private data */ sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); - if (!sharpsl) { - printk("Unable to allocate SharpSL NAND MTD device structure.\n"); + if (!sharpsl) return -ENOMEM; - } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { @@ -144,7 +134,7 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) /* map physical address */ sharpsl->io = ioremap(r->start, resource_size(r)); if (!sharpsl->io) { - printk("ioremap to access Sharp SL NAND chip failed\n"); + dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n"); err = -EIO; goto err_ioremap; } @@ -175,6 +165,7 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) this->ecc.mode = NAND_ECC_HW; this->ecc.size = 256; this->ecc.bytes = 3; + this->ecc.strength = 1; this->badblock_pattern = data->badblock_pattern; this->ecc.layout = data->ecc_layout; this->ecc.hwctl = sharpsl_nand_enable_hwecc; @@ -188,18 +179,9 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) /* Register the partitions */ sharpsl->mtd.name = "sharpsl-nand"; -#ifdef CONFIG_MTD_PARTITIONS - nr_partitions = parse_mtd_partitions(&sharpsl->mtd, part_probes, &sharpsl_partition_info, 0); - if (nr_partitions <= 0) { - nr_partitions = data->nr_partitions; - sharpsl_partition_info = data->partitions; - } - if (nr_partitions > 0) - err = add_mtd_partitions(&sharpsl->mtd, sharpsl_partition_info, nr_partitions); - else -#endif - err = add_mtd_device(&sharpsl->mtd); + err = mtd_device_parse_register(&sharpsl->mtd, NULL, NULL, + data->partitions, data->nr_partitions); if (err) goto err_add; @@ -210,7 +192,6 @@ err_add: nand_release(&sharpsl->mtd); err_scan: - platform_set_drvdata(pdev, NULL); iounmap(sharpsl->io); err_ioremap: err_get_res: @@ -221,15 +202,13 @@ err_get_res: /* * Clean up routine */ -static int __devexit sharpsl_nand_remove(struct platform_device *pdev) +static int sharpsl_nand_remove(struct platform_device *pdev) { struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); /* Release resources, unregister device */ nand_release(&sharpsl->mtd); - platform_set_drvdata(pdev, NULL); - iounmap(sharpsl->io); /* Free the MTD device structure */ @@ -244,20 +223,10 @@ static struct platform_driver sharpsl_nand_driver = { .owner = THIS_MODULE, }, .probe = sharpsl_nand_probe, - .remove = __devexit_p(sharpsl_nand_remove), + .remove = sharpsl_nand_remove, }; -static int __init sharpsl_nand_init(void) -{ - return platform_driver_register(&sharpsl_nand_driver); -} -module_init(sharpsl_nand_init); - -static void __exit sharpsl_nand_exit(void) -{ - platform_driver_unregister(&sharpsl_nand_driver); -} -module_exit(sharpsl_nand_exit); +module_platform_driver(sharpsl_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index 4a8f367c295..e06b5e5d328 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -8,6 +8,8 @@ */ #include <linux/kernel.h> #include <linux/mtd/nand.h> +#include <linux/module.h> +#include <linux/sizes.h> #include "sm_common.h" static struct nand_ecclayout nand_oob_sm = { @@ -40,71 +42,62 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_oob_ops ops; struct sm_oob oob; - int ret, error = 0; + int ret; memset(&oob, -1, SM_OOB_SIZE); oob.block_status = 0x0F; /* As long as this function is called on erase block boundaries it will work correctly for 256 byte nand */ - ops.mode = MTD_OOB_PLACE; + ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; ops.oobbuf = (void *)&oob; ops.datbuf = NULL; - ret = mtd->write_oob(mtd, ofs, &ops); + 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); - error = -EIO; - } else - mtd->ecc_stats.badblocks++; + return -EIO; + } - return error; + return 0; } - static struct nand_flash_dev nand_smartmedia_flash_ids[] = { - {"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0}, - {"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0}, - {"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0}, - {"SmartMedia 2MiB 3,3V", 0xea, 256, 2, 0x1000, 0}, - {"SmartMedia 2MiB 5V", 0x64, 256, 2, 0x1000, 0}, - {"SmartMedia 2MiB 3,3V ROM", 0x5d, 512, 2, 0x2000, NAND_ROM}, - {"SmartMedia 4MiB 3,3V", 0xe3, 512, 4, 0x2000, 0}, - {"SmartMedia 4MiB 3,3/5V", 0xe5, 512, 4, 0x2000, 0}, - {"SmartMedia 4MiB 5V", 0x6b, 512, 4, 0x2000, 0}, - {"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND_ROM}, - {"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0}, - {"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND_ROM}, - {"SmartMedia 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, - {"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND_ROM}, - {"SmartMedia 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, - {"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND_ROM}, - {"SmartMedia 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, - {"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND_ROM}, - {"SmartMedia 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, - {"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND_ROM}, - {"SmartMedia 256MiB 3,3V", 0x71, 512, 256, 0x4000 }, - {"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND_ROM}, - {NULL,} + LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM), + {NULL} }; -#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) static struct nand_flash_dev nand_xd_flash_ids[] = { - - {"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, - {"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, - {"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, - {"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, - {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, - {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM}, - {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM}, - {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM}, - {NULL,} + LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0), + LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0), + LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0), + LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0), + LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD), + LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD), + LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD), + LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD), + {NULL} }; int sm_register_device(struct mtd_info *mtd, int smartmedia) @@ -121,7 +114,7 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) if (ret) return ret; - /* Bad block marker postion */ + /* Bad block marker position */ chip->badblockpos = 0x05; chip->badblockbits = 7; chip->block_markbad = sm_block_markbad; @@ -139,7 +132,7 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) if (ret) return ret; - return add_mtd_device(mtd); + return mtd_device_register(mtd, NULL, 0); } EXPORT_SYMBOL_GPL(sm_register_device); diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index a8e403eebed..fe8058a4505 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -15,6 +15,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/io.h> @@ -98,24 +99,6 @@ static uint16_t socrates_nand_read_word(struct mtd_info *mtd) return word; } -/** - * socrates_nand_verify_buf - Verify chip data against buffer - * @mtd: MTD device structure - * @buf: buffer containing the data to compare - * @len: number of bytes to compare - */ -static int socrates_nand_verify_buf(struct mtd_info *mtd, const u8 *buf, - int len) -{ - int i; - - for (i = 0; i < len; i++) { - if (buf[i] != socrates_nand_read_byte(mtd)) - return -EFAULT; - } - return 0; -} - /* * Hardware specific access to control-lines */ @@ -155,38 +138,25 @@ static int socrates_nand_device_ready(struct mtd_info *mtd) return 1; } -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - /* * Probe for the NAND device. */ -static int __devinit socrates_nand_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) +static int socrates_nand_probe(struct platform_device *ofdev) { struct socrates_nand_host *host; struct mtd_info *mtd; struct nand_chip *nand_chip; int res; - -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *partitions = NULL; - int num_partitions = 0; -#endif + struct mtd_part_parser_data ppdata; /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL); - if (!host) { - printk(KERN_ERR - "socrates_nand: failed to allocate device structure.\n"); + host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) return -ENOMEM; - } host->io_base = of_iomap(ofdev->dev.of_node, 0); if (host->io_base == NULL) { - printk(KERN_ERR "socrates_nand: ioremap failed\n"); - kfree(host); + dev_err(&ofdev->dev, "ioremap failed\n"); return -EIO; } @@ -199,6 +169,7 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev, mtd->name = "socrates_nand"; mtd->owner = THIS_MODULE; mtd->dev.parent = &ofdev->dev; + ppdata.of_node = ofdev->dev.of_node; /*should never be accessed directly */ nand_chip->IO_ADDR_R = (void *)0xdeadbeef; @@ -209,7 +180,6 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev, nand_chip->read_word = socrates_nand_read_word; nand_chip->write_buf = socrates_nand_write_buf; nand_chip->read_buf = socrates_nand_read_buf; - nand_chip->verify_buf = socrates_nand_verify_buf; nand_chip->dev_ready = socrates_nand_device_ready; nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ @@ -231,61 +201,28 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev, goto out; } -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS - num_partitions = parse_mtd_partitions(mtd, part_probes, - &partitions, 0); - if (num_partitions < 0) { - res = num_partitions; - goto release; - } -#endif - -#ifdef CONFIG_MTD_OF_PARTS - if (num_partitions == 0) { - num_partitions = of_mtd_parse_partitions(&ofdev->dev, - ofdev->dev.of_node, - &partitions); - if (num_partitions < 0) { - res = num_partitions; - goto release; - } - } -#endif - if (partitions && (num_partitions > 0)) - res = add_mtd_partitions(mtd, partitions, num_partitions); - else -#endif - res = add_mtd_device(mtd); - + res = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (!res) return res; -#ifdef CONFIG_MTD_PARTITIONS -release: -#endif nand_release(mtd); out: - dev_set_drvdata(&ofdev->dev, NULL); iounmap(host->io_base); - kfree(host); return res; } /* * Remove a NAND device. */ -static int __devexit socrates_nand_remove(struct platform_device *ofdev) +static int socrates_nand_remove(struct platform_device *ofdev) { struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); struct mtd_info *mtd = &host->mtd; nand_release(mtd); - dev_set_drvdata(&ofdev->dev, NULL); iounmap(host->io_base); - kfree(host); return 0; } @@ -300,28 +237,17 @@ static const struct of_device_id socrates_nand_match[] = MODULE_DEVICE_TABLE(of, socrates_nand_match); -static struct of_platform_driver socrates_nand_driver = { +static struct platform_driver socrates_nand_driver = { .driver = { .name = "socrates_nand", .owner = THIS_MODULE, .of_match_table = socrates_nand_match, }, .probe = socrates_nand_probe, - .remove = __devexit_p(socrates_nand_remove), + .remove = socrates_nand_remove, }; -static int __init socrates_nand_init(void) -{ - return of_register_platform_driver(&socrates_nand_driver); -} - -static void __exit socrates_nand_exit(void) -{ - of_unregister_platform_driver(&socrates_nand_driver); -} - -module_init(socrates_nand_init); -module_exit(socrates_nand_exit); +module_platform_driver(socrates_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ilya Yanok"); diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c deleted file mode 100644 index 0cc6d0acb8f..00000000000 --- a/drivers/mtd/nand/spia.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * drivers/mtd/nand/spia.c - * - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * - * - * 10-29-2001 TG change to support hardwarespecific access - * to controllines (due to change in nand.c) - * page_cache added - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * SPIA board which utilizes the Toshiba TC58V64AFT part. This is - * a 64Mibit (8MiB x 8 bits) NAND flash device. - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> - -/* - * MTD structure for SPIA board - */ -static struct mtd_info *spia_mtd = NULL; - -/* - * Values specific to the SPIA board (used with EP7212 processor) - */ -#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */ -#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */ -#define SPIA_PEDR 0x0080 /* - * IO offset to Port E data register - * where the CLE, ALE and NCE pins - * are wired to. - */ -#define SPIA_PEDDR 0x00c0 /* - * IO offset to Port E data direction - * register so we can control the IO - * lines. - */ - -/* - * Module stuff - */ - -static int spia_io_base = SPIA_IO_BASE; -static int spia_fio_base = SPIA_FIO_BASE; -static int spia_pedr = SPIA_PEDR; -static int spia_peddr = SPIA_PEDDR; - -module_param(spia_io_base, int, 0); -module_param(spia_fio_base, int, 0); -module_param(spia_pedr, int, 0); -module_param(spia_peddr, int, 0); - -/* - * Define partitions for flash device - */ -static const struct mtd_partition partition_info[] = { - { - .name = "SPIA flash partition 1", - .offset = 0, - .size = 2 * 1024 * 1024}, - { - .name = "SPIA flash partition 2", - .offset = 2 * 1024 * 1024, - .size = 6 * 1024 * 1024} -}; - -#define NUM_PARTITIONS 2 - -/* - * hardware specific access to control-lines - * - * ctrl: - * NAND_CNE: bit 0 -> bit 2 - * NAND_CLE: bit 1 -> bit 0 - * NAND_ALE: bit 2 -> bit 1 - */ -static void spia_hwcontrol(struct mtd_info *mtd, int cmd) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - void __iomem *addr = spia_io_base + spia_pedr; - unsigned char bits; - - bits = (ctrl & NAND_CNE) << 2; - bits |= (ctrl & NAND_CLE | NAND_ALE) >> 1; - writeb((readb(addr) & ~0x7) | bits, addr); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * Main initialization routine - */ -static int __init spia_init(void) -{ - struct nand_chip *this; - - /* Allocate memory for MTD device structure and private data */ - spia_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!spia_mtd) { - printk("Unable to allocate SPIA NAND MTD device structure.\n"); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&spia_mtd[1]); - - /* Initialize structures */ - memset(spia_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - spia_mtd->priv = this; - spia_mtd->owner = THIS_MODULE; - - /* - * Set GPIO Port E control register so that the pins are configured - * to be outputs for controlling the NAND flash. - */ - (*(volatile unsigned char *)(spia_io_base + spia_peddr)) = 0x07; - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = (void __iomem *)spia_fio_base; - this->IO_ADDR_W = (void __iomem *)spia_fio_base; - /* Set address of hardware control function */ - this->cmd_ctrl = spia_hwcontrol; - /* 15 us command delay time */ - this->chip_delay = 15; - - /* Scan to find existence of the device */ - if (nand_scan(spia_mtd, 1)) { - kfree(spia_mtd); - return -ENXIO; - } - - /* Register the partitions */ - add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); - - /* Return happy */ - return 0; -} - -module_init(spia_init); - -/* - * Clean up routine - */ -static void __exit spia_cleanup(void) -{ - /* Release resources, unregister device */ - nand_release(spia_mtd); - - /* Free the MTD device structure */ - kfree(spia_mtd); -} - -module_exit(spia_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com"); -MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board"); diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 3041d1f7ae3..fb8fd35fa66 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -4,7 +4,7 @@ * Slightly murky pre-git history of the driver: * * Copyright (c) Ian Molton 2004, 2005, 2008 - * Original work, independant of sharps code. Included hardware ECC support. + * Original work, independent of sharps code. Included hardware ECC support. * Hard ECC did not work for writes in the early revisions. * Copyright (c) Dirk Opfer 2005. * Modifications developed from sharps code but @@ -121,9 +121,6 @@ struct tmio_nand { #define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd) -#ifdef CONFIG_MTD_CMDLINE_PARTS -static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif /*--------------------------------------------------------------------------*/ @@ -259,18 +256,6 @@ static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); } -static int -tmio_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - u16 *p = (u16 *) buf; - - for (len >>= 1; len; len--) - if (*(p++) != tmio_ioread16(tmio->fcr + FCR_DATA)) - return -EFAULT; - return 0; -} - static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode) { struct tmio_nand *tmio = mtd_to_tmio(mtd); @@ -319,7 +304,7 @@ static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); int ret; if (cell->enable) { @@ -363,7 +348,7 @@ static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); if (cell->disable) @@ -372,8 +357,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); - struct tmio_nand_data *data = cell->driver_data; + struct tmio_nand_data *data = dev_get_platdata(&dev->dev); struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *ccr = platform_get_resource(dev, @@ -382,20 +366,14 @@ static int tmio_probe(struct platform_device *dev) struct tmio_nand *tmio; struct mtd_info *mtd; struct nand_chip *nand_chip; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *parts; - int nbparts = 0; -#endif int retval; if (data == NULL) dev_warn(&dev->dev, "NULL platform data!\n"); - tmio = kzalloc(sizeof *tmio, GFP_KERNEL); - if (!tmio) { - retval = -ENOMEM; - goto err_kzalloc; - } + tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL); + if (!tmio) + return -ENOMEM; tmio->dev = dev; @@ -405,22 +383,18 @@ static int tmio_probe(struct platform_device *dev) mtd->priv = nand_chip; mtd->name = "tmio-nand"; - tmio->ccr = ioremap(ccr->start, resource_size(ccr)); - if (!tmio->ccr) { - retval = -EIO; - goto err_iomap_ccr; - } + tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); + if (!tmio->ccr) + return -EIO; tmio->fcr_base = fcr->start & 0xfffff; - tmio->fcr = ioremap(fcr->start, resource_size(fcr)); - if (!tmio->fcr) { - retval = -EIO; - goto err_iomap_fcr; - } + tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr)); + if (!tmio->fcr) + return -EIO; retval = tmio_hw_init(dev, tmio); if (retval) - goto err_hwinit; + return retval; /* Set address of NAND IO lines */ nand_chip->IO_ADDR_R = tmio->fcr; @@ -432,12 +406,12 @@ static int tmio_probe(struct platform_device *dev) nand_chip->read_byte = tmio_nand_read_byte; nand_chip->write_buf = tmio_nand_write_buf; nand_chip->read_buf = tmio_nand_read_buf; - nand_chip->verify_buf = tmio_nand_verify_buf; /* set eccmode using hardware ECC */ nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.size = 512; nand_chip->ecc.bytes = 6; + nand_chip->ecc.strength = 2; nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; nand_chip->ecc.calculate = tmio_nand_calculate_ecc; nand_chip->ecc.correct = tmio_nand_correct_data; @@ -448,8 +422,8 @@ static int tmio_probe(struct platform_device *dev) /* 15 us command delay time */ nand_chip->chip_delay = 15; - retval = request_irq(irq, &tmio_irq, - IRQF_DISABLED, dev_name(&dev->dev), tmio); + retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0, + dev_name(&dev->dev), tmio); if (retval) { dev_err(&dev->dev, "request_irq error %d\n", retval); goto err_irq; @@ -461,41 +435,19 @@ static int tmio_probe(struct platform_device *dev) /* Scan to find existence of the device */ if (nand_scan(mtd, 1)) { retval = -ENODEV; - goto err_scan; + goto err_irq; } /* Register the partitions */ -#ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS - nbparts = parse_mtd_partitions(mtd, part_probes, &parts, 0); -#endif - if (nbparts <= 0 && data) { - parts = data->partition; - nbparts = data->num_partitions; - } - - if (nbparts) - retval = add_mtd_partitions(mtd, parts, nbparts); - else -#endif - retval = add_mtd_device(mtd); - + retval = mtd_device_parse_register(mtd, NULL, NULL, + data ? data->partition : NULL, + data ? data->num_partitions : 0); if (!retval) return retval; nand_release(mtd); -err_scan: - if (tmio->irq) - free_irq(tmio->irq, tmio); err_irq: tmio_hw_stop(dev, tmio); -err_hwinit: - iounmap(tmio->fcr); -err_iomap_fcr: - iounmap(tmio->ccr); -err_iomap_ccr: - kfree(tmio); -err_kzalloc: return retval; } @@ -504,19 +456,14 @@ static int tmio_remove(struct platform_device *dev) struct tmio_nand *tmio = platform_get_drvdata(dev); nand_release(&tmio->mtd); - if (tmio->irq) - free_irq(tmio->irq, tmio); tmio_hw_stop(dev, tmio); - iounmap(tmio->fcr); - iounmap(tmio->ccr); - kfree(tmio); return 0; } #ifdef CONFIG_PM static int tmio_suspend(struct platform_device *dev, pm_message_t state) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); if (cell->suspend) cell->suspend(dev); @@ -527,7 +474,7 @@ static int tmio_suspend(struct platform_device *dev, pm_message_t state) static int tmio_resume(struct platform_device *dev) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); /* FIXME - is this required or merely another attack of the broken * SHARP platform? Looks suspicious. @@ -553,18 +500,7 @@ static struct platform_driver tmio_driver = { .resume = tmio_resume, }; -static int __init tmio_init(void) -{ - return platform_driver_register(&tmio_driver); -} - -static void __exit tmio_exit(void) -{ - platform_driver_unregister(&tmio_driver); -} - -module_init(tmio_init); -module_exit(tmio_exit); +module_platform_driver(tmio_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov"); diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index ca270a4881a..c1622a5ba81 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -9,6 +9,7 @@ * (C) Copyright TOSHIBA CORPORATION 2004-2007 * All Rights Reserved. */ +#include <linux/err.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> @@ -74,9 +75,6 @@ struct txx9ndfmc_drvdata { unsigned char hold; /* in gbusclock */ unsigned char spw; /* in gbusclock */ struct nand_hw_control hw_control; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *parts[MAX_TXX9NDFMC_DEV]; -#endif }; static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) @@ -89,7 +87,7 @@ static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) { struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); return drvdata->base + (reg << plat->shift); } @@ -134,25 +132,13 @@ static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) *buf++ = __raw_readl(ndfdtr); } -static int txx9ndfmc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct platform_device *dev = mtd_to_platdev(mtd); - void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); - - while (len--) - if (*buf++ != (uint8_t)__raw_readl(ndfdtr)) - return -EFAULT; - return 0; -} - static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *chip = mtd->priv; struct txx9ndfmc_priv *txx9_priv = chip->priv; struct platform_device *dev = txx9_priv->dev; - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); if (ctrl & NAND_CTRL_CHANGE) { u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); @@ -239,7 +225,7 @@ static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) static void txx9ndfmc_initialize(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int tmout = 100; @@ -288,29 +274,20 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) static int __init txx9ndfmc_probe(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; -#ifdef CONFIG_MTD_PARTITIONS - static const char *probes[] = { "cmdlinepart", NULL }; -#endif + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); int hold, spw; int i; struct txx9ndfmc_drvdata *drvdata; unsigned long gbusclk = plat->gbus_clock; struct resource *res; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - if (!devm_request_mem_region(&dev->dev, res->start, - resource_size(res), dev_name(&dev->dev))) - return -EBUSY; - drvdata->base = devm_ioremap(&dev->dev, res->start, - resource_size(res)); - if (!drvdata->base) - return -EBUSY; + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + drvdata->base = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(drvdata->base)) + return PTR_ERR(drvdata->base); hold = plat->hold ?: 20; /* tDH */ spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ @@ -337,19 +314,13 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) struct txx9ndfmc_priv *txx9_priv; struct nand_chip *chip; struct mtd_info *mtd; -#ifdef CONFIG_MTD_PARTITIONS - int nr_parts; -#endif if (!(plat->ch_mask & (1 << i))) continue; txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), GFP_KERNEL); - if (!txx9_priv) { - dev_err(&dev->dev, "Unable to allocate " - "TXx9 NDFMC MTD device structure.\n"); + if (!txx9_priv) continue; - } chip = &txx9_priv->chip; mtd = &txx9_priv->mtd; mtd->owner = THIS_MODULE; @@ -359,7 +330,6 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) chip->read_byte = txx9ndfmc_read_byte; chip->read_buf = txx9ndfmc_read_buf; chip->write_buf = txx9ndfmc_write_buf; - chip->verify_buf = txx9ndfmc_verify_buf; chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; chip->dev_ready = txx9ndfmc_dev_ready; chip->ecc.calculate = txx9ndfmc_calculate_ecc; @@ -369,6 +339,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; chip->chip_delay = 100; chip->controller = &drvdata->hw_control; @@ -399,13 +370,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) } mtd->name = txx9_priv->mtdname; -#ifdef CONFIG_MTD_PARTITIONS - nr_parts = parse_mtd_partitions(mtd, probes, - &drvdata->parts[i], 0); - if (nr_parts > 0) - add_mtd_partitions(mtd, drvdata->parts[i], nr_parts); -#endif - add_mtd_device(mtd); + mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); drvdata->mtds[i] = mtd; } @@ -417,7 +382,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int i; - platform_set_drvdata(dev, NULL); if (!drvdata) return 0; for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { @@ -431,9 +395,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) txx9_priv = chip->priv; nand_release(mtd); -#ifdef CONFIG_MTD_PARTITIONS - kfree(drvdata->parts[i]); -#endif kfree(txx9_priv->mtdname); kfree(txx9_priv); } @@ -460,18 +421,7 @@ static struct platform_driver txx9ndfmc_driver = { }, }; -static int __init txx9ndfmc_init(void) -{ - return platform_driver_probe(&txx9ndfmc_driver, txx9ndfmc_probe); -} - -static void __exit txx9ndfmc_exit(void) -{ - platform_driver_unregister(&txx9ndfmc_driver); -} - -module_init(txx9ndfmc_init); -module_exit(txx9ndfmc_exit); +module_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c new file mode 100644 index 00000000000..3f81dc8f214 --- /dev/null +++ b/drivers/mtd/nand/xway_nand.c @@ -0,0 +1,201 @@ +/* + * 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. + * + * Copyright © 2012 John Crispin <blogic@openwrt.org> + */ + +#include <linux/mtd/nand.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> + +#include <lantiq_soc.h> + +/* nand registers */ +#define EBU_ADDSEL1 0x24 +#define EBU_NAND_CON 0xB0 +#define EBU_NAND_WAIT 0xB4 +#define EBU_NAND_ECC0 0xB8 +#define EBU_NAND_ECC_AC 0xBC + +/* nand commands */ +#define NAND_CMD_ALE (1 << 2) +#define NAND_CMD_CLE (1 << 3) +#define NAND_CMD_CS (1 << 4) +#define NAND_WRITE_CMD_RESET 0xff +#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE) +#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE) +#define NAND_WRITE_DATA (NAND_CMD_CS) +#define NAND_READ_DATA (NAND_CMD_CS) +#define NAND_WAIT_WR_C (1 << 3) +#define NAND_WAIT_RD (0x1) + +/* we need to tel the ebu which addr we mapped the nand to */ +#define ADDSEL1_MASK(x) (x << 4) +#define ADDSEL1_REGEN 1 + +/* we need to tell the EBU that we have nand attached and set it up properly */ +#define BUSCON1_SETUP (1 << 22) +#define BUSCON1_BCGEN_RES (0x3 << 12) +#define BUSCON1_WAITWRC2 (2 << 8) +#define BUSCON1_WAITRDC2 (2 << 6) +#define BUSCON1_HOLDC1 (1 << 4) +#define BUSCON1_RECOVC1 (1 << 2) +#define BUSCON1_CMULT4 1 + +#define NAND_CON_CE (1 << 20) +#define NAND_CON_OUT_CS1 (1 << 10) +#define NAND_CON_IN_CS1 (1 << 8) +#define NAND_CON_PRE_P (1 << 7) +#define NAND_CON_WP_P (1 << 6) +#define NAND_CON_SE_P (1 << 5) +#define NAND_CON_CS_P (1 << 4) +#define NAND_CON_CSMUX (1 << 1) +#define NAND_CON_NANDM 1 + +static void xway_reset_chip(struct nand_chip *chip) +{ + unsigned long nandaddr = (unsigned long) chip->IO_ADDR_W; + unsigned long flags; + + nandaddr &= ~NAND_WRITE_ADDR; + nandaddr |= NAND_WRITE_CMD; + + /* finish with a reset */ + spin_lock_irqsave(&ebu_lock, flags); + writeb(NAND_WRITE_CMD_RESET, (void __iomem *) nandaddr); + while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) + ; + spin_unlock_irqrestore(&ebu_lock, flags); +} + +static void xway_select_chip(struct mtd_info *mtd, int chip) +{ + + switch (chip) { + case -1: + ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON); + ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON); + break; + case 0: + ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON); + ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON); + break; + default: + BUG(); + } +} + +static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + unsigned long nandaddr = (unsigned long) this->IO_ADDR_W; + unsigned long flags; + + if (ctrl & NAND_CTRL_CHANGE) { + nandaddr &= ~(NAND_WRITE_CMD | NAND_WRITE_ADDR); + if (ctrl & NAND_CLE) + nandaddr |= NAND_WRITE_CMD; + else + nandaddr |= NAND_WRITE_ADDR; + this->IO_ADDR_W = (void __iomem *) nandaddr; + } + + if (cmd != NAND_CMD_NONE) { + spin_lock_irqsave(&ebu_lock, flags); + writeb(cmd, this->IO_ADDR_W); + while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) + ; + spin_unlock_irqrestore(&ebu_lock, flags); + } +} + +static int xway_dev_ready(struct mtd_info *mtd) +{ + return ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD; +} + +static unsigned char xway_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + unsigned long nandaddr = (unsigned long) this->IO_ADDR_R; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ebu_lock, flags); + ret = ltq_r8((void __iomem *)(nandaddr + NAND_READ_DATA)); + spin_unlock_irqrestore(&ebu_lock, flags); + + return ret; +} + +static int xway_nand_probe(struct platform_device *pdev) +{ + struct nand_chip *this = platform_get_drvdata(pdev); + unsigned long nandaddr = (unsigned long) this->IO_ADDR_W; + const __be32 *cs = of_get_property(pdev->dev.of_node, + "lantiq,cs", NULL); + u32 cs_flag = 0; + + /* load our CS from the DT. Either we find a valid 1 or default to 0 */ + if (cs && (*cs == 1)) + cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1; + + /* setup the EBU to run in NAND mode on our base addr */ + ltq_ebu_w32(CPHYSADDR(nandaddr) + | ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1); + + ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2 + | BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1 + | BUSCON1_CMULT4, LTQ_EBU_BUSCON1); + + ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P + | NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P + | cs_flag, EBU_NAND_CON); + + /* finish with a reset */ + xway_reset_chip(this); + + return 0; +} + +/* allow users to override the partition in DT using the cmdline */ +static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL }; + +static struct platform_nand_data xway_nand_data = { + .chip = { + .nr_chips = 1, + .chip_delay = 30, + .part_probe_types = part_probes, + }, + .ctrl = { + .probe = xway_nand_probe, + .cmd_ctrl = xway_cmd_ctrl, + .dev_ready = xway_dev_ready, + .select_chip = xway_select_chip, + .read_byte = xway_read_byte, + } +}; + +/* + * Try to find the node inside the DT. If it is available attach out + * platform_nand_data + */ +static int __init xway_register_nand(void) +{ + struct device_node *node; + struct platform_device *pdev; + + node = of_find_compatible_node(NULL, NULL, "lantiq,nand-xway"); + if (!node) + return -ENOENT; + pdev = of_find_device_by_node(node); + if (!pdev) + return -EINVAL; + pdev->dev.platform_data = &xway_nand_data; + of_node_put(node); + return 0; +} + +subsys_initcall(xway_register_nand); |
