Commit e94696c1 authored by Oleg Dzhimiev's avatar Oleg Dzhimiev

NAND OTP support

parent 527402be
config MTD_NAND_ECC
tristate
config MTD_NAND_ECC_SMC
bool "NAND ECC Smart Media byte order"
depends on MTD_NAND_ECC
default n
help
Software ECC according to the Smart Media Specification.
The original Linux implementation had byte 0 and 1 swapped.
menuconfig MTD_NAND
tristate "NAND Device Support"
depends on MTD
select MTD_NAND_IDS
select MTD_NAND_ECC
help
This enables support for accessing all type of NAND flash
devices. For further information see
<http://www.linux-mtd.infradead.org/doc/nand.html>.
if MTD_NAND
config MTD_NAND_BCH
tristate
select BCH
depends on MTD_NAND_ECC_BCH
default MTD_NAND
config MTD_NAND_ECC_BCH
bool "Support software BCH ECC"
default n
help
This enables support for software BCH error correction. Binary BCH
codes are more powerful and cpu intensive than traditional Hamming
ECC codes. They are used with NAND devices requiring more than 1 bit
of error correction.
config MTD_NAND_OTP
bool "Support access to one-time programmable area of some Micron chips"
select HAVE_MTD_OTP
help
This enables support for reading and writing to the OTP area of some
Micron chips.
config MTD_SM_COMMON
tristate
default n
config MTD_NAND_DENALI
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_PCI
help
Some platforms place the NAND chip size in a scratch register
because (some versions of) the driver aren't able to automatically
determine the size of certain chips. Set the address of the
scratch register here to enable this feature. On Intel Moorestown
boards, the scratch register is at 0xFF108018.
config MTD_NAND_GPIO
tristate "GPIO assisted NAND Flash driver"
depends on GPIOLIB
help
This enables a NAND flash driver where control signals are
connected to GPIO pins, and commands and data are communicated
via a memory mapped interface.
config MTD_NAND_AMS_DELTA
tristate "NAND Flash device on Amstrad E3"
depends on MACH_AMS_DELTA
default y
help
Support for NAND flash on Amstrad E3 (Delta).
config MTD_NAND_OMAP2
tristate "NAND Flash device on OMAP2, OMAP3 and OMAP4"
depends on ARCH_OMAP2PLUS
help
Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4
platforms.
config MTD_NAND_OMAP_BCH
depends on MTD_NAND_OMAP2
bool "Support hardware based BCH error correction"
default n
select BCH
help
This config enables the ELM hardware engine, which can be used to
locate and correct errors when using BCH ECC scheme. This offloads
the cpu from doing ECC error searching and correction. However some
legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
so this is optional for them.
config MTD_NAND_OMAP_BCH_BUILD
def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
config MTD_NAND_IDS
tristate
config MTD_NAND_RICOH
tristate "Ricoh xD card reader"
default n
depends on PCI
select MTD_SM_COMMON
help
Enable support for Ricoh R5C852 xD card reader
You also need to enable ether
NAND SSFDC (SmartMedia) read only translation layer' or new
expermental, readwrite
'SmartMedia/xD new translation layer'
config MTD_NAND_AU1550
tristate "Au1550/1200 NAND support"
depends on MIPS_ALCHEMY
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_BF5XX
tristate "Blackfin on-chip NAND Flash Controller driver"
depends on BF54x || BF52x
help
This enables the Blackfin on-chip NAND flash controller
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
This driver can also be built as a module. If so, the module
will be called bf5xx-nand.
config MTD_NAND_BF5XX_HWECC
bool "BF5XX NAND Hardware ECC"
default y
depends on MTD_NAND_BF5XX
help
Enable the use of the BF5XX's internal ECC generator when
using NAND.
config MTD_NAND_BF5XX_BOOTROM_ECC
bool "Use Blackfin BootROM ECC Layout"
default n
depends on MTD_NAND_BF5XX_HWECC
help
If you wish to modify NAND pages and allow the Blackfin on-chip
BootROM to boot from them, say Y here. This is only necessary
if you are booting U-Boot out of NAND and you wish to update
U-Boot from Linux' userspace. Otherwise, you should say N here.
If unsure, say N.
config MTD_NAND_S3C2410
tristate "NAND Flash support for Samsung S3C SoCs"
depends on ARCH_S3C24XX || ARCH_S3C64XX
help
This enables the NAND flash controller on the S3C24xx and S3C64xx
SoCs
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_S3C2410_DEBUG
bool "Samsung S3C NAND driver debug"
depends on MTD_NAND_S3C2410
help
Enable debugging of the S3C NAND driver
config MTD_NAND_S3C2410_HWECC
bool "Samsung S3C NAND Hardware ECC"
depends on MTD_NAND_S3C2410
help
Enable the use of the controller's internal ECC generator when
using NAND. Early versions of the chips have had problems with
incorrect ECC generation, and if using these, the default of
software ECC is preferable.
config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller"
depends on 4xx
select MTD_NAND_ECC_SMC
help
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
config MTD_NAND_S3C2410_CLKSTOP
bool "Samsung S3C NAND IDLE clock stop"
depends on MTD_NAND_S3C2410
default n
help
Stop the clock to the NAND controller when there is no chip
selected to save power. This will mean there is a small delay
when the is NAND chip selected or released, but will save
approximately 5mA of power when there is nothing happening.
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
depends on HAS_IOMEM
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
This is a reimplementation of M-Systems DiskOnChip 2000,
Millennium and Millennium Plus as a standard NAND device driver,
as opposed to the earlier self-contained MTD device drivers.
This should enable, among other things, proper JFFS2 operation on
these devices.
config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
bool "Advanced detection options for DiskOnChip"
depends on MTD_NAND_DISKONCHIP
help
This option allows you to specify nonstandard address at which to
probe for a DiskOnChip, or to change the detection options. You
are unlikely to need any of this unless you are using LinuxBIOS.
Say 'N'.
config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
depends on MTD_NAND_DISKONCHIP
default "0"
---help---
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
This option allows you to specify a single address at which to probe
for the device, which is useful if you have other devices in that
range which get upset when they are probed.
(Note that on PowerPC, the normal probe will only check at
0xE4000000.)
Normally, you should leave this set to zero, to allow the probe at
the normal addresses.
config MTD_NAND_DISKONCHIP_PROBE_HIGH
bool "Probe high addresses"
depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
help
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
This option changes to make it probe between 0xFFFC8000 and
0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
useful to you. Say 'N'.
config MTD_NAND_DISKONCHIP_BBTWRITE
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
depends on MTD_NAND_DISKONCHIP
help
On DiskOnChip devices shipped with the INFTL filesystem (Millennium
and 2000 TSOP/Alon), Linux reserves some space at the end of the
device for the Bad Block Table (BBT). If you have existing INFTL
data on your device (created by non-Linux tools such as M-Systems'
DOS drivers), your data might overlap the area Linux wants to use for
the BBT. If this is a concern for you, leave this option disabled and
Linux will not write BBT data into this area.
The downside of leaving this option disabled is that if bad blocks
are detected by Linux, they will not be recorded in the BBT, which
could cause future problems.
Once you enable this option, new filesystems (INFTL or others, created
in Linux or other operating systems) will not use the reserved area.
The only reason not to enable this option is to prevent damage to
preexisting filesystems.
Even if you leave this disabled, you can enable BBT writes at module
load time (assuming you build diskonchip as a module) with the module
parameter "inftl_bbt_write=1".
config MTD_NAND_DOCG4
tristate "Support for DiskOnChip G4"
depends on HAS_IOMEM
select BCH
select BITREVERSE
help
Support for diskonchip G4 nand flash, found in various smartphones and
PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
Portege G900, Asus P526, and O2 XDA Zinc.
With this driver you will be able to use UBI and create a ubifs on the
device, so you may wish to consider enabling UBI and UBIFS as well.
These devices ship with the Mys/Sandisk SAFTL formatting, for which
there is currently no mtd parser, so you may want to use command line
partitioning to segregate write-protected blocks. On the Treo680, the
first five erase blocks (256KiB each) are write-protected, followed
by the block containing the saftl partition table. This is probably
typical.
config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on ARCH_PXA
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
depends on PCI
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
Use NAND flash attached to the CAFÉ chip designed for the OLPC
laptop.
config MTD_NAND_CS553X
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
depends on X86_32
help
The CS553x companion chips for the AMD Geode processor
include NAND flash controllers with built-in hardware ECC
capabilities; enabling this option will allow you to use
these. The driver will check the MSRs to verify that the
controller is enabled for NAND, and currently requires that
the controller be in MMIO mode.
If you say "m", the module will be called cs553x_nand.
config MTD_NAND_ATMEL
tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32"
depends on ARCH_AT91 || AVR32
help
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 and AVR32 processors.
config MTD_NAND_PXA3xx
tristate "NAND support on PXA3xx and Armada 370/XP"
depends on PXA3xx || ARCH_MMP || PLAT_ORION
help
This enables the driver for the NAND flash device found on
PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
config MTD_NAND_SLC_LPC32XX
tristate "NXP LPC32xx SLC Controller"
depends on ARCH_LPC32XX
help
Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
chips) NAND controller. This is the default for the PHYTEC 3250
reference board which contains a NAND256R3A2CZA6 chip.
Please check the actual NAND chip connected and its support
by the SLC NAND controller.
config MTD_NAND_MLC_LPC32XX
tristate "NXP LPC32xx MLC Controller"
depends on ARCH_LPC32XX
help
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
controller. This is the default for the WORK92105 controller
board.
Please check the actual NAND chip connected and its support
by the MLC NAND controller.
config MTD_NAND_CM_X270
tristate "Support for NAND Flash on CM-X270 modules"
depends on MACH_ARMCORE
config MTD_NAND_PASEMI
tristate "NAND support for PA Semi PWRficient"
depends on PPC_PASEMI
help
Enables support for NAND Flash interface on PA Semi PWRficient
based boards
config MTD_NAND_TMIO
tristate "NAND Flash device on Toshiba Mobile IO Controller"
depends on MFD_TMIO
help
Support for NAND flash connected to a Toshiba Mobile IO
Controller in some PDAs, including the Sharp SL6000x.
config MTD_NAND_NANDSIM
tristate "Support for NAND Flash Simulator"
help
The simulator may simulate various NAND flash chips for the
MTD nand layer.
config MTD_NAND_GPMI_NAND
tristate "GPMI NAND Flash Controller driver"
depends on MTD_NAND && MXS_DMA
help
Enables NAND Flash support for IMX23, IMX28 or IMX6.
The GPMI controller is very powerful, with the help of BCH
module, it can do the hardware ECC. The GPMI supports several
NAND flashs at the same time. 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_NAND_ORION
tristate "NAND Flash support for Marvell Orion SoC"
depends on PLAT_ORION
help
This enables the NAND flash controller on Orion machines.
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_FSL_ELBC
tristate "NAND support for Freescale eLBC controllers"
depends on PPC
select FSL_LBC
help
Various Freescale chips, including the 8313, include a NAND Flash
Controller Module with built-in hardware ECC capabilities.
Enabling this option will enable you to use this to control
external NAND devices.
config MTD_NAND_FSL_IFC
tristate "NAND support for Freescale IFC controller"
depends on 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
select FSL_LBC
help
Enables support for NAND Flash chips wired onto Freescale PowerPC
processor localbus with User-Programmable Machine support.
config MTD_NAND_MPC5121_NFC
tristate "MPC5121 built-in NAND Flash Controller support"
depends on PPC_MPC512x
help
This enables the driver for the NAND flash controller on the
MPC5121 SoC.
config MTD_NAND_MXC
tristate "MXC NAND support"
depends on ARCH_MXC
help
This enables the driver for the NAND flash controller on the
MXC processors.
config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL"
depends on SUPERH || 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/Keystone SoC"
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF)
help
Enable the driver for NAND flash chips on Texas Instruments
DaVinci/Keystone processors.
config MTD_NAND_TXX9NDFMC
tristate "NAND Flash support for TXx9 SoC"
depends on SOC_TX4938 || SOC_TX4939
help
This enables the NAND flash controller on the TXx9 SoCs.
config MTD_NAND_SOCRATES
tristate "Support for NAND on Socrates board"
depends on SOCRATES
help
Enables support for NAND Flash chips wired onto Socrates board.
config MTD_NAND_NUC900
tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
depends on ARCH_W90X900
help
This enables the driver for the NAND Flash on evaluation board based
on w90p910 / NUC9xx.
config MTD_NAND_PL35X
tristate "ARM Pl35X NAND flash driver"
depends on MTD_NAND && ARM
depends on PL35X_SMC
help
This enables access to the NAND flash device on PL351/PL353
SMC controller.
config MTD_NAND_JZ4740
tristate "Support for JZ4740 SoC NAND controller"
depends on MACH_JZ4740
help
Enables support for NAND Flash on JZ4740 SoC based boards.
config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC"
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).
config MTD_NAND_SUNXI
tristate "Support for NAND on Allwinner SoCs"
depends on ARCH_SUNXI
help
Enables support for NAND Flash chips on Allwinner SoCs.
config MTD_NAND_ARASAN
tristate "Support for Arasan Nand Flash controller"
depends on MTD_NAND
help
Enables the driver for the Arasan Nand Flash controller in ZynqMP SoC.
config MTD_NAND_HISI504
tristate "Support for NAND controller on Hisilicon SoC Hip04"
depends on HAS_DMA
help
Enables support for NAND controller on Hisilicon SoC Hip04.
endif # MTD_NAND
#
# linux/drivers/nand/Makefile
#
obj-$(CONFIG_MTD_NAND) += nand.o
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
obj-$(CONFIG_MTD_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_AMS_DELTA) += ams-delta.o
obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o
obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o
obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_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/
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_PL35X) += pl35x_nand.o
obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nfc.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o nandchip-micron.o
#include <linux/mtd/mtd.h>
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
int nand_get_device(struct mtd_info *mtd, int new_state);
void nand_release_device(struct mtd_info *mtd);
int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
void nandchip_micron_init(struct mtd_info *mtd, int dev_id);
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/flashchip.h>
#include <linux/io.h>
#include "nand.h"
#define MICRON_SETFEATURE_ARRAYOP 0x90
#define MICRON_SETFEATURE_ARRAYOP_NORMAL ((uint8_t[]){0,0,0,0})
#define MICRON_SETFEATURE_ARRAYOP_OTP ((uint8_t[]){1,0,0,0})
#define MICRON_SETFEATURE_ARRAYOP_OTPPROTECT ((uint8_t[]){3,0,0,0})
#define MICRON_NUM_OTP_FIRSTPAGE 2
#define MICRON_NUM_OTP_PAGES 30
static int mt29f_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
size_t len)
{
int i;
if (len < MICRON_NUM_OTP_PAGES * sizeof(*buf))
return -ENOSPC;
for (i = 0; i < MICRON_NUM_OTP_PAGES; ++i) {
buf->start = i * mtd->writesize;
buf->length = mtd->writesize;
/*
* XXX: don't know how to find out, if a page is locked
*/
buf->locked = 0;
buf++;
}
return MICRON_NUM_OTP_PAGES * sizeof(*buf);
}
static int mt29f_read_user_prot_reg(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;
u8 get_feature,i;
/* Valid pages in otp are 02h-1Fh. */
if (from > MICRON_NUM_OTP_PAGES << chip->page_shift)
return -EIO;
if (from + len > MICRON_NUM_OTP_PAGES << chip->page_shift)
len = (MICRON_NUM_OTP_PAGES << chip->page_shift) - from;
from += MICRON_NUM_OTP_FIRSTPAGE << chip->page_shift;
/* XXX: FL_READING? */
nand_get_device(mtd, FL_READING);
chip->select_chip(mtd, 0);
ret = chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,MICRON_SETFEATURE_ARRAYOP_OTP);
ndelay(1000);
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, MICRON_SETFEATURE_ARRAYOP, -1);
get_feature = readb(chip->IO_ADDR_R);
pr_debug("Feature on: 0x%02x\n",get_feature);
if (ret)
goto out;
ops.len = len;
ops.datbuf = buf;
ops.oobbuf = NULL;
ops.mode = 0;
/*
* XXX: some things in nand_do_read_ops might be wrong for OTP. e.g.
* chip->pagemask, chip->pagebuf handling, caching
*/
ret = nand_do_read_ops(mtd, from, &ops);
*retlen = ops.retlen;
/* nand_do_read_ops deselects the chip so reselect here */
chip->select_chip(mtd, 0);
chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,MICRON_SETFEATURE_ARRAYOP_NORMAL);
ndelay(1000);
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, MICRON_SETFEATURE_ARRAYOP, -1);
get_feature = readb(chip->IO_ADDR_R);
pr_debug("Feature off: 0x%02x\n",get_feature);
out:
nand_release_device(mtd);
return ret;
}
static int mt29f_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
size_t len, size_t *retlen, uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
struct mtd_oob_ops ops;
int ret;
pr_debug("mt29f_write_user_prot_reg start!!!");
/* Valid pages in otp are 02h-1Fh. */
if (to > MICRON_NUM_OTP_PAGES << chip->page_shift)
return -EIO;
if (to + len > MICRON_NUM_OTP_PAGES << chip->page_shift)
len = (MICRON_NUM_OTP_PAGES << chip->page_shift) - to;
to += MICRON_NUM_OTP_FIRSTPAGE << chip->page_shift;
nand_get_device(mtd, FL_WRITING);
chip->select_chip(mtd, 0);
ret = chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_OTP);
if (ret)
goto out;
ops.len = len;
ops.datbuf = buf;
ops.oobbuf = NULL;
ops.mode = 0;
/*
* some things in nand_do_write_ops might be wrong for OTP. e.g.
* chip->pagemask, chip->pagebuf handling
*/
ret = nand_do_write_ops(mtd, to, &ops);
*retlen = ops.retlen;
/* nand_do_write_ops deselects the chip so reselect here */
chip->select_chip(mtd, 0);
chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_NORMAL);
out:
nand_release_device(mtd);
return ret;
}
static int mt29f_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
struct nand_chip *chip = mtd->priv;
int ret;
int i;
/* assert from and len are aligned */
if (NOTALIGNED(from) || NOTALIGNED(len)) {
pr_notice("%s: attempt to lock non page aligned data\n",
__func__);
return -EINVAL;
}
if (!len)
return 0;
if (from > MICRON_NUM_OTP_PAGES << chip->page_shift)
return -EINVAL;
if (from + len > MICRON_NUM_OTP_PAGES << chip->page_shift)
return -EINVAL;
from += MICRON_NUM_OTP_FIRSTPAGE << chip->page_shift;
nand_get_device(mtd, FL_WRITING);
chip->select_chip(mtd, 0);
ret = chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_OTPPROTECT);
if (ret)
goto out;
for (i = 0; i < len << chip->page_shift; ++i) {
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0,
(from << chip->page_shift) + i);
chip->write_byte(mtd, 0);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
}
chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_NORMAL);
out:
nand_release_device(mtd);
return ret;
}
void nandchip_micron_init(struct mtd_info *mtd, int dev_id)
{
/*
* OTP is available on (at least) Micron's MT29F2G{08,16}AB[AB]EA,
* MT29F[48]G{08,16}AB[AB]DA, MT29F16G08AJADA having device IDs:
* 0xda, 0xca, 0xaa, 0xba;
* 0xdc, 0xcc, 0xac, 0xbc, 0xa3, 0xb3, 0xd3, 0xc3;
* 0xd3
*/
if (IS_ENABLED(CONFIG_MTD_NAND_OTP) &&
((dev_id + 0x20) & 0xc0) == 0xc0 &&
((dev_id & 0x09) == 8 || (dev_id & 0x0f) == 3)) {
mtd->_get_user_prot_info = mt29f_get_user_prot_info;
mtd->_read_user_prot_reg = mt29f_read_user_prot_reg;
mtd->_write_user_prot_reg = mt29f_write_user_prot_reg;
mtd->_lock_user_prot_reg = mt29f_lock_user_prot_reg;
}
}
/*
* ARM PL35X NAND Flash Controller Driver
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.
*
* This driver is based on plat_nand.c and mxc_nand.c drivers
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/memory/pl35x-smc.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define PL35X_NAND_DRIVER_NAME "pl35x-nand"
/* NAND flash driver defines */
#define PL35X_NAND_CMD_PHASE 1 /* End command valid in command phase */
#define PL35X_NAND_DATA_PHASE 2 /* End command valid in data phase */
#define PL35X_NAND_ECC_SIZE 512 /* Size of data for ECC operation */
/* Flash memory controller operating parameters */
#define PL35X_NAND_ECC_CONFIG (BIT(4) | /* ECC read at end of page */ \
(0 << 5)) /* No Jumping */
/* AXI Address definitions */
#define START_CMD_SHIFT 3
#define END_CMD_SHIFT 11
#define END_CMD_VALID_SHIFT 20
#define ADDR_CYCLES_SHIFT 21
#define CLEAR_CS_SHIFT 21
#define ECC_LAST_SHIFT 10
#define COMMAND_PHASE (0 << 19)
#define DATA_PHASE BIT(19)
#define PL35X_NAND_ECC_LAST BIT(ECC_LAST_SHIFT) /* Set ECC_Last */
#define PL35X_NAND_CLEAR_CS BIT(CLEAR_CS_SHIFT) /* Clear chip select */
#define ONDIE_ECC_FEATURE_ADDR 0x90
#define PL35X_NAND_ECC_BUSY_TIMEOUT (1 * HZ)
#define PL35X_NAND_DEV_BUSY_TIMEOUT (1 * HZ)
#define PL35X_NAND_LAST_TRANSFER_LENGTH 4
/* Inline function for the NAND controller register write */
static inline void pl35x_nand_write32(void __iomem *addr, u32 val)
{
writel_relaxed((val), (addr));
}
/**
* struct pl35x_nand_command_format - Defines NAND flash command format
* @start_cmd: First cycle command (Start command)
* @end_cmd: Second cycle command (Last command)
* @addr_cycles: Number of address cycles required to send the address
* @end_cmd_valid: The second cycle command is valid for cmd or data phase
*/
struct pl35x_nand_command_format {
int start_cmd;
int end_cmd;
u8 addr_cycles;
u8 end_cmd_valid;
};
/**
* struct pl35x_nand_info - Defines the NAND flash driver instance
* @chip: NAND chip information structure
* @mtd: MTD information structure
* @parts: Pointer to the mtd_partition structure
* @nand_base: Virtual address of the NAND flash device
* @end_cmd_pending: End command is pending
* @end_cmd: End command
* @row_addr_cycles: Row address cycles
* @col_addr_cycles: Column address cycles
*/
struct pl35x_nand_info {
struct nand_chip chip;
struct mtd_info mtd;
struct mtd_partition *parts;
void __iomem *nand_base;
unsigned long end_cmd_pending;
unsigned long end_cmd;
u8 row_addr_cycles;
u8 col_addr_cycles;
};
/*
* The NAND flash operations command format
*/
static const struct pl35x_nand_command_format pl35x_nand_commands[] = {
{NAND_CMD_READ0, NAND_CMD_READSTART, 5, PL35X_NAND_CMD_PHASE},
{NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, PL35X_NAND_CMD_PHASE},
{NAND_CMD_READID, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_STATUS, NAND_CMD_NONE, 0, NAND_CMD_NONE},
{NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, PL35X_NAND_DATA_PHASE},
{NAND_CMD_RNDIN, NAND_CMD_NONE, 2, NAND_CMD_NONE},
{NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, PL35X_NAND_CMD_PHASE},
{NAND_CMD_RESET, NAND_CMD_NONE, 0, NAND_CMD_NONE},
{NAND_CMD_PARAM, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_UNLOCK1, NAND_CMD_NONE, 3, NAND_CMD_NONE},
{NAND_CMD_UNLOCK2, NAND_CMD_NONE, 3, NAND_CMD_NONE},
{NAND_CMD_LOCK, NAND_CMD_NONE, 0, NAND_CMD_NONE},
{NAND_CMD_NONE, NAND_CMD_NONE, 0, 0},
/* Add all the flash commands supported by the flash device and Linux */
/*
* The cache program command is not supported by driver because driver
* cant differentiate between page program and cached page program from
* start command, these commands can be differentiated through end
* command, which doesn't fit in to the driver design. The cache program
* command is not supported by NAND subsystem also, look at 1612 line
* number (in nand_write_page function) of nand_base.c file.
* {NAND_CMD_SEQIN, NAND_CMD_CACHEDPROG, 5, PL35X_NAND_YES},
*/
};
/* Define default oob placement schemes for large and small page devices */
static struct nand_ecclayout nand_oob_16 = {
.eccbytes = 3,
.eccpos = {0, 1, 2},
.oobfree = {
{.offset = 8,
. length = 8} }
};
static struct nand_ecclayout nand_oob_64 = {
.eccbytes = 12,
.eccpos = {
52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63},
.oobfree = {
{.offset = 2,
.length = 50} }
};
static struct nand_ecclayout ondie_nand_oob_64 = {
.eccbytes = 32,
.eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
24, 25, 26, 27, 28, 29, 30, 31,
40, 41, 42, 43, 44, 45, 46, 47,
56, 57, 58, 59, 60, 61, 62, 63
},
.oobfree = {
{ .offset = 4, .length = 4 },
{ .offset = 20, .length = 4 },
{ .offset = 36, .length = 4 },
{ .offset = 52, .length = 4 }
}
};
/* Generic flash bbt decriptors */
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
static uint8_t 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 | NAND_BBT_PERCHIP,
.offs = 4,
.len = 4,
.veroffs = 20,
.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 | NAND_BBT_PERCHIP,
.offs = 4,
.len = 4,
.veroffs = 20,
.maxblocks = 4,
.pattern = mirror_pattern
};
/**
* pl35x_nand_calculate_hwecc - Calculate Hardware ECC
* @mtd: Pointer to the mtd_info structure
* @data: Pointer to the page data
* @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored
*
* This function retrieves the Hardware ECC data from the controller and returns
* ECC data back to the MTD subsystem.
*
* Return: 0 on success or error value on failure
*/
static int pl35x_nand_calculate_hwecc(struct mtd_info *mtd,
const u8 *data, u8 *ecc_code)
{
u32 ecc_value, ecc_status;
u8 ecc_reg, ecc_byte;
unsigned long timeout = jiffies + PL35X_NAND_ECC_BUSY_TIMEOUT;
/* Wait till the ECC operation is complete or timeout */
do {
if (pl35x_smc_ecc_is_busy())
cpu_relax();
else
break;
} while (!time_after_eq(jiffies, timeout));
if (time_after_eq(jiffies, timeout)) {
pr_err("%s timed out\n", __func__);
return -ETIMEDOUT;
}
for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) {
/* Read ECC value for each block */
ecc_value = pl35x_smc_get_ecc_val(ecc_reg);
ecc_status = (ecc_value >> 24) & 0xFF;
/* ECC value valid */
if (ecc_status & 0x40) {
for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) {
/* Copy ECC bytes to MTD buffer */
*ecc_code = ecc_value & 0xFF;
ecc_value = ecc_value >> 8;
ecc_code++;
}
} else {
pr_warn("%s status failed\n", __func__);
return -1;
}
}
return 0;
}
/**
* onehot - onehot function
* @value: Value to check for onehot
*
* This function checks whether a value is onehot or not.
* onehot is if and only if onebit is set.
*
* Return: 1 if it is onehot else 0
*/
static int onehot(unsigned short value)
{
return (value & (value - 1)) == 0;
}
/**
* pl35x_nand_correct_data - ECC correction function
* @mtd: Pointer to the mtd_info structure
* @buf: Pointer to the page data
* @read_ecc: Pointer to the ECC value read from spare data area
* @calc_ecc: Pointer to the calculated ECC value
*
* This function corrects the ECC single bit errors & detects 2-bit errors.
*
* Return: 0 if no ECC errors found
* 1 if single bit error found and corrected.
* -1 if multiple ECC errors found.
*/
static int pl35x_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc,
unsigned char *calc_ecc)
{
unsigned char bit_addr;
unsigned int byte_addr;
unsigned short ecc_odd, ecc_even, read_ecc_lower, read_ecc_upper;
unsigned short calc_ecc_lower, calc_ecc_upper;
read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff;
read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff;
calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff;
calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff;
ecc_odd = read_ecc_lower ^ calc_ecc_lower;
ecc_even = read_ecc_upper ^ calc_ecc_upper;
if ((ecc_odd == 0) && (ecc_even == 0))
return 0; /* no error */
if (ecc_odd == (~ecc_even & 0xfff)) {
/* bits [11:3] of error code is byte offset */
byte_addr = (ecc_odd >> 3) & 0x1ff;
/* bits [2:0] of error code is bit offset */
bit_addr = ecc_odd & 0x7;
/* Toggling error bit */
buf[byte_addr] ^= (1 << bit_addr);
return 1;
}
if (onehot(ecc_odd | ecc_even) == 1)
return 1; /* one error in parity */
return -1; /* Uncorrectable error */
}
/**
* pl35x_nand_read_oob - [REPLACABLE] the most common OOB data read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl35x_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
unsigned long data_phase_addr;
uint8_t *p;
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
p = chip->oob_poi;
chip->read_buf(mtd, p,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* pl35x_nand_write_oob - [REPLACABLE] the most common OOB data write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to write
*
* Return: Zero on success and EIO on failure
*/
static int pl35x_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
int status = 0;
const uint8_t *buf = chip->oob_poi;
unsigned long data_phase_addr;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
chip->write_buf(mtd, buf,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
buf += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, buf, PL35X_NAND_LAST_TRANSFER_LENGTH);
/* 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;
}
/**
* pl35x_nand_read_page_raw - [Intern] read raw page data without ecc
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl35x_nand_read_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
unsigned long data_phase_addr;
uint8_t *p;
chip->read_buf(mtd, buf, mtd->writesize);
p = chip->oob_poi;
chip->read_buf(mtd, p,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* pl35x_nand_write_page_raw - [Intern] raw page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
*
* Return: Always return zero
*/
static int pl35x_nand_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
unsigned long data_phase_addr;
uint8_t *p;
chip->write_buf(mtd, buf, mtd->writesize);
p = chip->oob_poi;
chip->write_buf(mtd, p,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* nand_write_page_hwecc - Hardware ECC based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
*
* This functions writes data and hardware generated ECC values in to the page.
*
* Return: Always return zero
*/
static int pl35x_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 eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
unsigned long data_phase_addr;
uint8_t *oob_ptr;
for ( ; (eccsteps - 1); eccsteps--) {
chip->write_buf(mtd, p, eccsize);
p += eccsize;
}
chip->write_buf(mtd, p, (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Set ECC Last bit to 1 */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL35X_NAND_ECC_LAST;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Wait for ECC to be calculated and read the error values */
p = buf;
chip->ecc.calculate(mtd, p, &ecc_calc[0]);
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]);
/* Clear ECC last bit */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr &= ~PL35X_NAND_ECC_LAST;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
/* Write the spare area with ECC bytes */
oob_ptr = chip->oob_poi;
chip->write_buf(mtd, oob_ptr,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
oob_ptr += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
chip->write_buf(mtd, oob_ptr, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* pl35x_nand_write_page_swecc - [REPLACABLE] software ecc based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
*
* Return: Always return zero
*/
static int pl35x_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;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
/* 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, 1);
return 0;
}
/**
* pl35x_nand_read_page_hwecc - Hardware ECC based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* This functions reads data and checks the data integrity by comparing hardware
* generated ECC values and read ECC values from spare area.
*
* Return: 0 always and updates ECC operation status in to MTD structure
*/
static int pl35x_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, stat, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
unsigned long data_phase_addr;
uint8_t *oob_ptr;
for ( ; (eccsteps - 1); eccsteps--) {
chip->read_buf(mtd, p, eccsize);
p += eccsize;
}
chip->read_buf(mtd, p, (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Set ECC Last bit to 1 */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL35X_NAND_ECC_LAST;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Read the calculated ECC value */
p = buf;
chip->ecc.calculate(mtd, p, &ecc_calc[0]);
/* Clear ECC last bit */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr &= ~PL35X_NAND_ECC_LAST;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
/* Read the stored ECC value */
oob_ptr = chip->oob_poi;
chip->read_buf(mtd, oob_ptr,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
/* de-assert chip select */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
oob_ptr += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
chip->read_buf(mtd, oob_ptr, PL35X_NAND_LAST_TRANSFER_LENGTH);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = ~(chip->oob_poi[eccpos[i]]);
eccsteps = chip->ecc.steps;
p = buf;
/* Check ECC error for all blocks and correct if it is correctable */
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
/**
* pl35x_nand_read_page_swecc - [REPLACABLE] software ecc based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl35x_nand_read_page_swecc(struct mtd_info *mtd,
struct nand_chip *chip,
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 *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
chip->ecc.read_page_raw(mtd, chip, buf, page, 1);
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++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
eccsteps = chip->ecc.steps;
p = buf;
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
/**
* pl35x_nand_select_chip - Select the flash device
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
*
* This function is empty as the NAND controller handles chip select line
* internally based on the chip address passed in command and data phase.
*/
static void pl35x_nand_select_chip(struct mtd_info *mtd, int chip)
{
return;
}
/**
* pl35x_nand_cmd_function - Send command to NAND device
* @mtd: Pointer to the mtd_info structure
* @command: The command to be sent to the flash device
* @column: The column address for this command, -1 if none
* @page_addr: The page address for this command, -1 if none
*/
static void pl35x_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
if (command == NAND_CMD_READ0) pr_debug("NAND READ\n");
if (command == NAND_CMD_UNLOCK1) pr_debug("NAND UNLOCK1\n");
if (command == NAND_CMD_SET_FEATURES) pr_debug("NAND NAND_CMD_SET_FEATURES\n");
struct nand_chip *chip = mtd->priv;
const struct pl35x_nand_command_format *curr_cmd = NULL;
struct pl35x_nand_info *xnand =
container_of(mtd, struct pl35x_nand_info, mtd);
void __iomem *cmd_addr;
unsigned long cmd_data = 0, end_cmd_valid = 0;
unsigned long cmd_phase_addr, data_phase_addr, end_cmd, i;
unsigned long timeout = jiffies + PL35X_NAND_DEV_BUSY_TIMEOUT;
u32 addrcycles;
if (xnand->end_cmd_pending) {
/*
* Check for end command if this command request is same as the
* pending command then return
*/
if (xnand->end_cmd == command) {
xnand->end_cmd = 0;
xnand->end_cmd_pending = 0;
return;
}
}
/* Emulate NAND_CMD_READOOB for large page device */
if ((mtd->writesize > PL35X_NAND_ECC_SIZE) &&
(command == NAND_CMD_READOOB)) {
column += mtd->writesize;
command = NAND_CMD_READ0;
}
/* Get the command format */
for (i = 0; (pl35x_nand_commands[i].start_cmd != NAND_CMD_NONE ||
pl35x_nand_commands[i].end_cmd != NAND_CMD_NONE); i++)
if (command == pl35x_nand_commands[i].start_cmd)
curr_cmd = &pl35x_nand_commands[i];
if (curr_cmd == NULL)
return;
/* Clear interrupt */
pl35x_smc_clr_nand_int();
/* Get the command phase address */
if (curr_cmd->end_cmd_valid == PL35X_NAND_CMD_PHASE)
end_cmd_valid = 1;
if (curr_cmd->end_cmd == NAND_CMD_NONE)
end_cmd = 0x0;
else
end_cmd = curr_cmd->end_cmd;
if (command == NAND_CMD_READ0 || command == NAND_CMD_SEQIN)
addrcycles = xnand->row_addr_cycles + xnand->col_addr_cycles;
else if (command == NAND_CMD_ERASE1)
addrcycles = xnand->row_addr_cycles;
else
addrcycles = curr_cmd->addr_cycles;
cmd_phase_addr = (unsigned long __force)xnand->nand_base |
(addrcycles << ADDR_CYCLES_SHIFT) |
(end_cmd_valid << END_CMD_VALID_SHIFT) |
(COMMAND_PHASE) |
(end_cmd << END_CMD_SHIFT) |
(curr_cmd->start_cmd << START_CMD_SHIFT);
cmd_addr = (void __iomem * __force)cmd_phase_addr;
/* Get the data phase address */
end_cmd_valid = 0;
data_phase_addr = (unsigned long __force)xnand->nand_base |
(0x0 << CLEAR_CS_SHIFT) |
(end_cmd_valid << END_CMD_VALID_SHIFT) |
(DATA_PHASE) |
(end_cmd << END_CMD_SHIFT) |
(0x0 << ECC_LAST_SHIFT);
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->IO_ADDR_W = chip->IO_ADDR_R;
/* Command phase AXI write */
/* Read & Write */
if (column != -1 && page_addr != -1) {
/* Adjust columns for 16 bit bus width */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
cmd_data = column;
if (mtd->writesize > PL35X_NAND_ECC_SIZE) {
cmd_data |= page_addr << 16;
/* Another address cycle for devices > 128MiB */
if (chip->chipsize > (128 << 20)) {
pl35x_nand_write32(cmd_addr, cmd_data);
cmd_data = (page_addr >> 16);
}
} else {
cmd_data |= page_addr << 8;
}
} else if (page_addr != -1) {
/* Erase */
cmd_data = page_addr;
} else if (column != -1) {
/*
* Change read/write column, read id etc
* Adjust columns for 16 bit bus width
*/
if ((chip->options & NAND_BUSWIDTH_16) &&
((command == NAND_CMD_READ0) ||
(command == NAND_CMD_SEQIN) ||
(command == NAND_CMD_RNDOUT) ||
(command == NAND_CMD_RNDIN)))
column >>= 1;
cmd_data = column;
}
pl35x_nand_write32(cmd_addr, cmd_data);
if (curr_cmd->end_cmd_valid) {
xnand->end_cmd = curr_cmd->end_cmd;
xnand->end_cmd_pending = 1;
}
ndelay(100);
if ((command == NAND_CMD_READ0) ||
(command == NAND_CMD_RESET) ||
(command == NAND_CMD_PARAM) ||
(command == NAND_CMD_GET_FEATURES)) {
/* Wait till the device is ready or timeout */
do {
if (chip->dev_ready(mtd))
break;
else
cpu_relax();
} while (!time_after_eq(jiffies, timeout));
if (time_after_eq(jiffies, timeout))
pr_err("%s timed out\n", __func__);
return;
}
}
/**
* pl35x_nand_read_buf - read chip data into buffer
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to read
*/
static void pl35x_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;
unsigned long *ptr = (unsigned long *)buf;
len >>= 2;
for (i = 0; i < len; i++)
ptr[i] = readl(chip->IO_ADDR_R);
}
/**
* pl35x_nand_write_buf - write buffer to chip
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to write
*/
static void pl35x_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
int i;
struct nand_chip *chip = mtd->priv;
unsigned long *ptr = (unsigned long *)buf;
pr_debug("pl35x_nand_write_buf: datasize=%d len=%d len>>2=%d\n",sizeof(ptr[0]),len,len>>2);
len >>= 2;
for (i = 0; i < len; i++)
writel(ptr[i], chip->IO_ADDR_W);
//writeb(byte, chip->IO_ADDR_W);
}
/**
* pl35x_nand_device_ready - Check device ready/busy line
* @mtd: Pointer to the mtd_info structure
*
* Return: 0 on busy or 1 on ready state
*/
static int pl35x_nand_device_ready(struct mtd_info *mtd)
{
if (pl35x_smc_get_nand_int_status_raw()) {
pl35x_smc_clr_nand_int();
return 1;
}
return 0;
}
/**
* pl35x_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 pl35x_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
int status;
int i;
uint8_t ondie_ecc_feature;
if (!chip->onfi_version ||
!(le16_to_cpu(chip->onfi_params.opt_cmd)
& ONFI_OPT_CMD_SET_GET_FEATURES))
return -EINVAL;
if (addr==ONDIE_ECC_FEATURE_ADDR){
//keep ondie ecc on;
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
ondie_ecc_feature = readb(chip->IO_ADDR_R);
subfeature_param[0] |= (ondie_ecc_feature&0x08);
}
chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
writeb(subfeature_param[i], chip->IO_ADDR_W);
//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 pl35x_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;
}
/**
* pl35x_nand_detect_ondie_ecc - Get the flash ondie ecc state
* @mtd: Pointer to the mtd_info structure
*
* This function enables the ondie ecc for the Micron ondie ecc capable devices
*
* Return: 1 on detect, 0 if fail to detect
*/
static int pl35x_nand_detect_ondie_ecc(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
u8 maf_id, dev_id, i, get_feature;
u8 set_feature[4] = { 0x08, 0x00, 0x00, 0x00 };
/* Check if On-Die ECC flash */
nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
maf_id = readb(nand_chip->IO_ADDR_R);
dev_id = readb(nand_chip->IO_ADDR_R);
if ((maf_id == NAND_MFR_MICRON) &&
((dev_id == 0xf1) || (dev_id == 0xa1) ||
(dev_id == 0xb1) || (dev_id == 0xaa) ||
(dev_id == 0xba) || (dev_id == 0xda) ||
(dev_id == 0xca) || (dev_id == 0xac) ||
(dev_id == 0xbc) || (dev_id == 0xdc) ||
(dev_id == 0xcc) || (dev_id == 0xa3) ||
(dev_id == 0xb3) ||
(dev_id == 0xd3) || (dev_id == 0xc3))) {
nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
get_feature = readb(nand_chip->IO_ADDR_R);
if (get_feature & 0x08) {
return 1;
} else {
nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
for (i = 0; i < 4; i++)
writeb(set_feature[i], nand_chip->IO_ADDR_W);
ndelay(1000);
nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
get_feature = readb(nand_chip->IO_ADDR_R);
if (get_feature & 0x08)
return 1;
}
}
return 0;
}
/**
* pl35x_nand_ecc_init - Initialize the ecc information as per the ecc mode
* @mtd: Pointer to the mtd_info structure
* @ondie_ecc_state: ondie ecc status
*
* This function initializes the ecc block and functional pointers as per the
* ecc mode
*/
static void pl35x_nand_ecc_init(struct mtd_info *mtd, int ondie_ecc_state)
{
struct nand_chip *nand_chip = mtd->priv;
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.read_oob = pl35x_nand_read_oob;
nand_chip->ecc.read_page_raw = pl35x_nand_read_page_raw;
nand_chip->ecc.strength = 1;
nand_chip->ecc.write_oob = pl35x_nand_write_oob;
nand_chip->ecc.write_page_raw = pl35x_nand_write_page_raw;
if (ondie_ecc_state) {
/* bypass the controller ECC block */
pl35x_smc_set_ecc_mode(PL35X_SMC_ECCMODE_BYPASS);
/*
* The software ECC routines won't work with the
* SMC controller
*/
nand_chip->ecc.bytes = 0;
nand_chip->ecc.layout = &ondie_nand_oob_64;
nand_chip->ecc.read_page = pl35x_nand_read_page_raw;
nand_chip->ecc.write_page = pl35x_nand_write_page_raw;
nand_chip->ecc.size = mtd->writesize;
/*
* On-Die ECC spare bytes offset 8 is used for ECC codes
* Use the BBT pattern descriptors
*/
nand_chip->bbt_td = &bbt_main_descr;
nand_chip->bbt_md = &bbt_mirror_descr;
} else {
/* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
nand_chip->ecc.bytes = 3;
nand_chip->ecc.calculate = pl35x_nand_calculate_hwecc;
nand_chip->ecc.correct = pl35x_nand_correct_data;
nand_chip->ecc.hwctl = NULL;
nand_chip->ecc.read_page = pl35x_nand_read_page_hwecc;
nand_chip->ecc.size = PL35X_NAND_ECC_SIZE;
nand_chip->ecc.write_page = pl35x_nand_write_page_hwecc;
pl35x_smc_set_ecc_pg_size(mtd->writesize);
switch (mtd->writesize) {
case 512:
case 1024:
case 2048:
pl35x_smc_set_ecc_mode(PL35X_SMC_ECCMODE_APB);
break;
default:
/*
* The software ECC routines won't work with the
* SMC controller
*/
nand_chip->ecc.calculate = nand_calculate_ecc;
nand_chip->ecc.correct = nand_correct_data;
nand_chip->ecc.read_page = pl35x_nand_read_page_swecc;
nand_chip->ecc.write_page = pl35x_nand_write_page_swecc;
nand_chip->ecc.size = 256;
break;
}
if (mtd->oobsize == 16)
nand_chip->ecc.layout = &nand_oob_16;
else if (mtd->oobsize == 64)
nand_chip->ecc.layout = &nand_oob_64;
}
}
/**
* pl35x_nand_probe - Probe method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function initializes the driver data structures and the hardware.
*
* Return: 0 on success or error value on failure
*/
static int pl35x_nand_probe(struct platform_device *pdev)
{
struct pl35x_nand_info *xnand;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct resource *res;
struct mtd_part_parser_data ppdata;
int ondie_ecc_state;
xnand = devm_kzalloc(&pdev->dev, sizeof(*xnand), GFP_KERNEL);
if (!xnand)
return -ENOMEM;
/* Map physical address of NAND flash */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
xnand->nand_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(xnand->nand_base))
return PTR_ERR(xnand->nand_base);
/* Link the private data with the MTD structure */
mtd = &xnand->mtd;
nand_chip = &xnand->chip;
nand_chip->priv = xnand;
mtd->priv = nand_chip;
mtd->owner = THIS_MODULE;
mtd->name = PL35X_NAND_DRIVER_NAME;
/* Set address of NAND IO lines */
nand_chip->IO_ADDR_R = xnand->nand_base;
nand_chip->IO_ADDR_W = xnand->nand_base;
/* Set the driver entry points for MTD */
nand_chip->cmdfunc = pl35x_nand_cmd_function;
nand_chip->dev_ready = pl35x_nand_device_ready;
nand_chip->select_chip = pl35x_nand_select_chip;
nand_chip->onfi_set_features = pl35x_nand_onfi_set_features;
nand_chip->onfi_get_features = pl35x_nand_onfi_get_features;
/* If we don't set this delay driver sets 20us by default */
nand_chip->chip_delay = 30;
/* Buffer read/write routines */
nand_chip->read_buf = pl35x_nand_read_buf;
nand_chip->write_buf = pl35x_nand_write_buf;
/* Set the device option and flash width */
nand_chip->options = NAND_BUSWIDTH_AUTO;
nand_chip->bbt_options = NAND_BBT_USE_FLASH;
platform_set_drvdata(pdev, xnand);
ondie_ecc_state = pl35x_nand_detect_ondie_ecc(mtd);
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
return -ENXIO;
}
xnand->row_addr_cycles = nand_chip->onfi_params.addr_cycles & 0xF;
xnand->col_addr_cycles =
(nand_chip->onfi_params.addr_cycles >> 4) & 0xF;
pl35x_nand_ecc_init(mtd, ondie_ecc_state);
if (nand_chip->options & NAND_BUSWIDTH_16)
pl35x_smc_set_buswidth(PL35X_SMC_MEM_WIDTH_16);
/* second phase scan */
if (nand_scan_tail(mtd)) {
dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
return -ENXIO;
}
//elphel393 modification for Micron NAND chips
//TODO: add Micron chip ID checking
mtd->_unlock = nand_unlock;
mtd->_lock = nand_lock;
ppdata.of_node = pdev->dev.of_node;
mtd_device_parse_register(&xnand->mtd, NULL, &ppdata, NULL, 0);
return 0;
}
/**
* pl35x_nand_remove - Remove method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function is called if the driver module is being unloaded. It frees all
* resources allocated to the device.
*
* Return: 0 on success or error value on failure
*/
static int pl35x_nand_remove(struct platform_device *pdev)
{
struct pl35x_nand_info *xnand = platform_get_drvdata(pdev);
/* Release resources, unregister device */
nand_release(&xnand->mtd);
/* kfree(NULL) is safe */
kfree(xnand->parts);
return 0;
}
/* Match table for device tree binding */
static const struct of_device_id pl35x_nand_of_match[] = {
{ .compatible = "arm,pl353-nand-r2p1" },
{},
};
MODULE_DEVICE_TABLE(of, pl35x_nand_of_match);
/*
* pl35x_nand_driver - This structure defines the NAND subsystem platform driver
*/
static struct platform_driver pl35x_nand_driver = {
.probe = pl35x_nand_probe,
.remove = pl35x_nand_remove,
.driver = {
.name = PL35X_NAND_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = pl35x_nand_of_match,
},
};
module_platform_driver(pl35x_nand_driver);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_ALIAS("platform:" PL35X_NAND_DRIVER_NAME);
MODULE_DESCRIPTION("ARM PL35X NAND Flash Driver");
MODULE_LICENSE("GPL");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment