Merge tag 'mtd/for-5.2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull MTD updates from Richard Weinberger: "MTD core changes: - New AFS partition parser - Update MAINTAINERS entry - Use of fall-throughs markers NAND core changes: - Support having the bad block markers in either the first, second or last page of a block. The combination of all three location is now possible. - Constification of NAND_OP_PARSER(_PATTERN) elements. - Generic NAND DT bindings changed to yaml format (can be used to check the proposed bindings. First platform to be fully supported: sunxi. - Stopped using several legacy hooks. - Preparation to use the generic NAND layer with the addition of several helpers and the removal of the struct nand_chip from generic functions. - Kconfig cleanup to prepare the introduction of external ECC engines support. - Fallthrough comments. - Introduction of the SPI-mem dirmap API for SPI-NAND devices. Raw NAND controller drivers changes: - nandsim: - Switch to ->exec-op(). - meson: - Misc cleanups and fixes. - New OOB layout. - Sunxi: - A23/A33 NAND DMA support. - Ingenic: - Full reorganization and cleanup. - Clear separation between NAND controller and ECC engine. - Support JZ4740 an JZ4725B. - Denali: - Clear controller/chip separation. - ->exec_op() migration. - Various cleanups. - fsl_elbc: - Enable software ECC support. - Atmel: - Sam9x60 support. - GPMI: - Introduce the GPMI_IS_MXS() macro. - Various trivial/spelling/coding style fixes. SPI NOR core changes: - Print all JEDEC ID bytes on error - Fix comment of spi_nor_find_best_erase_type() - Add region locking flags for s25fl512s SPI NOR controller drivers changes: - intel-spi: - Avoid crossing 4K address boundary on read/write - Add support for Intel Comet Lake SPI serial flash" * tag 'mtd/for-5.2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (120 commits) mtd: part: fix incorrect format specifier for an unsigned long long mtd: lpddr_cmds: Mark expected switch fall-through mtd: phram: Mark expected switch fall-throughs mtd: cfi_cmdset_0002: Mark expected switch fall-throughs mtd: cfi_util: mark expected switch fall-throughs MAINTAINERS: MTD Git repository is hosted on kernel.org MAINTAINERS: Update jffs2 entry mtd: afs: add v2 partition parsing mtd: afs: factor the IIS read into partition parser mtd: afs: factor footer parsing into the v1 part parsing mtd: factor out v1 partition parsing mtd: afs: simplify partition detection mtd: afs: simplify partition parsing mtd: partitions: Add OF support to AFS partitions mtd: partitions: Add AFS partitions DT bindings mtd: afs: Move AFS partition parser to parsers subdir mtd: maps: Make uclinux_ram_map static mtd: maps: Allow MTD_PHYSMAP with MTD_RAM MAINTAINERS: Add myself as MTD maintainer MAINTAINERS: Remove my name from the MTD and NAND entries ...
This commit is contained in:
@@ -2,6 +2,5 @@ config MTD_NAND_CORE
|
||||
tristate
|
||||
|
||||
source "drivers/mtd/nand/onenand/Kconfig"
|
||||
|
||||
source "drivers/mtd/nand/raw/Kconfig"
|
||||
source "drivers/mtd/nand/spi/Kconfig"
|
||||
|
@@ -173,6 +173,40 @@ int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nanddev_mtd_erase);
|
||||
|
||||
/**
|
||||
* nanddev_mtd_max_bad_blocks() - Get the maximum number of bad eraseblock on
|
||||
* a specific region of the NAND device
|
||||
* @mtd: MTD device
|
||||
* @offs: offset of the NAND region
|
||||
* @len: length of the NAND region
|
||||
*
|
||||
* Default implementation for mtd->_max_bad_blocks(). Only works if
|
||||
* nand->memorg.max_bad_eraseblocks_per_lun is > 0.
|
||||
*
|
||||
* Return: a positive number encoding the maximum number of eraseblocks on a
|
||||
* portion of memory, a negative error code otherwise.
|
||||
*/
|
||||
int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len)
|
||||
{
|
||||
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
struct nand_pos pos, end;
|
||||
unsigned int max_bb = 0;
|
||||
|
||||
if (!nand->memorg.max_bad_eraseblocks_per_lun)
|
||||
return -ENOTSUPP;
|
||||
|
||||
nanddev_offs_to_pos(nand, offs, &pos);
|
||||
nanddev_offs_to_pos(nand, offs + len, &end);
|
||||
|
||||
for (nanddev_offs_to_pos(nand, offs, &pos);
|
||||
nanddev_pos_cmp(&pos, &end) < 0;
|
||||
nanddev_pos_next_lun(nand, &pos))
|
||||
max_bb += nand->memorg.max_bad_eraseblocks_per_lun;
|
||||
|
||||
return max_bb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks);
|
||||
|
||||
/**
|
||||
* nanddev_init() - Initialize a NAND device
|
||||
* @nand: NAND device
|
||||
|
@@ -2458,7 +2458,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
||||
|
||||
/* We write two bytes, so we don't have to mess with 16-bit access */
|
||||
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
|
||||
ofs += mtd->oobsize + (this->badblockpos & ~0x01);
|
||||
/* FIXME : What to do when marking SLC block in partition
|
||||
* with MLC erasesize? For now, it is not advisable to
|
||||
* create partitions containing both SLC and MLC regions.
|
||||
@@ -3967,6 +3967,9 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
||||
if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING))
|
||||
this->unlock_all(mtd);
|
||||
|
||||
/* Set the bad block marker position */
|
||||
this->badblockpos = ONENAND_BADBLOCK_POS;
|
||||
|
||||
ret = this->scan_bbt(mtd);
|
||||
if ((!FLEXONENAND(this)) || ret)
|
||||
return ret;
|
||||
|
@@ -190,9 +190,6 @@ static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
if (!bbm->bbt)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set the bad block position */
|
||||
bbm->badblockpos = ONENAND_BADBLOCK_POS;
|
||||
|
||||
/* Set erase shift */
|
||||
bbm->bbt_erase_shift = this->erase_shift;
|
||||
|
||||
|
@@ -1,34 +1,29 @@
|
||||
config MTD_NAND_ECC
|
||||
config MTD_NAND_ECC_SW_HAMMING
|
||||
tristate
|
||||
|
||||
config MTD_NAND_ECC_SMC
|
||||
config MTD_NAND_ECC_SW_HAMMING_SMC
|
||||
bool "NAND ECC Smart Media byte order"
|
||||
depends on MTD_NAND_ECC
|
||||
depends on MTD_NAND_ECC_SW_HAMMING
|
||||
default n
|
||||
help
|
||||
Software ECC according to the Smart Media Specification.
|
||||
The original Linux implementation had byte 0 and 1 swapped.
|
||||
|
||||
|
||||
menuconfig MTD_NAND
|
||||
menuconfig MTD_RAW_NAND
|
||||
tristate "Raw/Parallel NAND Device Support"
|
||||
depends on MTD
|
||||
select MTD_NAND_ECC
|
||||
select MTD_NAND_CORE
|
||||
select MTD_NAND_ECC_SW_HAMMING
|
||||
help
|
||||
This enables support for accessing all type of raw/parallel
|
||||
NAND flash devices. For further information see
|
||||
<http://www.linux-mtd.infradead.org/doc/nand.html>.
|
||||
|
||||
if MTD_NAND
|
||||
if MTD_RAW_NAND
|
||||
|
||||
config MTD_NAND_BCH
|
||||
tristate
|
||||
select BCH
|
||||
depends on MTD_NAND_ECC_BCH
|
||||
default MTD_NAND
|
||||
|
||||
config MTD_NAND_ECC_BCH
|
||||
config MTD_NAND_ECC_SW_BCH
|
||||
bool "Support software BCH ECC"
|
||||
select BCH
|
||||
default n
|
||||
help
|
||||
This enables support for software BCH error correction. Binary BCH
|
||||
@@ -36,15 +31,13 @@ config MTD_NAND_ECC_BCH
|
||||
ECC codes. They are used with NAND devices requiring more than 1 bit
|
||||
of error correction.
|
||||
|
||||
config MTD_SM_COMMON
|
||||
tristate
|
||||
default n
|
||||
comment "Raw/parallel NAND flash controllers"
|
||||
|
||||
config MTD_NAND_DENALI
|
||||
tristate
|
||||
|
||||
config MTD_NAND_DENALI_PCI
|
||||
tristate "Support Denali NAND controller on Intel Moorestown"
|
||||
tristate "Denali NAND controller on Intel Moorestown"
|
||||
select MTD_NAND_DENALI
|
||||
depends on PCI
|
||||
help
|
||||
@@ -52,31 +45,22 @@ config MTD_NAND_DENALI_PCI
|
||||
Denali NAND controller core.
|
||||
|
||||
config MTD_NAND_DENALI_DT
|
||||
tristate "Support Denali NAND controller as a DT device"
|
||||
tristate "Denali NAND controller as a DT device"
|
||||
select MTD_NAND_DENALI
|
||||
depends on HAS_DMA && HAVE_CLK && OF
|
||||
help
|
||||
Enable the driver for NAND flash on platforms using a Denali NAND
|
||||
controller as a DT device.
|
||||
|
||||
config MTD_NAND_GPIO
|
||||
tristate "GPIO assisted NAND Flash driver"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables a NAND flash driver where control signals are
|
||||
connected to GPIO pins, and commands and data are communicated
|
||||
via a memory mapped interface.
|
||||
|
||||
config MTD_NAND_AMS_DELTA
|
||||
tristate "NAND Flash device on Amstrad E3"
|
||||
tristate "Amstrad E3 NAND controller"
|
||||
depends on MACH_AMS_DELTA || COMPILE_TEST
|
||||
default y
|
||||
help
|
||||
Support for NAND flash on Amstrad E3 (Delta).
|
||||
|
||||
config MTD_NAND_OMAP2
|
||||
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
|
||||
tristate "OMAP2, OMAP3, OMAP4 and Keystone NAND controller"
|
||||
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
@@ -98,18 +82,6 @@ config MTD_NAND_OMAP_BCH
|
||||
config MTD_NAND_OMAP_BCH_BUILD
|
||||
def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
|
||||
|
||||
config MTD_NAND_RICOH
|
||||
tristate "Ricoh xD card reader"
|
||||
default n
|
||||
depends on PCI
|
||||
select MTD_SM_COMMON
|
||||
help
|
||||
Enable support for Ricoh R5C852 xD card reader
|
||||
You also need to enable ether
|
||||
NAND SSFDC (SmartMedia) read only translation layer' or new
|
||||
expermental, readwrite
|
||||
'SmartMedia/xD new translation layer'
|
||||
|
||||
config MTD_NAND_AU1550
|
||||
tristate "Au1550/1200 NAND support"
|
||||
depends on MIPS_ALCHEMY
|
||||
@@ -117,8 +89,15 @@ config MTD_NAND_AU1550
|
||||
This enables the driver for the NAND flash controller on the
|
||||
AMD/Alchemy 1550 SOC.
|
||||
|
||||
config MTD_NAND_NDFC
|
||||
tristate "IBM/MCC 4xx NAND controller"
|
||||
depends on 4xx
|
||||
select MTD_NAND_ECC_SW_HAMMING_SMC
|
||||
help
|
||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
|
||||
config MTD_NAND_S3C2410
|
||||
tristate "NAND Flash support for Samsung S3C SoCs"
|
||||
tristate "Samsung S3C NAND controller"
|
||||
depends on ARCH_S3C24XX || ARCH_S3C64XX
|
||||
help
|
||||
This enables the NAND flash controller on the S3C24xx and S3C64xx
|
||||
@@ -128,18 +107,11 @@ config MTD_NAND_S3C2410
|
||||
must advertise a platform_device for the driver to attach.
|
||||
|
||||
config MTD_NAND_S3C2410_DEBUG
|
||||
bool "Samsung S3C NAND driver debug"
|
||||
bool "Samsung S3C NAND controller debug"
|
||||
depends on MTD_NAND_S3C2410
|
||||
help
|
||||
Enable debugging of the S3C NAND driver
|
||||
|
||||
config MTD_NAND_NDFC
|
||||
tristate "NDFC NanD Flash Controller"
|
||||
depends on 4xx
|
||||
select MTD_NAND_ECC_SMC
|
||||
help
|
||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
|
||||
config MTD_NAND_S3C2410_CLKSTOP
|
||||
bool "Samsung S3C NAND IDLE clock stop"
|
||||
depends on MTD_NAND_S3C2410
|
||||
@@ -151,12 +123,358 @@ config MTD_NAND_S3C2410_CLKSTOP
|
||||
approximately 5mA of power when there is nothing happening.
|
||||
|
||||
config MTD_NAND_TANGO
|
||||
tristate "NAND Flash support for Tango chips"
|
||||
tristate "Tango NAND controller"
|
||||
depends on ARCH_TANGO || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables the NAND Flash controller on Tango chips.
|
||||
|
||||
config MTD_NAND_SHARPSL
|
||||
tristate "Sharp SL Series (C7xx + others) NAND controller"
|
||||
depends on ARCH_PXA || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
|
||||
config MTD_NAND_CAFE
|
||||
tristate "OLPC CAFÉ NAND controller"
|
||||
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 "CS5535/CS5536 (AMD Geode companion) NAND controller"
|
||||
depends on X86_32
|
||||
depends on !UML && HAS_IOMEM
|
||||
help
|
||||
The CS553x companion chips for the AMD Geode processor
|
||||
include NAND flash controllers with built-in hardware ECC
|
||||
capabilities; enabling this option will allow you to use
|
||||
these. The driver will check the MSRs to verify that the
|
||||
controller is enabled for NAND, and currently requires that
|
||||
the controller be in MMIO mode.
|
||||
|
||||
If you say "m", the module will be called cs553x_nand.
|
||||
|
||||
config MTD_NAND_ATMEL
|
||||
tristate "Atmel AT91 NAND Flash/SmartMedia NAND controller"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select GENERIC_ALLOCATOR
|
||||
select MFD_ATMEL_SMC
|
||||
help
|
||||
Enables support for NAND Flash / Smart Media Card interface
|
||||
on Atmel AT91 processors.
|
||||
|
||||
config MTD_NAND_ORION
|
||||
tristate "Marvell Orion NAND controller"
|
||||
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_MARVELL
|
||||
tristate "Marvell EBU NAND controller"
|
||||
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
|
||||
COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the NAND flash controller driver for Marvell boards,
|
||||
including:
|
||||
- PXA3xx processors (NFCv1)
|
||||
- 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
|
||||
- 64-bit Aramda platforms (7k, 8k) (NFCv2)
|
||||
|
||||
config MTD_NAND_SLC_LPC32XX
|
||||
tristate "NXP LPC32xx SLC NAND controller"
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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 NAND controller"
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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 "CM-X270 modules NAND controller"
|
||||
depends on MACH_ARMCORE
|
||||
|
||||
config MTD_NAND_PASEMI
|
||||
tristate "PA Semi PWRficient NAND controller"
|
||||
depends on PPC_PASEMI
|
||||
help
|
||||
Enables support for NAND Flash interface on PA Semi PWRficient
|
||||
based boards
|
||||
|
||||
config MTD_NAND_TMIO
|
||||
tristate "Toshiba Mobile IO NAND 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_BRCMNAND
|
||||
tristate "Broadcom STB NAND controller"
|
||||
depends on ARM || ARM64 || MIPS || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables the Broadcom NAND controller driver. The controller was
|
||||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
||||
|
||||
config MTD_NAND_BCM47XXNFLASH
|
||||
tristate "BCM4706 BCMA NAND controller"
|
||||
depends on BCMA_NFLASH
|
||||
depends on BCMA
|
||||
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_OXNAS
|
||||
tristate "Oxford Semiconductor NAND controller"
|
||||
depends on ARCH_OXNAS || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the NAND flash controller on Oxford Semiconductor SoCs.
|
||||
|
||||
config MTD_NAND_MPC5121_NFC
|
||||
tristate "MPC5121 NAND controller"
|
||||
depends on PPC_MPC512x
|
||||
help
|
||||
This enables the driver for the NAND flash controller on the
|
||||
MPC5121 SoC.
|
||||
|
||||
config MTD_NAND_GPMI_NAND
|
||||
tristate "Freescale GPMI NAND controller"
|
||||
depends on MXS_DMA
|
||||
help
|
||||
Enables NAND Flash support for IMX23, IMX28 or IMX6.
|
||||
The GPMI controller is very powerful, with the help of BCH
|
||||
module, it can do the hardware ECC. The GPMI supports several
|
||||
NAND flashs at the same time.
|
||||
|
||||
config MTD_NAND_FSL_ELBC
|
||||
tristate "Freescale eLBC NAND controller"
|
||||
depends on FSL_SOC
|
||||
select FSL_LBC
|
||||
help
|
||||
Various Freescale chips, including the 8313, include a NAND Flash
|
||||
Controller Module with built-in hardware ECC capabilities.
|
||||
Enabling this option will enable you to use this to control
|
||||
external NAND devices.
|
||||
|
||||
config MTD_NAND_FSL_IFC
|
||||
tristate "Freescale IFC NAND controller"
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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 "Freescale UPM NAND controller"
|
||||
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_VF610_NFC
|
||||
tristate "Freescale VF610/MPC5125 NAND controller"
|
||||
depends on (SOC_VF610 || COMPILE_TEST)
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash Controller on some Freescale
|
||||
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
|
||||
The driver supports a maximum 2k page size. With 2k pages and
|
||||
64 bytes or more of OOB, hardware ECC with up to 32-bit error
|
||||
correction is supported. Hardware ECC is only enabled through
|
||||
device tree.
|
||||
|
||||
config MTD_NAND_MXC
|
||||
tristate "Freescale MXC NAND controller"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the driver for the NAND flash controller on the
|
||||
MXC processors.
|
||||
|
||||
config MTD_NAND_SH_FLCTL
|
||||
tristate "Renesas SuperH FLCTL NAND controller"
|
||||
depends on SUPERH || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Several Renesas SuperH CPU has FLCTL. This option enables support
|
||||
for NAND Flash using FLCTL.
|
||||
|
||||
config MTD_NAND_DAVINCI
|
||||
tristate "DaVinci/Keystone NAND controller"
|
||||
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enable the driver for NAND flash chips on Texas Instruments
|
||||
DaVinci/Keystone processors.
|
||||
|
||||
config MTD_NAND_TXX9NDFMC
|
||||
tristate "TXx9 NAND controller"
|
||||
depends on SOC_TX4938 || SOC_TX4939 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the NAND flash controller on the TXx9 SoCs.
|
||||
|
||||
config MTD_NAND_SOCRATES
|
||||
tristate "Socrates NAND controller"
|
||||
depends on SOCRATES
|
||||
help
|
||||
Enables support for NAND Flash chips wired onto Socrates board.
|
||||
|
||||
config MTD_NAND_NUC900
|
||||
tristate "Nuvoton NUC9xx/w90p910 NAND controller"
|
||||
depends on ARCH_W90X900 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the driver for the NAND Flash on evaluation board based
|
||||
on w90p910 / NUC9xx.
|
||||
|
||||
source "drivers/mtd/nand/raw/ingenic/Kconfig"
|
||||
|
||||
config MTD_NAND_FSMC
|
||||
tristate "ST Micros FSMC NAND controller"
|
||||
depends on OF && HAS_IOMEM
|
||||
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 || \
|
||||
COMPILE_TEST
|
||||
help
|
||||
Enables support for NAND Flash chips on the ST Microelectronics
|
||||
Flexible Static Memory Controller (FSMC)
|
||||
|
||||
config MTD_NAND_XWAY
|
||||
bool "Lantiq XWAY NAND controller"
|
||||
depends on LANTIQ && SOC_TYPE_XWAY
|
||||
help
|
||||
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
|
||||
to the External Bus Unit (EBU).
|
||||
|
||||
config MTD_NAND_SUNXI
|
||||
tristate "Allwinner NAND controller"
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash chips on Allwinner SoCs.
|
||||
|
||||
config MTD_NAND_HISI504
|
||||
tristate "Hisilicon Hip04 NAND controller"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND controller on Hisilicon SoC Hip04.
|
||||
|
||||
config MTD_NAND_QCOM
|
||||
tristate "QCOM NAND controller"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
|
||||
controller. This controller is found on IPQ806x SoC.
|
||||
|
||||
config MTD_NAND_MTK
|
||||
tristate "MTK NAND controller"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND controller on MTK SoCs.
|
||||
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
|
||||
|
||||
config MTD_NAND_TEGRA
|
||||
tristate "NVIDIA Tegra NAND controller"
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND flash controller on NVIDIA Tegra SoC.
|
||||
The driver has been developed and tested on a Tegra 2 SoC. DMA
|
||||
support, raw read/write page as well as HW ECC read/write page
|
||||
is supported. Extra OOB bytes when using HW ECC are currently
|
||||
not supported.
|
||||
|
||||
config MTD_NAND_STM32_FMC2
|
||||
tristate "Support for NAND controller on STM32MP SoCs"
|
||||
depends on MACH_STM32MP157 || COMPILE_TEST
|
||||
help
|
||||
Enables support for NAND Flash chips on SoCs containing the FMC2
|
||||
NAND controller. This controller is found on STM32MP SoCs.
|
||||
The controller supports a maximum 8k page size and supports
|
||||
a maximum 8-bit correction error per sector of 512 bytes.
|
||||
|
||||
config MTD_NAND_MESON
|
||||
tristate "Support for NAND controller on Amlogic's Meson SoCs"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enables support for NAND controller on Amlogic's Meson SoCs.
|
||||
This controller is found on Meson SoCs.
|
||||
|
||||
config MTD_NAND_GPIO
|
||||
tristate "GPIO assisted NAND controller"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables a NAND flash driver where control signals are
|
||||
connected to GPIO pins, and commands and data are communicated
|
||||
via a memory mapped interface.
|
||||
|
||||
config MTD_NAND_PLATFORM
|
||||
tristate "Generic NAND controller"
|
||||
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.
|
||||
|
||||
comment "Misc"
|
||||
|
||||
config MTD_SM_COMMON
|
||||
tristate
|
||||
default n
|
||||
|
||||
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_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_DISKONCHIP
|
||||
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
|
||||
depends on HAS_IOMEM
|
||||
@@ -227,335 +545,4 @@ 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_SHARPSL
|
||||
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
|
||||
depends on ARCH_PXA || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
|
||||
config MTD_NAND_CAFE
|
||||
tristate "NAND support for OLPC CAFÉ chip"
|
||||
depends on PCI
|
||||
select REED_SOLOMON
|
||||
select REED_SOLOMON_DEC16
|
||||
help
|
||||
Use NAND flash attached to the CAFÉ chip designed for the OLPC
|
||||
laptop.
|
||||
|
||||
config MTD_NAND_CS553X
|
||||
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
|
||||
depends on X86_32
|
||||
depends on !UML && HAS_IOMEM
|
||||
help
|
||||
The CS553x companion chips for the AMD Geode processor
|
||||
include NAND flash controllers with built-in hardware ECC
|
||||
capabilities; enabling this option will allow you to use
|
||||
these. The driver will check the MSRs to verify that the
|
||||
controller is enabled for NAND, and currently requires that
|
||||
the controller be in MMIO mode.
|
||||
|
||||
If you say "m", the module will be called cs553x_nand.
|
||||
|
||||
config MTD_NAND_ATMEL
|
||||
tristate "Support for NAND Flash / SmartMedia on AT91"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select GENERIC_ALLOCATOR
|
||||
select MFD_ATMEL_SMC
|
||||
help
|
||||
Enables support for NAND Flash / Smart Media Card interface
|
||||
on Atmel AT91 processors.
|
||||
|
||||
config MTD_NAND_MARVELL
|
||||
tristate "NAND controller support on Marvell boards"
|
||||
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
|
||||
COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the NAND flash controller driver for Marvell boards,
|
||||
including:
|
||||
- PXA3xx processors (NFCv1)
|
||||
- 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
|
||||
- 64-bit Aramda platforms (7k, 8k) (NFCv2)
|
||||
|
||||
config MTD_NAND_SLC_LPC32XX
|
||||
tristate "NXP LPC32xx SLC Controller"
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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 MXS_DMA
|
||||
help
|
||||
Enables NAND Flash support for IMX23, IMX28 or IMX6.
|
||||
The GPMI controller is very powerful, with the help of BCH
|
||||
module, it can do the hardware ECC. The GPMI supports several
|
||||
NAND flashs at the same time.
|
||||
|
||||
config MTD_NAND_BRCMNAND
|
||||
tristate "Broadcom STB NAND controller"
|
||||
depends on ARM || ARM64 || MIPS || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables the Broadcom NAND controller driver. The controller was
|
||||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
||||
|
||||
config MTD_NAND_BCM47XXNFLASH
|
||||
tristate "Support for NAND flash on BCM4706 BCMA bus"
|
||||
depends on BCMA_NFLASH
|
||||
depends on BCMA
|
||||
help
|
||||
BCMA bus can have various flash memories attached, they are
|
||||
registered by bcma as platform devices. This enables driver for
|
||||
NAND flash memories. For now only BCM4706 is supported.
|
||||
|
||||
config MTD_NAND_PLATFORM
|
||||
tristate "Support for generic platform NAND driver"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This implements a generic NAND driver for on-SOC platform
|
||||
devices. You will need to provide platform-specific functions
|
||||
via platform_data.
|
||||
|
||||
config MTD_NAND_ORION
|
||||
tristate "NAND Flash support for Marvell Orion SoC"
|
||||
depends on PLAT_ORION
|
||||
help
|
||||
This enables the NAND flash controller on Orion machines.
|
||||
|
||||
No board specific support is done by this driver, each board
|
||||
must advertise a platform_device for the driver to attach.
|
||||
|
||||
config MTD_NAND_OXNAS
|
||||
tristate "NAND Flash support for Oxford Semiconductor SoC"
|
||||
depends on ARCH_OXNAS || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the NAND flash controller on Oxford Semiconductor SoCs.
|
||||
|
||||
config MTD_NAND_FSL_ELBC
|
||||
tristate "NAND support for Freescale eLBC controllers"
|
||||
depends on FSL_SOC
|
||||
select FSL_LBC
|
||||
help
|
||||
Various Freescale chips, including the 8313, include a NAND Flash
|
||||
Controller Module with built-in hardware ECC capabilities.
|
||||
Enabling this option will enable you to use this to control
|
||||
external NAND devices.
|
||||
|
||||
config MTD_NAND_FSL_IFC
|
||||
tristate "NAND support for Freescale IFC controller"
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select FSL_IFC
|
||||
select MEMORY
|
||||
help
|
||||
Various Freescale chips e.g P1010, include a NAND Flash machine
|
||||
with built-in hardware ECC capabilities.
|
||||
Enabling this option will enable you to use this to control
|
||||
external NAND devices.
|
||||
|
||||
config MTD_NAND_FSL_UPM
|
||||
tristate "Support for NAND on Freescale UPM"
|
||||
depends on PPC_83xx || PPC_85xx
|
||||
select FSL_LBC
|
||||
help
|
||||
Enables support for NAND Flash chips wired onto Freescale PowerPC
|
||||
processor localbus with User-Programmable Machine support.
|
||||
|
||||
config MTD_NAND_MPC5121_NFC
|
||||
tristate "MPC5121 built-in NAND Flash Controller support"
|
||||
depends on PPC_MPC512x
|
||||
help
|
||||
This enables the driver for the NAND flash controller on the
|
||||
MPC5121 SoC.
|
||||
|
||||
config MTD_NAND_VF610_NFC
|
||||
tristate "Support for Freescale NFC for VF610/MPC5125"
|
||||
depends on (SOC_VF610 || COMPILE_TEST)
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash Controller on some Freescale
|
||||
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
|
||||
The driver supports a maximum 2k page size. With 2k pages and
|
||||
64 bytes or more of OOB, hardware ECC with up to 32-bit error
|
||||
correction is supported. Hardware ECC is only enabled through
|
||||
device tree.
|
||||
|
||||
config MTD_NAND_MXC
|
||||
tristate "MXC NAND support"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the driver for the NAND flash controller on the
|
||||
MXC processors.
|
||||
|
||||
config MTD_NAND_SH_FLCTL
|
||||
tristate "Support for NAND on Renesas SuperH FLCTL"
|
||||
depends on SUPERH || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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) || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
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 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the driver for the NAND Flash on evaluation board based
|
||||
on w90p910 / NUC9xx.
|
||||
|
||||
config MTD_NAND_JZ4740
|
||||
tristate "Support for JZ4740 SoC NAND controller"
|
||||
depends on MACH_JZ4740 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash on JZ4740 SoC based boards.
|
||||
|
||||
config MTD_NAND_JZ4780
|
||||
tristate "Support for NAND on JZ4780 SoC"
|
||||
depends on JZ4780_NEMC
|
||||
help
|
||||
Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
|
||||
based boards, using the BCH controller for hardware error correction.
|
||||
|
||||
config MTD_NAND_FSMC
|
||||
tristate "Support for NAND on ST Micros FSMC"
|
||||
depends on OF && HAS_IOMEM
|
||||
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 || \
|
||||
COMPILE_TEST
|
||||
help
|
||||
Enables support for NAND Flash chips on the ST Microelectronics
|
||||
Flexible Static Memory Controller (FSMC)
|
||||
|
||||
config MTD_NAND_XWAY
|
||||
bool "Support for NAND on Lantiq XWAY SoC"
|
||||
depends on LANTIQ && SOC_TYPE_XWAY
|
||||
help
|
||||
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
|
||||
to the External Bus Unit (EBU).
|
||||
|
||||
config MTD_NAND_SUNXI
|
||||
tristate "Support for NAND on Allwinner SoCs"
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash chips on Allwinner SoCs.
|
||||
|
||||
config MTD_NAND_HISI504
|
||||
tristate "Support for NAND controller on Hisilicon SoC Hip04"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND controller on Hisilicon SoC Hip04.
|
||||
|
||||
config MTD_NAND_QCOM
|
||||
tristate "Support for NAND on QCOM SoCs"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
|
||||
controller. This controller is found on IPQ806x SoC.
|
||||
|
||||
config MTD_NAND_MTK
|
||||
tristate "Support for NAND controller on MTK SoCs"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND controller on MTK SoCs.
|
||||
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
|
||||
|
||||
config MTD_NAND_TEGRA
|
||||
tristate "Support for NAND controller on NVIDIA Tegra"
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND flash controller on NVIDIA Tegra SoC.
|
||||
The driver has been developed and tested on a Tegra 2 SoC. DMA
|
||||
support, raw read/write page as well as HW ECC read/write page
|
||||
is supported. Extra OOB bytes when using HW ECC are currently
|
||||
not supported.
|
||||
|
||||
config MTD_NAND_STM32_FMC2
|
||||
tristate "Support for NAND controller on STM32MP SoCs"
|
||||
depends on MACH_STM32MP157 || COMPILE_TEST
|
||||
help
|
||||
Enables support for NAND Flash chips on SoCs containing the FMC2
|
||||
NAND controller. This controller is found on STM32MP SoCs.
|
||||
The controller supports a maximum 8k page size and supports
|
||||
a maximum 8-bit correction error per sector of 512 bytes.
|
||||
|
||||
config MTD_NAND_MESON
|
||||
tristate "Support for NAND controller on Amlogic's Meson SoCs"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enables support for NAND controller on Amlogic's Meson SoCs.
|
||||
This controller is found on Meson SoCs.
|
||||
|
||||
endif # MTD_NAND
|
||||
endif # MTD_RAW_NAND
|
||||
|
@@ -1,8 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_MTD_NAND) += nand.o
|
||||
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
|
||||
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
|
||||
obj-$(CONFIG_MTD_RAW_NAND) += nand.o
|
||||
obj-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += nand_ecc.o
|
||||
nand-$(CONFIG_MTD_NAND_ECC_SW_BCH) += nand_bch.o
|
||||
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
|
||||
|
||||
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
|
||||
@@ -45,8 +45,7 @@ obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
|
||||
obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
|
||||
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
|
||||
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
|
||||
obj-y += ingenic/
|
||||
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
|
||||
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2017 ATMEL
|
||||
* Copyright 2017 Free Electrons
|
||||
@@ -29,10 +30,6 @@
|
||||
* Add Nand Flash Controller support for SAMA5 SoC
|
||||
* Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* A few words about the naming convention in this file. This convention
|
||||
* applies to structure and function names.
|
||||
*
|
||||
@@ -65,6 +62,7 @@
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <soc/at91/atmel-sfr.h>
|
||||
|
||||
#include "pmecc.h"
|
||||
|
||||
@@ -211,6 +209,7 @@ struct atmel_nand_controller_caps {
|
||||
bool legacy_of_bindings;
|
||||
u32 ale_offs;
|
||||
u32 cle_offs;
|
||||
const char *ebi_csa_regmap_name;
|
||||
const struct atmel_nand_controller_ops *ops;
|
||||
};
|
||||
|
||||
@@ -231,10 +230,15 @@ to_nand_controller(struct nand_controller *ctl)
|
||||
return container_of(ctl, struct atmel_nand_controller, base);
|
||||
}
|
||||
|
||||
struct atmel_smc_nand_ebi_csa_cfg {
|
||||
u32 offs;
|
||||
u32 nfd0_on_d16;
|
||||
};
|
||||
|
||||
struct atmel_smc_nand_controller {
|
||||
struct atmel_nand_controller base;
|
||||
struct regmap *matrix;
|
||||
unsigned int ebi_csa_offs;
|
||||
struct regmap *ebi_csa_regmap;
|
||||
struct atmel_smc_nand_ebi_csa_cfg *ebi_csa;
|
||||
};
|
||||
|
||||
static inline struct atmel_smc_nand_controller *
|
||||
@@ -1068,15 +1072,15 @@ static int atmel_nand_pmecc_init(struct nand_chip *chip)
|
||||
req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
|
||||
else if (chip->ecc.strength)
|
||||
req.ecc.strength = chip->ecc.strength;
|
||||
else if (chip->ecc_strength_ds)
|
||||
req.ecc.strength = chip->ecc_strength_ds;
|
||||
else if (chip->base.eccreq.strength)
|
||||
req.ecc.strength = chip->base.eccreq.strength;
|
||||
else
|
||||
req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
|
||||
|
||||
if (chip->ecc.size)
|
||||
req.ecc.sectorsize = chip->ecc.size;
|
||||
else if (chip->ecc_step_ds)
|
||||
req.ecc.sectorsize = chip->ecc_step_ds;
|
||||
else if (chip->base.eccreq.step_size)
|
||||
req.ecc.sectorsize = chip->base.eccreq.step_size;
|
||||
else
|
||||
req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO;
|
||||
|
||||
@@ -1507,13 +1511,20 @@ static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
|
||||
atmel_nand_init(nc, nand);
|
||||
|
||||
smc_nc = to_smc_nand_controller(chip->controller);
|
||||
if (!smc_nc->matrix)
|
||||
if (!smc_nc->ebi_csa_regmap)
|
||||
return;
|
||||
|
||||
/* Attach the CS to the NAND Flash logic. */
|
||||
for (i = 0; i < nand->numcs; i++)
|
||||
regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs,
|
||||
regmap_update_bits(smc_nc->ebi_csa_regmap,
|
||||
smc_nc->ebi_csa->offs,
|
||||
BIT(nand->cs[i].id), BIT(nand->cs[i].id));
|
||||
|
||||
if (smc_nc->ebi_csa->nfd0_on_d16)
|
||||
regmap_update_bits(smc_nc->ebi_csa_regmap,
|
||||
smc_nc->ebi_csa->offs,
|
||||
smc_nc->ebi_csa->nfd0_on_d16,
|
||||
smc_nc->ebi_csa->nfd0_on_d16);
|
||||
}
|
||||
|
||||
static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
|
||||
@@ -1797,7 +1808,7 @@ static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
|
||||
|
||||
ret = of_property_read_u32(np, "#size-cells", &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "missing #address-cells property\n");
|
||||
dev_err(dev, "missing #size-cells property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1833,34 +1844,71 @@ static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc)
|
||||
clk_put(nc->mck);
|
||||
}
|
||||
|
||||
static const struct of_device_id atmel_matrix_of_ids[] = {
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg at91sam9260_ebi_csa = {
|
||||
.offs = AT91SAM9260_MATRIX_EBICSA,
|
||||
};
|
||||
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg at91sam9261_ebi_csa = {
|
||||
.offs = AT91SAM9261_MATRIX_EBICSA,
|
||||
};
|
||||
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg at91sam9263_ebi_csa = {
|
||||
.offs = AT91SAM9263_MATRIX_EBI0CSA,
|
||||
};
|
||||
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg at91sam9rl_ebi_csa = {
|
||||
.offs = AT91SAM9RL_MATRIX_EBICSA,
|
||||
};
|
||||
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg at91sam9g45_ebi_csa = {
|
||||
.offs = AT91SAM9G45_MATRIX_EBICSA,
|
||||
};
|
||||
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg at91sam9n12_ebi_csa = {
|
||||
.offs = AT91SAM9N12_MATRIX_EBICSA,
|
||||
};
|
||||
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg at91sam9x5_ebi_csa = {
|
||||
.offs = AT91SAM9X5_MATRIX_EBICSA,
|
||||
};
|
||||
|
||||
static const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = {
|
||||
.offs = AT91_SFR_CCFG_EBICSA,
|
||||
.nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16,
|
||||
};
|
||||
|
||||
static const struct of_device_id atmel_ebi_csa_regmap_of_ids[] = {
|
||||
{
|
||||
.compatible = "atmel,at91sam9260-matrix",
|
||||
.data = (void *)AT91SAM9260_MATRIX_EBICSA,
|
||||
.data = &at91sam9260_ebi_csa,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9261-matrix",
|
||||
.data = (void *)AT91SAM9261_MATRIX_EBICSA,
|
||||
.data = &at91sam9261_ebi_csa,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9263-matrix",
|
||||
.data = (void *)AT91SAM9263_MATRIX_EBI0CSA,
|
||||
.data = &at91sam9263_ebi_csa,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9rl-matrix",
|
||||
.data = (void *)AT91SAM9RL_MATRIX_EBICSA,
|
||||
.data = &at91sam9rl_ebi_csa,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9g45-matrix",
|
||||
.data = (void *)AT91SAM9G45_MATRIX_EBICSA,
|
||||
.data = &at91sam9g45_ebi_csa,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9n12-matrix",
|
||||
.data = (void *)AT91SAM9N12_MATRIX_EBICSA,
|
||||
.data = &at91sam9n12_ebi_csa,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-matrix",
|
||||
.data = (void *)AT91SAM9X5_MATRIX_EBICSA,
|
||||
.data = &at91sam9x5_ebi_csa,
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,sam9x60-sfr",
|
||||
.data = &sam9x60_ebi_csa,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
@@ -1982,37 +2030,38 @@ atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
/* We do not retrieve the matrix syscon when parsing old DTs. */
|
||||
/* We do not retrieve the EBICSA regmap when parsing old DTs. */
|
||||
if (nc->base.caps->legacy_of_bindings)
|
||||
return 0;
|
||||
|
||||
np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0);
|
||||
np = of_parse_phandle(dev->parent->of_node,
|
||||
nc->base.caps->ebi_csa_regmap_name, 0);
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
match = of_match_node(atmel_matrix_of_ids, np);
|
||||
match = of_match_node(atmel_ebi_csa_regmap_of_ids, np);
|
||||
if (!match) {
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nc->matrix = syscon_node_to_regmap(np);
|
||||
nc->ebi_csa_regmap = syscon_node_to_regmap(np);
|
||||
of_node_put(np);
|
||||
if (IS_ERR(nc->matrix)) {
|
||||
ret = PTR_ERR(nc->matrix);
|
||||
dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret);
|
||||
if (IS_ERR(nc->ebi_csa_regmap)) {
|
||||
ret = PTR_ERR(nc->ebi_csa_regmap);
|
||||
dev_err(dev, "Could not get EBICSA regmap (err = %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nc->ebi_csa_offs = (uintptr_t)match->data;
|
||||
nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data;
|
||||
|
||||
/*
|
||||
* The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
|
||||
* add 4 to ->ebi_csa_offs.
|
||||
* add 4 to ->ebi_csa->offs.
|
||||
*/
|
||||
if (of_device_is_compatible(dev->parent->of_node,
|
||||
"atmel,at91sam9263-ebi1"))
|
||||
nc->ebi_csa_offs += 4;
|
||||
nc->ebi_csa->offs += 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2341,6 +2390,7 @@ static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
|
||||
static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
|
||||
.ale_offs = BIT(21),
|
||||
.cle_offs = BIT(22),
|
||||
.ebi_csa_regmap_name = "atmel,matrix",
|
||||
.ops = &at91rm9200_nc_ops,
|
||||
};
|
||||
|
||||
@@ -2355,12 +2405,14 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
|
||||
static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
|
||||
.ale_offs = BIT(21),
|
||||
.cle_offs = BIT(22),
|
||||
.ebi_csa_regmap_name = "atmel,matrix",
|
||||
.ops = &atmel_smc_nc_ops,
|
||||
};
|
||||
|
||||
static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = {
|
||||
.ale_offs = BIT(22),
|
||||
.cle_offs = BIT(21),
|
||||
.ebi_csa_regmap_name = "atmel,matrix",
|
||||
.ops = &atmel_smc_nc_ops,
|
||||
};
|
||||
|
||||
@@ -2368,6 +2420,15 @@ static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = {
|
||||
.has_dma = true,
|
||||
.ale_offs = BIT(21),
|
||||
.cle_offs = BIT(22),
|
||||
.ebi_csa_regmap_name = "atmel,matrix",
|
||||
.ops = &atmel_smc_nc_ops,
|
||||
};
|
||||
|
||||
static const struct atmel_nand_controller_caps microchip_sam9x60_nc_caps = {
|
||||
.has_dma = true,
|
||||
.ale_offs = BIT(21),
|
||||
.cle_offs = BIT(22),
|
||||
.ebi_csa_regmap_name = "microchip,sfr",
|
||||
.ops = &atmel_smc_nc_ops,
|
||||
};
|
||||
|
||||
@@ -2415,6 +2476,10 @@ static const struct of_device_id atmel_nand_controller_of_ids[] = {
|
||||
.compatible = "atmel,sama5d3-nand-controller",
|
||||
.data = &atmel_sama5_nc_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,sam9x60-nand-controller",
|
||||
.data = µchip_sam9x60_nc_caps,
|
||||
},
|
||||
/* Support for old/deprecated bindings: */
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-nand",
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2017 ATMEL
|
||||
* Copyright 2017 Free Electrons
|
||||
@@ -28,10 +29,6 @@
|
||||
* Add Nand Flash Controller support for SAMA5 SoC
|
||||
* Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The PMECC is an hardware assisted BCH engine, which means part of the
|
||||
* ECC algorithm is left to the software. The hardware/software repartition
|
||||
* is explained in the "PMECC Controller Functional Description" chapter in
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* © Copyright 2016 ATMEL
|
||||
* © Copyright 2016 Free Electrons
|
||||
@@ -28,11 +29,6 @@
|
||||
*
|
||||
* Add Nand Flash Controller support for SAMA5 SoC
|
||||
* © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ATMEL_PMECC_H
|
||||
|
@@ -428,7 +428,7 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
|
||||
}
|
||||
|
||||
/* Configure FLASH */
|
||||
chipsize = b47n->nand_chip.chipsize >> 20;
|
||||
chipsize = nanddev_target_size(&b47n->nand_chip.base) >> 20;
|
||||
tbits = ffs(chipsize); /* find first bit set */
|
||||
if (!tbits || tbits != fls(chipsize)) {
|
||||
pr_err("Invalid flash size: 0x%lX\n", chipsize);
|
||||
|
@@ -1676,11 +1676,8 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
|
||||
int page = addr >> chip->page_shift;
|
||||
int ret;
|
||||
|
||||
if (!buf) {
|
||||
buf = chip->data_buf;
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
}
|
||||
if (!buf)
|
||||
buf = nand_get_data_buf(chip);
|
||||
|
||||
sas = mtd->oobsize / chip->ecc.steps;
|
||||
|
||||
|
Plik diff jest za duży
Load Diff
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/types.h>
|
||||
@@ -290,38 +291,108 @@
|
||||
#define CHNL_ACTIVE__CHANNEL2 BIT(2)
|
||||
#define CHNL_ACTIVE__CHANNEL3 BIT(3)
|
||||
|
||||
struct denali_nand_info {
|
||||
struct nand_chip nand;
|
||||
unsigned long clk_rate; /* core clock rate */
|
||||
unsigned long clk_x_rate; /* bus interface clock rate */
|
||||
int active_bank; /* currently selected bank */
|
||||
/**
|
||||
* struct denali_chip_sel - per-CS data of Denali NAND
|
||||
*
|
||||
* @bank: bank id of the controller this CS is connected to
|
||||
* @hwhr2_and_we_2_re: value of timing register HWHR2_AND_WE_2_RE
|
||||
* @tcwaw_and_addr_2_data: value of timing register TCWAW_AND_ADDR_2_DATA
|
||||
* @re_2_we: value of timing register RE_2_WE
|
||||
* @acc_clks: value of timing register ACC_CLKS
|
||||
* @rdwr_en_lo_cnt: value of timing register RDWR_EN_LO_CNT
|
||||
* @rdwr_en_hi_cnt: value of timing register RDWR_EN_HI_CNT
|
||||
* @cs_setup_cnt: value of timing register CS_SETUP_CNT
|
||||
* @re_2_re: value of timing register RE_2_RE
|
||||
*/
|
||||
struct denali_chip_sel {
|
||||
int bank;
|
||||
u32 hwhr2_and_we_2_re;
|
||||
u32 tcwaw_and_addr_2_data;
|
||||
u32 re_2_we;
|
||||
u32 acc_clks;
|
||||
u32 rdwr_en_lo_cnt;
|
||||
u32 rdwr_en_hi_cnt;
|
||||
u32 cs_setup_cnt;
|
||||
u32 re_2_re;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct denali_chip - per-chip data of Denali NAND
|
||||
*
|
||||
* @chip: base NAND chip structure
|
||||
* @node: node to be used to associate this chip with the controller
|
||||
* @nsels: the number of CS lines of this chip
|
||||
* @sels: the array of per-cs data
|
||||
*/
|
||||
struct denali_chip {
|
||||
struct nand_chip chip;
|
||||
struct list_head node;
|
||||
unsigned int nsels;
|
||||
struct denali_chip_sel sels[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct denali_controller - Denali NAND controller data
|
||||
*
|
||||
* @controller: base NAND controller structure
|
||||
* @dev: device
|
||||
* @chips: the list of chips attached to this controller
|
||||
* @clk_rate: frequency of core clock
|
||||
* @clk_x_rate: frequency of bus interface clock
|
||||
* @reg: base of Register Interface
|
||||
* @host: base of Host Data/Command interface
|
||||
* @complete: completion used to wait for interrupts
|
||||
* @irq: interrupt number
|
||||
* @irq_mask: interrupt bits the controller is waiting for
|
||||
* @irq_status: interrupt bits of events that have happened
|
||||
* @irq_lock: lock to protect @irq_mask and @irq_status
|
||||
* @dma_avail: set if DMA engine is available
|
||||
* @devs_per_cs: number of devices connected in parallel
|
||||
* @oob_skip_bytes: number of bytes in OOB skipped by the ECC engine
|
||||
* @active_bank: active bank id
|
||||
* @nbanks: the number of banks supported by this controller
|
||||
* @revision: IP revision
|
||||
* @caps: controller capabilities that cannot be detected run-time
|
||||
* @ecc_caps: ECC engine capabilities
|
||||
* @host_read: callback for read access of Host Data/Command Interface
|
||||
* @host_write: callback for write access of Host Data/Command Interface
|
||||
* @setup_dma: callback for setup of the Data DMA
|
||||
*/
|
||||
struct denali_controller {
|
||||
struct nand_controller controller;
|
||||
struct device *dev;
|
||||
void __iomem *reg; /* Register Interface */
|
||||
void __iomem *host; /* Host Data/Command Interface */
|
||||
struct list_head chips;
|
||||
unsigned long clk_rate;
|
||||
unsigned long clk_x_rate;
|
||||
void __iomem *reg;
|
||||
void __iomem *host;
|
||||
struct completion complete;
|
||||
spinlock_t irq_lock; /* protect irq_mask and irq_status */
|
||||
u32 irq_mask; /* interrupts we are waiting for */
|
||||
u32 irq_status; /* interrupts that have happened */
|
||||
int irq;
|
||||
void *buf; /* for syndrome layout conversion */
|
||||
int dma_avail; /* can support DMA? */
|
||||
int devs_per_cs; /* devices connected in parallel */
|
||||
int oob_skip_bytes; /* number of bytes reserved for BBM */
|
||||
int max_banks;
|
||||
unsigned int revision; /* IP revision */
|
||||
unsigned int caps; /* IP capability (or quirk) */
|
||||
u32 irq_mask;
|
||||
u32 irq_status;
|
||||
spinlock_t irq_lock;
|
||||
bool dma_avail;
|
||||
int devs_per_cs;
|
||||
int oob_skip_bytes;
|
||||
int active_bank;
|
||||
int nbanks;
|
||||
unsigned int revision;
|
||||
unsigned int caps;
|
||||
const struct nand_ecc_caps *ecc_caps;
|
||||
u32 (*host_read)(struct denali_nand_info *denali, u32 addr);
|
||||
void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data);
|
||||
void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr,
|
||||
int page, int write);
|
||||
u32 (*host_read)(struct denali_controller *denali, u32 addr);
|
||||
void (*host_write)(struct denali_controller *denali, u32 addr,
|
||||
u32 data);
|
||||
void (*setup_dma)(struct denali_controller *denali, dma_addr_t dma_addr,
|
||||
int page, bool write);
|
||||
};
|
||||
|
||||
#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
|
||||
#define DENALI_CAP_DMA_64BIT BIT(1)
|
||||
|
||||
int denali_calc_ecc_bytes(int step_size, int strength);
|
||||
int denali_init(struct denali_nand_info *denali);
|
||||
void denali_remove(struct denali_nand_info *denali);
|
||||
int denali_chip_init(struct denali_controller *denali,
|
||||
struct denali_chip *dchip);
|
||||
int denali_init(struct denali_controller *denali);
|
||||
void denali_remove(struct denali_controller *denali);
|
||||
|
||||
#endif /* __DENALI_H__ */
|
||||
|
@@ -18,7 +18,7 @@
|
||||
#include "denali.h"
|
||||
|
||||
struct denali_dt {
|
||||
struct denali_nand_info denali;
|
||||
struct denali_controller controller;
|
||||
struct clk *clk; /* core clock */
|
||||
struct clk *clk_x; /* bus interface clock */
|
||||
struct clk *clk_ecc; /* ECC circuit clock */
|
||||
@@ -71,19 +71,92 @@ static const struct of_device_id denali_nand_dt_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
|
||||
|
||||
static int denali_dt_chip_init(struct denali_controller *denali,
|
||||
struct device_node *chip_np)
|
||||
{
|
||||
struct denali_chip *dchip;
|
||||
u32 bank;
|
||||
int nsels, i, ret;
|
||||
|
||||
nsels = of_property_count_u32_elems(chip_np, "reg");
|
||||
if (nsels < 0)
|
||||
return nsels;
|
||||
|
||||
dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
|
||||
GFP_KERNEL);
|
||||
if (!dchip)
|
||||
return -ENOMEM;
|
||||
|
||||
dchip->nsels = nsels;
|
||||
|
||||
for (i = 0; i < nsels; i++) {
|
||||
ret = of_property_read_u32_index(chip_np, "reg", i, &bank);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dchip->sels[i].bank = bank;
|
||||
|
||||
nand_set_flash_node(&dchip->chip, chip_np);
|
||||
}
|
||||
|
||||
return denali_chip_init(denali, dchip);
|
||||
}
|
||||
|
||||
/* Backward compatibility for old platforms */
|
||||
static int denali_dt_legacy_chip_init(struct denali_controller *denali)
|
||||
{
|
||||
struct denali_chip *dchip;
|
||||
int nsels, i;
|
||||
|
||||
nsels = denali->nbanks;
|
||||
|
||||
dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
|
||||
GFP_KERNEL);
|
||||
if (!dchip)
|
||||
return -ENOMEM;
|
||||
|
||||
dchip->nsels = nsels;
|
||||
|
||||
for (i = 0; i < nsels; i++)
|
||||
dchip->sels[i].bank = i;
|
||||
|
||||
nand_set_flash_node(&dchip->chip, denali->dev->of_node);
|
||||
|
||||
return denali_chip_init(denali, dchip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the DT binding.
|
||||
* The new binding expects chip subnodes in the controller node.
|
||||
* So, #address-cells = <1>; #size-cells = <0>; are required.
|
||||
* Check the #size-cells to distinguish the binding.
|
||||
*/
|
||||
static bool denali_dt_is_legacy_binding(struct device_node *np)
|
||||
{
|
||||
u32 cells;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "#size-cells", &cells);
|
||||
if (ret)
|
||||
return true;
|
||||
|
||||
return cells != 0;
|
||||
}
|
||||
|
||||
static int denali_dt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct denali_dt *dt;
|
||||
const struct denali_dt_data *data;
|
||||
struct denali_nand_info *denali;
|
||||
struct denali_controller *denali;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
|
||||
if (!dt)
|
||||
return -ENOMEM;
|
||||
denali = &dt->denali;
|
||||
denali = &dt->controller;
|
||||
|
||||
data = of_device_get_match_data(dev);
|
||||
if (data) {
|
||||
@@ -140,9 +213,26 @@ static int denali_dt_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto out_disable_clk_ecc;
|
||||
|
||||
if (denali_dt_is_legacy_binding(dev->of_node)) {
|
||||
ret = denali_dt_legacy_chip_init(denali);
|
||||
if (ret)
|
||||
goto out_remove_denali;
|
||||
} else {
|
||||
for_each_child_of_node(dev->of_node, np) {
|
||||
ret = denali_dt_chip_init(denali, np);
|
||||
if (ret) {
|
||||
of_node_put(np);
|
||||
goto out_remove_denali;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dt);
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove_denali:
|
||||
denali_remove(denali);
|
||||
out_disable_clk_ecc:
|
||||
clk_disable_unprepare(dt->clk_ecc);
|
||||
out_disable_clk_x:
|
||||
@@ -157,7 +247,7 @@ static int denali_dt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct denali_dt *dt = platform_get_drvdata(pdev);
|
||||
|
||||
denali_remove(&dt->denali);
|
||||
denali_remove(&dt->controller);
|
||||
clk_disable_unprepare(dt->clk_ecc);
|
||||
clk_disable_unprepare(dt->clk_x);
|
||||
clk_disable_unprepare(dt->clk);
|
||||
|
@@ -29,10 +29,11 @@ NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15);
|
||||
|
||||
static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
resource_size_t csr_base, mem_base;
|
||||
unsigned long csr_len, mem_len;
|
||||
struct denali_nand_info *denali;
|
||||
struct denali_controller *denali;
|
||||
struct denali_chip *dchip;
|
||||
int nsels, ret, i;
|
||||
|
||||
denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
|
||||
if (!denali)
|
||||
@@ -64,7 +65,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
denali->dev = &dev->dev;
|
||||
denali->irq = dev->irq;
|
||||
denali->ecc_caps = &denali_pci_ecc_caps;
|
||||
denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
|
||||
denali->clk_rate = 50000000; /* 50 MHz */
|
||||
denali->clk_x_rate = 200000000; /* 200 MHz */
|
||||
|
||||
@@ -84,27 +84,49 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
if (!denali->host) {
|
||||
dev_err(&dev->dev, "Spectra: ioremap_nocache failed!");
|
||||
ret = -ENOMEM;
|
||||
goto failed_remap_reg;
|
||||
goto out_unmap_reg;
|
||||
}
|
||||
|
||||
ret = denali_init(denali);
|
||||
if (ret)
|
||||
goto failed_remap_mem;
|
||||
goto out_unmap_host;
|
||||
|
||||
nsels = denali->nbanks;
|
||||
|
||||
dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
|
||||
GFP_KERNEL);
|
||||
if (!dchip) {
|
||||
ret = -ENOMEM;
|
||||
goto out_remove_denali;
|
||||
}
|
||||
|
||||
dchip->chip.ecc.options |= NAND_ECC_MAXIMIZE;
|
||||
|
||||
dchip->nsels = nsels;
|
||||
|
||||
for (i = 0; i < nsels; i++)
|
||||
dchip->sels[i].bank = i;
|
||||
|
||||
ret = denali_chip_init(denali, dchip);
|
||||
if (ret)
|
||||
goto out_remove_denali;
|
||||
|
||||
pci_set_drvdata(dev, denali);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_remap_mem:
|
||||
out_remove_denali:
|
||||
denali_remove(denali);
|
||||
out_unmap_host:
|
||||
iounmap(denali->host);
|
||||
failed_remap_reg:
|
||||
out_unmap_reg:
|
||||
iounmap(denali->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void denali_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct denali_nand_info *denali = pci_get_drvdata(dev);
|
||||
struct denali_controller *denali = pci_get_drvdata(dev);
|
||||
|
||||
denali_remove(denali);
|
||||
iounmap(denali->reg);
|
||||
|
@@ -1028,6 +1028,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
struct doc_priv *doc = nand_get_controller_data(this);
|
||||
struct nand_memory_organization *memorg;
|
||||
int ret = 0;
|
||||
u_char *buf;
|
||||
struct NFTLMediaHeader *mh;
|
||||
@@ -1036,6 +1037,8 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
|
||||
unsigned blocks, maxblocks;
|
||||
int offs, numheaders;
|
||||
|
||||
memorg = nanddev_get_memorg(&this->base);
|
||||
|
||||
buf = kmalloc(mtd->writesize, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
return 0;
|
||||
@@ -1082,6 +1085,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
|
||||
implementation of the NAND layer. */
|
||||
if (mh->UnitSizeFactor != 0xff) {
|
||||
this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
|
||||
memorg->pages_per_eraseblock <<= (0xff - mh->UnitSizeFactor);
|
||||
mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
|
||||
pr_info("Setting virtual erase size to %d\n", mtd->erasesize);
|
||||
blocks = mtd->size >> this->bbt_erase_shift;
|
||||
@@ -1287,7 +1291,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
|
||||
struct doc_priv *doc = nand_get_controller_data(this);
|
||||
struct mtd_partition parts[5];
|
||||
|
||||
if (this->numchips > doc->chips_per_floor) {
|
||||
if (nanddev_ntargets(&this->base) > doc->chips_per_floor) {
|
||||
pr_err("Multi-floor INFTL devices not yet supported.\n");
|
||||
return -EIO;
|
||||
}
|
||||
@@ -1477,6 +1481,7 @@ static int __init doc_probe(unsigned long physadr)
|
||||
break;
|
||||
case DOC_ChipID_DocMilPlus32:
|
||||
pr_err("DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
|
||||
/* fall through */
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
goto notfound;
|
||||
|
@@ -355,6 +355,15 @@ static void fsl_elbc_cmdfunc(struct nand_chip *chip, unsigned int command,
|
||||
fsl_elbc_run_command(mtd);
|
||||
return;
|
||||
|
||||
/* RNDOUT moves the pointer inside the page */
|
||||
case NAND_CMD_RNDOUT:
|
||||
dev_dbg(priv->dev,
|
||||
"fsl_elbc_cmdfunc: NAND_CMD_RNDOUT, column: 0x%x.\n",
|
||||
column);
|
||||
|
||||
elbc_fcm_ctrl->index = column;
|
||||
return;
|
||||
|
||||
/* READOOB reads only the OOB because no ECC is performed. */
|
||||
case NAND_CMD_READOOB:
|
||||
dev_vdbg(priv->dev,
|
||||
@@ -635,79 +644,6 @@ static int fsl_elbc_wait(struct nand_chip *chip)
|
||||
return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
|
||||
}
|
||||
|
||||
static int fsl_elbc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
unsigned int al;
|
||||
|
||||
/* calculate FMR Address Length field */
|
||||
al = 0;
|
||||
if (chip->pagemask & 0xffff0000)
|
||||
al++;
|
||||
if (chip->pagemask & 0xff000000)
|
||||
al++;
|
||||
|
||||
priv->fmr |= al << FMR_AL_SHIFT;
|
||||
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
|
||||
chip->numchips);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
|
||||
chip->chipsize);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
|
||||
chip->pagemask);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->legacy.chip_delay = %d\n",
|
||||
chip->legacy.chip_delay);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
|
||||
chip->badblockpos);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
|
||||
chip->chip_shift);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
|
||||
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->ecc.mode = %d\n",
|
||||
chip->ecc.mode);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
|
||||
chip->ecc.steps);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
|
||||
chip->ecc.bytes);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
|
||||
chip->ecc.total);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
|
||||
mtd->ooblayout);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
|
||||
mtd->erasesize);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
|
||||
mtd->writesize);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
|
||||
mtd->oobsize);
|
||||
|
||||
/* adjust Option Register and ECC to match Flash page size */
|
||||
if (mtd->writesize == 512) {
|
||||
priv->page_size = 0;
|
||||
clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
|
||||
} else if (mtd->writesize == 2048) {
|
||||
priv->page_size = 1;
|
||||
setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
|
||||
} else {
|
||||
dev_err(priv->dev,
|
||||
"fsl_elbc_init: page size %d is not supported\n",
|
||||
mtd->writesize);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops fsl_elbc_controller_ops = {
|
||||
.attach_chip = fsl_elbc_attach_chip,
|
||||
};
|
||||
|
||||
static int fsl_elbc_read_page(struct nand_chip *chip, uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
@@ -794,27 +730,116 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||
chip->controller = &elbc_fcm_ctrl->controller;
|
||||
nand_set_controller_data(chip, 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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If CS Base Register selects full hardware ECC then use it */
|
||||
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
|
||||
BR_DECC_CHK_GEN) {
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
static int fsl_elbc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
unsigned int al;
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
/*
|
||||
* if ECC was not chosen in DT, decide whether to use HW or SW ECC from
|
||||
* CS Base Register
|
||||
*/
|
||||
case NAND_ECC_NONE:
|
||||
/* If CS Base Register selects full hardware ECC then use it */
|
||||
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
|
||||
BR_DECC_CHK_GEN) {
|
||||
chip->ecc.read_page = fsl_elbc_read_page;
|
||||
chip->ecc.write_page = fsl_elbc_write_page;
|
||||
chip->ecc.write_subpage = fsl_elbc_write_subpage;
|
||||
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
|
||||
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;
|
||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
||||
}
|
||||
break;
|
||||
|
||||
/* if SW ECC was chosen in DT, we do not need to set anything here */
|
||||
case NAND_ECC_SOFT:
|
||||
break;
|
||||
|
||||
/* should we also implement NAND_ECC_HW to do as the code above? */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* calculate FMR Address Length field */
|
||||
al = 0;
|
||||
if (chip->pagemask & 0xffff0000)
|
||||
al++;
|
||||
if (chip->pagemask & 0xff000000)
|
||||
al++;
|
||||
|
||||
priv->fmr |= al << FMR_AL_SHIFT;
|
||||
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
|
||||
nanddev_ntargets(&chip->base));
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
|
||||
nanddev_target_size(&chip->base));
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
|
||||
chip->pagemask);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->legacy.chip_delay = %d\n",
|
||||
chip->legacy.chip_delay);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
|
||||
chip->badblockpos);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
|
||||
chip->chip_shift);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
|
||||
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->ecc.mode = %d\n",
|
||||
chip->ecc.mode);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
|
||||
chip->ecc.steps);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
|
||||
chip->ecc.bytes);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
|
||||
chip->ecc.total);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
|
||||
mtd->ooblayout);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
|
||||
mtd->erasesize);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
|
||||
mtd->writesize);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
|
||||
mtd->oobsize);
|
||||
|
||||
/* adjust Option Register and ECC to match Flash page size */
|
||||
if (mtd->writesize == 512) {
|
||||
priv->page_size = 0;
|
||||
clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
|
||||
} else if (mtd->writesize == 2048) {
|
||||
priv->page_size = 1;
|
||||
setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
|
||||
} else {
|
||||
/* otherwise fall back to default software ECC */
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
||||
dev_err(priv->dev,
|
||||
"fsl_elbc_init: page size %d is not supported\n",
|
||||
mtd->writesize);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops fsl_elbc_controller_ops = {
|
||||
.attach_chip = fsl_elbc_attach_chip,
|
||||
};
|
||||
|
||||
static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
|
||||
{
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
|
@@ -722,9 +722,9 @@ static int fsl_ifc_attach_chip(struct nand_chip *chip)
|
||||
struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
|
||||
|
||||
dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__,
|
||||
chip->numchips);
|
||||
nanddev_ntargets(&chip->base));
|
||||
dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__,
|
||||
chip->chipsize);
|
||||
nanddev_target_size(&chip->base));
|
||||
dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__,
|
||||
chip->pagemask);
|
||||
dev_dbg(priv->dev, "%s: nand->legacy.chip_delay = %d\n", __func__,
|
||||
|
@@ -157,8 +157,7 @@ int gpmi_init(struct gpmi_nand_data *this)
|
||||
* Reset BCH here, too. We got failures otherwise :(
|
||||
* See later BCH reset for explanation of MX23 and MX28 handling
|
||||
*/
|
||||
ret = gpmi_reset_block(r->bch_regs,
|
||||
GPMI_IS_MX23(this) || GPMI_IS_MX28(this));
|
||||
ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this));
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
@@ -266,8 +265,7 @@ int bch_set_geometry(struct gpmi_nand_data *this)
|
||||
* chip, otherwise it will lock up. So we skip resetting BCH on the MX23.
|
||||
* and MX28.
|
||||
*/
|
||||
ret = gpmi_reset_block(r->bch_regs,
|
||||
GPMI_IS_MX23(this) || GPMI_IS_MX28(this));
|
||||
ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this));
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
|
@@ -171,7 +171,7 @@ 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)) {
|
||||
if (GPMI_IS_MXS(this)) {
|
||||
/* The mx23/mx28 only support the GF13. */
|
||||
if (geo->gf_len == 14)
|
||||
return false;
|
||||
@@ -204,7 +204,8 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
|
||||
default:
|
||||
dev_err(this->dev,
|
||||
"unsupported nand chip. ecc bits : %d, ecc size : %d\n",
|
||||
chip->ecc_strength_ds, chip->ecc_step_ds);
|
||||
chip->base.eccreq.strength,
|
||||
chip->base.eccreq.step_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
geo->ecc_chunk_size = ecc_step;
|
||||
@@ -417,11 +418,13 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
|
||||
|
||||
if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
|
||||
|| legacy_set_geometry(this)) {
|
||||
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
|
||||
if (!(chip->base.eccreq.strength > 0 &&
|
||||
chip->base.eccreq.step_size > 0))
|
||||
return -EINVAL;
|
||||
|
||||
return set_geometry_by_ecc_info(this, chip->ecc_strength_ds,
|
||||
chip->ecc_step_ds);
|
||||
return set_geometry_by_ecc_info(this,
|
||||
chip->base.eccreq.strength,
|
||||
chip->base.eccreq.step_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1602,7 +1605,7 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
|
||||
unsigned int search_area_size_in_strides;
|
||||
unsigned int stride;
|
||||
unsigned int page;
|
||||
uint8_t *buffer = chip->data_buf;
|
||||
u8 *buffer = nand_get_data_buf(chip);
|
||||
int saved_chip_number;
|
||||
int found_an_ncb_fingerprint = false;
|
||||
|
||||
@@ -1664,7 +1667,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
|
||||
unsigned int block;
|
||||
unsigned int stride;
|
||||
unsigned int page;
|
||||
uint8_t *buffer = chip->data_buf;
|
||||
u8 *buffer = nand_get_data_buf(chip);
|
||||
int saved_chip_number;
|
||||
int status;
|
||||
|
||||
@@ -1753,7 +1756,7 @@ static int mx23_boot_init(struct gpmi_nand_data *this)
|
||||
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;
|
||||
block_count = nanddev_eraseblocks_per_target(&chip->base);
|
||||
|
||||
/*
|
||||
* Loop over all the blocks in the medium, transcribing block marks as
|
||||
|
@@ -207,4 +207,5 @@ void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
|
||||
|
||||
#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \
|
||||
GPMI_IS_MX7D(x))
|
||||
#define GPMI_IS_MXS(x) (GPMI_IS_MX23(x) || GPMI_IS_MX28(x))
|
||||
#endif
|
||||
|
@@ -849,7 +849,7 @@ static int hisi_nfc_resume(struct device *dev)
|
||||
struct hinfc_host *host = dev_get_drvdata(dev);
|
||||
struct nand_chip *chip = &host->chip;
|
||||
|
||||
for (cs = 0; cs < chip->numchips; cs++)
|
||||
for (cs = 0; cs < nanddev_ntargets(&chip->base); cs++)
|
||||
hisi_nfc_send_cmd_reset(host, cs);
|
||||
hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
|
||||
HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
|
||||
|
50
drivers/mtd/nand/raw/ingenic/Kconfig
Normal file
50
drivers/mtd/nand/raw/ingenic/Kconfig
Normal file
@@ -0,0 +1,50 @@
|
||||
config MTD_NAND_JZ4740
|
||||
tristate "JZ4740 NAND controller"
|
||||
depends on MACH_JZ4740 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash on JZ4740 SoC based boards.
|
||||
|
||||
config MTD_NAND_JZ4780
|
||||
tristate "JZ4780 NAND controller"
|
||||
depends on JZ4780_NEMC
|
||||
help
|
||||
Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
|
||||
based boards, using the BCH controller for hardware error correction.
|
||||
|
||||
if MTD_NAND_JZ4780
|
||||
|
||||
config MTD_NAND_INGENIC_ECC
|
||||
tristate
|
||||
|
||||
config MTD_NAND_JZ4740_ECC
|
||||
tristate "Hardware BCH support for JZ4740 SoC"
|
||||
select MTD_NAND_INGENIC_ECC
|
||||
help
|
||||
Enable this driver to support the Reed-Solomon error-correction
|
||||
hardware present on the JZ4740 SoC from Ingenic.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called jz4740-ecc.
|
||||
|
||||
config MTD_NAND_JZ4725B_BCH
|
||||
tristate "Hardware BCH support for JZ4725B SoC"
|
||||
select MTD_NAND_INGENIC_ECC
|
||||
help
|
||||
Enable this driver to support the BCH error-correction hardware
|
||||
present on the JZ4725B SoC from Ingenic.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called jz4725b-bch.
|
||||
|
||||
config MTD_NAND_JZ4780_BCH
|
||||
tristate "Hardware BCH support for JZ4780 SoC"
|
||||
select MTD_NAND_INGENIC_ECC
|
||||
help
|
||||
Enable this driver to support the BCH error-correction hardware
|
||||
present on the JZ4780 SoC from Ingenic.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called jz4780-bch.
|
||||
|
||||
endif # MTD_NAND_JZ4780
|
7
drivers/mtd/nand/raw/ingenic/Makefile
Normal file
7
drivers/mtd/nand/raw/ingenic/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_JZ4780) += ingenic_nand.o
|
||||
|
||||
obj-$(CONFIG_MTD_NAND_INGENIC_ECC) += ingenic_ecc.o
|
||||
obj-$(CONFIG_MTD_NAND_JZ4740_ECC) += jz4740_ecc.o
|
||||
obj-$(CONFIG_MTD_NAND_JZ4725B_BCH) += jz4725b_bch.o
|
||||
obj-$(CONFIG_MTD_NAND_JZ4780_BCH) += jz4780_bch.o
|
166
drivers/mtd/nand/raw/ingenic/ingenic_ecc.c
Normal file
166
drivers/mtd/nand/raw/ingenic/ingenic_ecc.c
Normal file
@@ -0,0 +1,166 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* JZ47xx ECC common code
|
||||
*
|
||||
* Copyright (c) 2015 Imagination Technologies
|
||||
* Author: Alex Smith <alex.smith@imgtec.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "ingenic_ecc.h"
|
||||
|
||||
/**
|
||||
* ingenic_ecc_calculate() - calculate ECC for a data buffer
|
||||
* @ecc: ECC device.
|
||||
* @params: ECC parameters.
|
||||
* @buf: input buffer with raw data.
|
||||
* @ecc_code: output buffer with ECC.
|
||||
*
|
||||
* Return: 0 on success, -ETIMEDOUT if timed out while waiting for ECC
|
||||
* controller.
|
||||
*/
|
||||
int ingenic_ecc_calculate(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
const u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
return ecc->ops->calculate(ecc, params, buf, ecc_code);
|
||||
}
|
||||
EXPORT_SYMBOL(ingenic_ecc_calculate);
|
||||
|
||||
/**
|
||||
* ingenic_ecc_correct() - detect and correct bit errors
|
||||
* @ecc: ECC device.
|
||||
* @params: ECC parameters.
|
||||
* @buf: raw data read from the chip.
|
||||
* @ecc_code: ECC read from the chip.
|
||||
*
|
||||
* Given the raw data and the ECC read from the NAND device, detects and
|
||||
* corrects errors in the data.
|
||||
*
|
||||
* Return: the number of bit errors corrected, -EBADMSG if there are too many
|
||||
* errors to correct or -ETIMEDOUT if we timed out waiting for the controller.
|
||||
*/
|
||||
int ingenic_ecc_correct(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
return ecc->ops->correct(ecc, params, buf, ecc_code);
|
||||
}
|
||||
EXPORT_SYMBOL(ingenic_ecc_correct);
|
||||
|
||||
/**
|
||||
* ingenic_ecc_get() - get the ECC controller device
|
||||
* @np: ECC device tree node.
|
||||
*
|
||||
* Gets the ECC controller device from the specified device tree node. The
|
||||
* device must be released with ingenic_ecc_release() when it is no longer being
|
||||
* used.
|
||||
*
|
||||
* Return: a pointer to ingenic_ecc, errors are encoded into the pointer.
|
||||
* PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
|
||||
*/
|
||||
static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct ingenic_ecc *ecc;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev || !platform_get_drvdata(pdev))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
get_device(&pdev->dev);
|
||||
|
||||
ecc = platform_get_drvdata(pdev);
|
||||
clk_prepare_enable(ecc->clk);
|
||||
|
||||
return ecc;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_ingenic_ecc_get() - get the ECC controller from a DT node
|
||||
* @of_node: the node that contains an ecc-engine property.
|
||||
*
|
||||
* Get the ecc-engine property from the given device tree
|
||||
* node and pass it to ingenic_ecc_get to do the work.
|
||||
*
|
||||
* Return: a pointer to ingenic_ecc, errors are encoded into the pointer.
|
||||
* PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
|
||||
*/
|
||||
struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *of_node)
|
||||
{
|
||||
struct ingenic_ecc *ecc = NULL;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(of_node, "ecc-engine", 0);
|
||||
|
||||
/*
|
||||
* If the ecc-engine property is not found, check for the deprecated
|
||||
* ingenic,bch-controller property
|
||||
*/
|
||||
if (!np)
|
||||
np = of_parse_phandle(of_node, "ingenic,bch-controller", 0);
|
||||
|
||||
if (np) {
|
||||
ecc = ingenic_ecc_get(np);
|
||||
of_node_put(np);
|
||||
}
|
||||
return ecc;
|
||||
}
|
||||
EXPORT_SYMBOL(of_ingenic_ecc_get);
|
||||
|
||||
/**
|
||||
* ingenic_ecc_release() - release the ECC controller device
|
||||
* @ecc: ECC device.
|
||||
*/
|
||||
void ingenic_ecc_release(struct ingenic_ecc *ecc)
|
||||
{
|
||||
clk_disable_unprepare(ecc->clk);
|
||||
put_device(ecc->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(ingenic_ecc_release);
|
||||
|
||||
int ingenic_ecc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ingenic_ecc *ecc;
|
||||
struct resource *res;
|
||||
|
||||
ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
|
||||
if (!ecc)
|
||||
return -ENOMEM;
|
||||
|
||||
ecc->ops = device_get_match_data(dev);
|
||||
if (!ecc->ops)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ecc->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ecc->base))
|
||||
return PTR_ERR(ecc->base);
|
||||
|
||||
ecc->ops->disable(ecc);
|
||||
|
||||
ecc->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(ecc->clk)) {
|
||||
dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
|
||||
return PTR_ERR(ecc->clk);
|
||||
}
|
||||
|
||||
mutex_init(&ecc->lock);
|
||||
|
||||
ecc->dev = dev;
|
||||
platform_set_drvdata(pdev, ecc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ingenic_ecc_probe);
|
||||
|
||||
MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
|
||||
MODULE_AUTHOR("Harvey Hunt <harveyhuntnexus@gmail.com>");
|
||||
MODULE_DESCRIPTION("Ingenic ECC common driver");
|
||||
MODULE_LICENSE("GPL v2");
|
83
drivers/mtd/nand/raw/ingenic/ingenic_ecc.h
Normal file
83
drivers/mtd/nand/raw/ingenic/ingenic_ecc.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __DRIVERS_MTD_NAND_INGENIC_ECC_INTERNAL_H__
|
||||
#define __DRIVERS_MTD_NAND_INGENIC_ECC_INTERNAL_H__
|
||||
|
||||
#include <linux/compiler_types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
|
||||
struct clk;
|
||||
struct device;
|
||||
struct ingenic_ecc;
|
||||
struct platform_device;
|
||||
|
||||
/**
|
||||
* struct ingenic_ecc_params - ECC parameters
|
||||
* @size: data bytes per ECC step.
|
||||
* @bytes: ECC bytes per step.
|
||||
* @strength: number of correctable bits per ECC step.
|
||||
*/
|
||||
struct ingenic_ecc_params {
|
||||
int size;
|
||||
int bytes;
|
||||
int strength;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_MTD_NAND_INGENIC_ECC)
|
||||
int ingenic_ecc_calculate(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
const u8 *buf, u8 *ecc_code);
|
||||
int ingenic_ecc_correct(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params, u8 *buf,
|
||||
u8 *ecc_code);
|
||||
|
||||
void ingenic_ecc_release(struct ingenic_ecc *ecc);
|
||||
struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *np);
|
||||
#else /* CONFIG_MTD_NAND_INGENIC_ECC */
|
||||
int ingenic_ecc_calculate(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
const u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int ingenic_ecc_correct(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params, u8 *buf,
|
||||
u8 *ecc_code)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
void ingenic_ecc_release(struct ingenic_ecc *ecc)
|
||||
{
|
||||
}
|
||||
|
||||
struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *np)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
#endif /* CONFIG_MTD_NAND_INGENIC_ECC */
|
||||
|
||||
struct ingenic_ecc_ops {
|
||||
void (*disable)(struct ingenic_ecc *ecc);
|
||||
int (*calculate)(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
const u8 *buf, u8 *ecc_code);
|
||||
int (*correct)(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
u8 *buf, u8 *ecc_code);
|
||||
};
|
||||
|
||||
struct ingenic_ecc {
|
||||
struct device *dev;
|
||||
const struct ingenic_ecc_ops *ops;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
int ingenic_ecc_probe(struct platform_device *pdev);
|
||||
|
||||
#endif /* __DRIVERS_MTD_NAND_INGENIC_ECC_INTERNAL_H__ */
|
530
drivers/mtd/nand/raw/ingenic/ingenic_nand.c
Normal file
530
drivers/mtd/nand/raw/ingenic/ingenic_nand.c
Normal file
@@ -0,0 +1,530 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Ingenic JZ47xx NAND driver
|
||||
*
|
||||
* Copyright (c) 2015 Imagination Technologies
|
||||
* Author: Alex Smith <alex.smith@imgtec.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <linux/jz4780-nemc.h>
|
||||
|
||||
#include "ingenic_ecc.h"
|
||||
|
||||
#define DRV_NAME "ingenic-nand"
|
||||
|
||||
/* Command delay when there is no R/B pin. */
|
||||
#define RB_DELAY_US 100
|
||||
|
||||
struct jz_soc_info {
|
||||
unsigned long data_offset;
|
||||
unsigned long addr_offset;
|
||||
unsigned long cmd_offset;
|
||||
const struct mtd_ooblayout_ops *oob_layout;
|
||||
};
|
||||
|
||||
struct ingenic_nand_cs {
|
||||
unsigned int bank;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
struct ingenic_nfc {
|
||||
struct device *dev;
|
||||
struct ingenic_ecc *ecc;
|
||||
const struct jz_soc_info *soc_info;
|
||||
struct nand_controller controller;
|
||||
unsigned int num_banks;
|
||||
struct list_head chips;
|
||||
int selected;
|
||||
struct ingenic_nand_cs cs[];
|
||||
};
|
||||
|
||||
struct ingenic_nand {
|
||||
struct nand_chip chip;
|
||||
struct list_head chip_list;
|
||||
|
||||
struct gpio_desc *busy_gpio;
|
||||
struct gpio_desc *wp_gpio;
|
||||
unsigned int reading: 1;
|
||||
};
|
||||
|
||||
static inline struct ingenic_nand *to_ingenic_nand(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd_to_nand(mtd), struct ingenic_nand, chip);
|
||||
}
|
||||
|
||||
static inline struct ingenic_nfc *to_ingenic_nfc(struct nand_controller *ctrl)
|
||||
{
|
||||
return container_of(ctrl, struct ingenic_nfc, controller);
|
||||
}
|
||||
|
||||
static int qi_lb60_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
if (section || !ecc->total)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->length = ecc->total;
|
||||
oobregion->offset = 12;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qi_lb60_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->length = mtd->oobsize - ecc->total - 12;
|
||||
oobregion->offset = 12 + ecc->total;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct mtd_ooblayout_ops qi_lb60_ooblayout_ops = {
|
||||
.ecc = qi_lb60_ooblayout_ecc,
|
||||
.free = qi_lb60_ooblayout_free,
|
||||
};
|
||||
|
||||
static int jz4725b_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
if (section || !ecc->total)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->length = ecc->total;
|
||||
oobregion->offset = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4725b_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->length = mtd->oobsize - ecc->total - 3;
|
||||
oobregion->offset = 3 + ecc->total;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops jz4725b_ooblayout_ops = {
|
||||
.ecc = jz4725b_ooblayout_ecc,
|
||||
.free = jz4725b_ooblayout_free,
|
||||
};
|
||||
|
||||
static void ingenic_nand_select_chip(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
|
||||
struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller);
|
||||
struct ingenic_nand_cs *cs;
|
||||
|
||||
/* Ensure the currently selected chip is deasserted. */
|
||||
if (chipnr == -1 && nfc->selected >= 0) {
|
||||
cs = &nfc->cs[nfc->selected];
|
||||
jz4780_nemc_assert(nfc->dev, cs->bank, false);
|
||||
}
|
||||
|
||||
nfc->selected = chipnr;
|
||||
}
|
||||
|
||||
static void ingenic_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
|
||||
struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller);
|
||||
struct ingenic_nand_cs *cs;
|
||||
|
||||
if (WARN_ON(nfc->selected < 0))
|
||||
return;
|
||||
|
||||
cs = &nfc->cs[nfc->selected];
|
||||
|
||||
jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE);
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_ALE)
|
||||
writeb(cmd, cs->base + nfc->soc_info->addr_offset);
|
||||
else if (ctrl & NAND_CLE)
|
||||
writeb(cmd, cs->base + nfc->soc_info->cmd_offset);
|
||||
}
|
||||
|
||||
static int ingenic_nand_dev_ready(struct nand_chip *chip)
|
||||
{
|
||||
struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
|
||||
|
||||
return !gpiod_get_value_cansleep(nand->busy_gpio);
|
||||
}
|
||||
|
||||
static void ingenic_nand_ecc_hwctl(struct nand_chip *chip, int mode)
|
||||
{
|
||||
struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
|
||||
|
||||
nand->reading = (mode == NAND_ECC_READ);
|
||||
}
|
||||
|
||||
static int ingenic_nand_ecc_calculate(struct nand_chip *chip, const u8 *dat,
|
||||
u8 *ecc_code)
|
||||
{
|
||||
struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
|
||||
struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller);
|
||||
struct ingenic_ecc_params params;
|
||||
|
||||
/*
|
||||
* Don't need to generate the ECC when reading, the ECC engine does it
|
||||
* for us as part of decoding/correction.
|
||||
*/
|
||||
if (nand->reading)
|
||||
return 0;
|
||||
|
||||
params.size = nand->chip.ecc.size;
|
||||
params.bytes = nand->chip.ecc.bytes;
|
||||
params.strength = nand->chip.ecc.strength;
|
||||
|
||||
return ingenic_ecc_calculate(nfc->ecc, ¶ms, dat, ecc_code);
|
||||
}
|
||||
|
||||
static int ingenic_nand_ecc_correct(struct nand_chip *chip, u8 *dat,
|
||||
u8 *read_ecc, u8 *calc_ecc)
|
||||
{
|
||||
struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
|
||||
struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller);
|
||||
struct ingenic_ecc_params params;
|
||||
|
||||
params.size = nand->chip.ecc.size;
|
||||
params.bytes = nand->chip.ecc.bytes;
|
||||
params.strength = nand->chip.ecc.strength;
|
||||
|
||||
return ingenic_ecc_correct(nfc->ecc, ¶ms, dat, read_ecc);
|
||||
}
|
||||
|
||||
static int ingenic_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct ingenic_nfc *nfc = to_ingenic_nfc(chip->controller);
|
||||
int eccbytes;
|
||||
|
||||
if (chip->ecc.strength == 4) {
|
||||
/* JZ4740 uses 9 bytes of ECC to correct maximum 4 errors */
|
||||
chip->ecc.bytes = 9;
|
||||
} else {
|
||||
chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) *
|
||||
(chip->ecc.strength / 8);
|
||||
}
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
if (!nfc->ecc) {
|
||||
dev_err(nfc->dev, "HW ECC selected, but ECC controller not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip->ecc.hwctl = ingenic_nand_ecc_hwctl;
|
||||
chip->ecc.calculate = ingenic_nand_ecc_calculate;
|
||||
chip->ecc.correct = ingenic_nand_ecc_correct;
|
||||
/* fall through */
|
||||
case NAND_ECC_SOFT:
|
||||
dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n",
|
||||
(nfc->ecc) ? "hardware ECC" : "software ECC",
|
||||
chip->ecc.strength, chip->ecc.size, chip->ecc.bytes);
|
||||
break;
|
||||
case NAND_ECC_NONE:
|
||||
dev_info(nfc->dev, "not using ECC\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(nfc->dev, "ECC mode %d not supported\n",
|
||||
chip->ecc.mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The NAND core will generate the ECC layout for SW ECC */
|
||||
if (chip->ecc.mode != NAND_ECC_HW)
|
||||
return 0;
|
||||
|
||||
/* Generate ECC layout. ECC codes are right aligned in the OOB area. */
|
||||
eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
|
||||
|
||||
if (eccbytes > mtd->oobsize - 2) {
|
||||
dev_err(nfc->dev,
|
||||
"invalid ECC config: required %d ECC bytes, but only %d are available",
|
||||
eccbytes, mtd->oobsize - 2);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The generic layout for BBT markers will most likely overlap with our
|
||||
* ECC bytes in the OOB, so move the BBT markers outside the OOB area.
|
||||
*/
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
/* For legacy reasons we use a different layout on the qi,lb60 board. */
|
||||
if (of_machine_is_compatible("qi,lb60"))
|
||||
mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops);
|
||||
else
|
||||
mtd_set_ooblayout(mtd, nfc->soc_info->oob_layout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops ingenic_nand_controller_ops = {
|
||||
.attach_chip = ingenic_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int ingenic_nand_init_chip(struct platform_device *pdev,
|
||||
struct ingenic_nfc *nfc,
|
||||
struct device_node *np,
|
||||
unsigned int chipnr)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ingenic_nand *nand;
|
||||
struct ingenic_nand_cs *cs;
|
||||
struct resource *res;
|
||||
struct nand_chip *chip;
|
||||
struct mtd_info *mtd;
|
||||
const __be32 *reg;
|
||||
int ret = 0;
|
||||
|
||||
cs = &nfc->cs[chipnr];
|
||||
|
||||
reg = of_get_property(np, "reg", NULL);
|
||||
if (!reg)
|
||||
return -EINVAL;
|
||||
|
||||
cs->bank = be32_to_cpu(*reg);
|
||||
|
||||
jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr);
|
||||
cs->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(cs->base))
|
||||
return PTR_ERR(cs->base);
|
||||
|
||||
nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
|
||||
if (!nand)
|
||||
return -ENOMEM;
|
||||
|
||||
nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN);
|
||||
|
||||
if (IS_ERR(nand->busy_gpio)) {
|
||||
ret = PTR_ERR(nand->busy_gpio);
|
||||
dev_err(dev, "failed to request busy GPIO: %d\n", ret);
|
||||
return ret;
|
||||
} else if (nand->busy_gpio) {
|
||||
nand->chip.legacy.dev_ready = ingenic_nand_dev_ready;
|
||||
}
|
||||
|
||||
nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW);
|
||||
|
||||
if (IS_ERR(nand->wp_gpio)) {
|
||||
ret = PTR_ERR(nand->wp_gpio);
|
||||
dev_err(dev, "failed to request WP GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip = &nand->chip;
|
||||
mtd = nand_to_mtd(chip);
|
||||
mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev),
|
||||
cs->bank);
|
||||
if (!mtd->name)
|
||||
return -ENOMEM;
|
||||
mtd->dev.parent = dev;
|
||||
|
||||
chip->legacy.IO_ADDR_R = cs->base + nfc->soc_info->data_offset;
|
||||
chip->legacy.IO_ADDR_W = cs->base + nfc->soc_info->data_offset;
|
||||
chip->legacy.chip_delay = RB_DELAY_US;
|
||||
chip->options = NAND_NO_SUBPAGE_WRITE;
|
||||
chip->legacy.select_chip = ingenic_nand_select_chip;
|
||||
chip->legacy.cmd_ctrl = ingenic_nand_cmd_ctrl;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->controller = &nfc->controller;
|
||||
nand_set_flash_node(chip, np);
|
||||
|
||||
chip->controller->ops = &ingenic_nand_controller_ops;
|
||||
ret = nand_scan(chip, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
nand_release(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&nand->chip_list, &nfc->chips);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ingenic_nand_cleanup_chips(struct ingenic_nfc *nfc)
|
||||
{
|
||||
struct ingenic_nand *chip;
|
||||
|
||||
while (!list_empty(&nfc->chips)) {
|
||||
chip = list_first_entry(&nfc->chips,
|
||||
struct ingenic_nand, chip_list);
|
||||
nand_release(&chip->chip);
|
||||
list_del(&chip->chip_list);
|
||||
}
|
||||
}
|
||||
|
||||
static int ingenic_nand_init_chips(struct ingenic_nfc *nfc,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np;
|
||||
int i = 0;
|
||||
int ret;
|
||||
int num_chips = of_get_child_count(dev->of_node);
|
||||
|
||||
if (num_chips > nfc->num_banks) {
|
||||
dev_err(dev, "found %d chips but only %d banks\n",
|
||||
num_chips, nfc->num_banks);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_child_of_node(dev->of_node, np) {
|
||||
ret = ingenic_nand_init_chip(pdev, nfc, np, i);
|
||||
if (ret) {
|
||||
ingenic_nand_cleanup_chips(nfc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ingenic_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned int num_banks;
|
||||
struct ingenic_nfc *nfc;
|
||||
int ret;
|
||||
|
||||
num_banks = jz4780_nemc_num_banks(dev);
|
||||
if (num_banks == 0) {
|
||||
dev_err(dev, "no banks found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
nfc = devm_kzalloc(dev, struct_size(nfc, cs, num_banks), GFP_KERNEL);
|
||||
if (!nfc)
|
||||
return -ENOMEM;
|
||||
|
||||
nfc->soc_info = device_get_match_data(dev);
|
||||
if (!nfc->soc_info)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Check for ECC HW before we call nand_scan_ident, to prevent us from
|
||||
* having to call it again if the ECC driver returns -EPROBE_DEFER.
|
||||
*/
|
||||
nfc->ecc = of_ingenic_ecc_get(dev->of_node);
|
||||
if (IS_ERR(nfc->ecc))
|
||||
return PTR_ERR(nfc->ecc);
|
||||
|
||||
nfc->dev = dev;
|
||||
nfc->num_banks = num_banks;
|
||||
|
||||
nand_controller_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
|
||||
ret = ingenic_nand_init_chips(nfc, pdev);
|
||||
if (ret) {
|
||||
if (nfc->ecc)
|
||||
ingenic_ecc_release(nfc->ecc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, nfc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ingenic_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ingenic_nfc *nfc = platform_get_drvdata(pdev);
|
||||
|
||||
if (nfc->ecc)
|
||||
ingenic_ecc_release(nfc->ecc);
|
||||
|
||||
ingenic_nand_cleanup_chips(nfc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct jz_soc_info jz4740_soc_info = {
|
||||
.data_offset = 0x00000000,
|
||||
.cmd_offset = 0x00008000,
|
||||
.addr_offset = 0x00010000,
|
||||
.oob_layout = &nand_ooblayout_lp_ops,
|
||||
};
|
||||
|
||||
static const struct jz_soc_info jz4725b_soc_info = {
|
||||
.data_offset = 0x00000000,
|
||||
.cmd_offset = 0x00008000,
|
||||
.addr_offset = 0x00010000,
|
||||
.oob_layout = &jz4725b_ooblayout_ops,
|
||||
};
|
||||
|
||||
static const struct jz_soc_info jz4780_soc_info = {
|
||||
.data_offset = 0x00000000,
|
||||
.cmd_offset = 0x00400000,
|
||||
.addr_offset = 0x00800000,
|
||||
.oob_layout = &nand_ooblayout_lp_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id ingenic_nand_dt_match[] = {
|
||||
{ .compatible = "ingenic,jz4740-nand", .data = &jz4740_soc_info },
|
||||
{ .compatible = "ingenic,jz4725b-nand", .data = &jz4725b_soc_info },
|
||||
{ .compatible = "ingenic,jz4780-nand", .data = &jz4780_soc_info },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ingenic_nand_dt_match);
|
||||
|
||||
static struct platform_driver ingenic_nand_driver = {
|
||||
.probe = ingenic_nand_probe,
|
||||
.remove = ingenic_nand_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(ingenic_nand_dt_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(ingenic_nand_driver);
|
||||
|
||||
MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
|
||||
MODULE_AUTHOR("Harvey Hunt <harveyhuntnexus@gmail.com>");
|
||||
MODULE_DESCRIPTION("Ingenic JZ47xx NAND driver");
|
||||
MODULE_LICENSE("GPL v2");
|
295
drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
Normal file
295
drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
Normal file
@@ -0,0 +1,295 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* JZ4725B BCH controller driver
|
||||
*
|
||||
* Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
|
||||
*
|
||||
* Based on jz4780_bch.c
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "ingenic_ecc.h"
|
||||
|
||||
#define BCH_BHCR 0x0
|
||||
#define BCH_BHCSR 0x4
|
||||
#define BCH_BHCCR 0x8
|
||||
#define BCH_BHCNT 0xc
|
||||
#define BCH_BHDR 0x10
|
||||
#define BCH_BHPAR0 0x14
|
||||
#define BCH_BHERR0 0x28
|
||||
#define BCH_BHINT 0x24
|
||||
#define BCH_BHINTES 0x3c
|
||||
#define BCH_BHINTEC 0x40
|
||||
#define BCH_BHINTE 0x38
|
||||
|
||||
#define BCH_BHCR_ENCE BIT(3)
|
||||
#define BCH_BHCR_BSEL BIT(2)
|
||||
#define BCH_BHCR_INIT BIT(1)
|
||||
#define BCH_BHCR_BCHE BIT(0)
|
||||
|
||||
#define BCH_BHCNT_DEC_COUNT_SHIFT 16
|
||||
#define BCH_BHCNT_DEC_COUNT_MASK (0x3ff << BCH_BHCNT_DEC_COUNT_SHIFT)
|
||||
#define BCH_BHCNT_ENC_COUNT_SHIFT 0
|
||||
#define BCH_BHCNT_ENC_COUNT_MASK (0x3ff << BCH_BHCNT_ENC_COUNT_SHIFT)
|
||||
|
||||
#define BCH_BHERR_INDEX0_SHIFT 0
|
||||
#define BCH_BHERR_INDEX0_MASK (0x1fff << BCH_BHERR_INDEX0_SHIFT)
|
||||
#define BCH_BHERR_INDEX1_SHIFT 16
|
||||
#define BCH_BHERR_INDEX1_MASK (0x1fff << BCH_BHERR_INDEX1_SHIFT)
|
||||
|
||||
#define BCH_BHINT_ERRC_SHIFT 28
|
||||
#define BCH_BHINT_ERRC_MASK (0xf << BCH_BHINT_ERRC_SHIFT)
|
||||
#define BCH_BHINT_TERRC_SHIFT 16
|
||||
#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT)
|
||||
#define BCH_BHINT_ALL_0 BIT(5)
|
||||
#define BCH_BHINT_ALL_F BIT(4)
|
||||
#define BCH_BHINT_DECF BIT(3)
|
||||
#define BCH_BHINT_ENCF BIT(2)
|
||||
#define BCH_BHINT_UNCOR BIT(1)
|
||||
#define BCH_BHINT_ERR BIT(0)
|
||||
|
||||
/* Timeout for BCH calculation/correction. */
|
||||
#define BCH_TIMEOUT_US 100000
|
||||
|
||||
static inline void jz4725b_bch_config_set(struct ingenic_ecc *bch, u32 cfg)
|
||||
{
|
||||
writel(cfg, bch->base + BCH_BHCSR);
|
||||
}
|
||||
|
||||
static inline void jz4725b_bch_config_clear(struct ingenic_ecc *bch, u32 cfg)
|
||||
{
|
||||
writel(cfg, bch->base + BCH_BHCCR);
|
||||
}
|
||||
|
||||
static int jz4725b_bch_reset(struct ingenic_ecc *bch,
|
||||
struct ingenic_ecc_params *params, bool calc_ecc)
|
||||
{
|
||||
u32 reg, max_value;
|
||||
|
||||
/* Clear interrupt status. */
|
||||
writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
|
||||
|
||||
/* Initialise and enable BCH. */
|
||||
jz4725b_bch_config_clear(bch, 0x1f);
|
||||
jz4725b_bch_config_set(bch, BCH_BHCR_BCHE);
|
||||
|
||||
if (params->strength == 8)
|
||||
jz4725b_bch_config_set(bch, BCH_BHCR_BSEL);
|
||||
else
|
||||
jz4725b_bch_config_clear(bch, BCH_BHCR_BSEL);
|
||||
|
||||
if (calc_ecc) /* calculate ECC from data */
|
||||
jz4725b_bch_config_set(bch, BCH_BHCR_ENCE);
|
||||
else /* correct data from ECC */
|
||||
jz4725b_bch_config_clear(bch, BCH_BHCR_ENCE);
|
||||
|
||||
jz4725b_bch_config_set(bch, BCH_BHCR_INIT);
|
||||
|
||||
max_value = BCH_BHCNT_ENC_COUNT_MASK >> BCH_BHCNT_ENC_COUNT_SHIFT;
|
||||
if (params->size > max_value)
|
||||
return -EINVAL;
|
||||
|
||||
max_value = BCH_BHCNT_DEC_COUNT_MASK >> BCH_BHCNT_DEC_COUNT_SHIFT;
|
||||
if (params->size + params->bytes > max_value)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set up BCH count register. */
|
||||
reg = params->size << BCH_BHCNT_ENC_COUNT_SHIFT;
|
||||
reg |= (params->size + params->bytes) << BCH_BHCNT_DEC_COUNT_SHIFT;
|
||||
writel(reg, bch->base + BCH_BHCNT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4725b_bch_disable(struct ingenic_ecc *bch)
|
||||
{
|
||||
/* Clear interrupts */
|
||||
writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
|
||||
|
||||
/* Disable the hardware */
|
||||
jz4725b_bch_config_clear(bch, BCH_BHCR_BCHE);
|
||||
}
|
||||
|
||||
static void jz4725b_bch_write_data(struct ingenic_ecc *bch, const u8 *buf,
|
||||
size_t size)
|
||||
{
|
||||
while (size--)
|
||||
writeb(*buf++, bch->base + BCH_BHDR);
|
||||
}
|
||||
|
||||
static void jz4725b_bch_read_parity(struct ingenic_ecc *bch, u8 *buf,
|
||||
size_t size)
|
||||
{
|
||||
size_t size32 = size / sizeof(u32);
|
||||
size_t size8 = size % sizeof(u32);
|
||||
u32 *dest32;
|
||||
u8 *dest8;
|
||||
u32 val, offset = 0;
|
||||
|
||||
dest32 = (u32 *)buf;
|
||||
while (size32--) {
|
||||
*dest32++ = readl_relaxed(bch->base + BCH_BHPAR0 + offset);
|
||||
offset += sizeof(u32);
|
||||
}
|
||||
|
||||
dest8 = (u8 *)dest32;
|
||||
val = readl_relaxed(bch->base + BCH_BHPAR0 + offset);
|
||||
switch (size8) {
|
||||
case 3:
|
||||
dest8[2] = (val >> 16) & 0xff;
|
||||
/* fall-through */
|
||||
case 2:
|
||||
dest8[1] = (val >> 8) & 0xff;
|
||||
/* fall-through */
|
||||
case 1:
|
||||
dest8[0] = val & 0xff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int jz4725b_bch_wait_complete(struct ingenic_ecc *bch, unsigned int irq,
|
||||
u32 *status)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* While we could use interrupts here and sleep until the operation
|
||||
* completes, the controller works fairly quickly (usually a few
|
||||
* microseconds) and so the overhead of sleeping until we get an
|
||||
* interrupt quite noticeably decreases performance.
|
||||
*/
|
||||
ret = readl_relaxed_poll_timeout(bch->base + BCH_BHINT, reg,
|
||||
reg & irq, 0, BCH_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (status)
|
||||
*status = reg;
|
||||
|
||||
writel(reg, bch->base + BCH_BHINT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4725b_calculate(struct ingenic_ecc *bch,
|
||||
struct ingenic_ecc_params *params,
|
||||
const u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&bch->lock);
|
||||
|
||||
ret = jz4725b_bch_reset(bch, params, true);
|
||||
if (ret) {
|
||||
dev_err(bch->dev, "Unable to init BCH with given parameters\n");
|
||||
goto out_disable;
|
||||
}
|
||||
|
||||
jz4725b_bch_write_data(bch, buf, params->size);
|
||||
|
||||
ret = jz4725b_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL);
|
||||
if (ret) {
|
||||
dev_err(bch->dev, "timed out while calculating ECC\n");
|
||||
goto out_disable;
|
||||
}
|
||||
|
||||
jz4725b_bch_read_parity(bch, ecc_code, params->bytes);
|
||||
|
||||
out_disable:
|
||||
jz4725b_bch_disable(bch);
|
||||
mutex_unlock(&bch->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jz4725b_correct(struct ingenic_ecc *bch,
|
||||
struct ingenic_ecc_params *params,
|
||||
u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
u32 reg, errors, bit;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&bch->lock);
|
||||
|
||||
ret = jz4725b_bch_reset(bch, params, false);
|
||||
if (ret) {
|
||||
dev_err(bch->dev, "Unable to init BCH with given parameters\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
jz4725b_bch_write_data(bch, buf, params->size);
|
||||
jz4725b_bch_write_data(bch, ecc_code, params->bytes);
|
||||
|
||||
ret = jz4725b_bch_wait_complete(bch, BCH_BHINT_DECF, ®);
|
||||
if (ret) {
|
||||
dev_err(bch->dev, "timed out while correcting data\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (reg & (BCH_BHINT_ALL_F | BCH_BHINT_ALL_0)) {
|
||||
/* Data and ECC is all 0xff or 0x00 - nothing to correct */
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (reg & BCH_BHINT_UNCOR) {
|
||||
/* Uncorrectable ECC error */
|
||||
ret = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
errors = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT;
|
||||
|
||||
/* Correct any detected errors. */
|
||||
for (i = 0; i < errors; i++) {
|
||||
if (i & 1) {
|
||||
bit = (reg & BCH_BHERR_INDEX1_MASK) >> BCH_BHERR_INDEX1_SHIFT;
|
||||
} else {
|
||||
reg = readl(bch->base + BCH_BHERR0 + (i * 4));
|
||||
bit = (reg & BCH_BHERR_INDEX0_MASK) >> BCH_BHERR_INDEX0_SHIFT;
|
||||
}
|
||||
|
||||
buf[(bit >> 3)] ^= BIT(bit & 0x7);
|
||||
}
|
||||
|
||||
out:
|
||||
jz4725b_bch_disable(bch);
|
||||
mutex_unlock(&bch->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ingenic_ecc_ops jz4725b_bch_ops = {
|
||||
.disable = jz4725b_bch_disable,
|
||||
.calculate = jz4725b_calculate,
|
||||
.correct = jz4725b_correct,
|
||||
};
|
||||
|
||||
static const struct of_device_id jz4725b_bch_dt_match[] = {
|
||||
{ .compatible = "ingenic,jz4725b-bch", .data = &jz4725b_bch_ops },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, jz4725b_bch_dt_match);
|
||||
|
||||
static struct platform_driver jz4725b_bch_driver = {
|
||||
.probe = ingenic_ecc_probe,
|
||||
.driver = {
|
||||
.name = "jz4725b-bch",
|
||||
.of_match_table = jz4725b_bch_dt_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(jz4725b_bch_driver);
|
||||
|
||||
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
|
||||
MODULE_DESCRIPTION("Ingenic JZ4725B BCH controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
197
drivers/mtd/nand/raw/ingenic/jz4740_ecc.c
Normal file
197
drivers/mtd/nand/raw/ingenic/jz4740_ecc.c
Normal file
@@ -0,0 +1,197 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* JZ4740 ECC controller driver
|
||||
*
|
||||
* Copyright (c) 2019 Paul Cercueil <paul@crapouillou.net>
|
||||
*
|
||||
* based on jz4740-nand.c
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "ingenic_ecc.h"
|
||||
|
||||
#define JZ_REG_NAND_ECC_CTRL 0x00
|
||||
#define JZ_REG_NAND_DATA 0x04
|
||||
#define JZ_REG_NAND_PAR0 0x08
|
||||
#define JZ_REG_NAND_PAR1 0x0C
|
||||
#define JZ_REG_NAND_PAR2 0x10
|
||||
#define JZ_REG_NAND_IRQ_STAT 0x14
|
||||
#define JZ_REG_NAND_IRQ_CTRL 0x18
|
||||
#define JZ_REG_NAND_ERR(x) (0x1C + ((x) << 2))
|
||||
|
||||
#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4)
|
||||
#define JZ_NAND_ECC_CTRL_ENCODING BIT(3)
|
||||
#define JZ_NAND_ECC_CTRL_RS BIT(2)
|
||||
#define JZ_NAND_ECC_CTRL_RESET BIT(1)
|
||||
#define JZ_NAND_ECC_CTRL_ENABLE BIT(0)
|
||||
|
||||
#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29))
|
||||
#define JZ_NAND_STATUS_PAD_FINISH BIT(4)
|
||||
#define JZ_NAND_STATUS_DEC_FINISH BIT(3)
|
||||
#define JZ_NAND_STATUS_ENC_FINISH BIT(2)
|
||||
#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1)
|
||||
#define JZ_NAND_STATUS_ERROR BIT(0)
|
||||
|
||||
static const uint8_t empty_block_ecc[] = {
|
||||
0xcd, 0x9d, 0x90, 0x58, 0xf4, 0x8b, 0xff, 0xb7, 0x6f
|
||||
};
|
||||
|
||||
static void jz4740_ecc_reset(struct ingenic_ecc *ecc, bool calc_ecc)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Clear interrupt status */
|
||||
writel(0, ecc->base + JZ_REG_NAND_IRQ_STAT);
|
||||
|
||||
/* Initialize and enable ECC hardware */
|
||||
reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
reg |= JZ_NAND_ECC_CTRL_RESET;
|
||||
reg |= JZ_NAND_ECC_CTRL_ENABLE;
|
||||
reg |= JZ_NAND_ECC_CTRL_RS;
|
||||
if (calc_ecc) /* calculate ECC from data */
|
||||
reg |= JZ_NAND_ECC_CTRL_ENCODING;
|
||||
else /* correct data from ECC */
|
||||
reg &= ~JZ_NAND_ECC_CTRL_ENCODING;
|
||||
|
||||
writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
}
|
||||
|
||||
static int jz4740_ecc_calculate(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
const u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
uint32_t reg, status;
|
||||
unsigned int timeout = 1000;
|
||||
int i;
|
||||
|
||||
jz4740_ecc_reset(ecc, true);
|
||||
|
||||
do {
|
||||
status = readl(ecc->base + JZ_REG_NAND_IRQ_STAT);
|
||||
} while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout);
|
||||
|
||||
if (timeout == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
|
||||
writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
|
||||
for (i = 0; i < params->bytes; ++i)
|
||||
ecc_code[i] = readb(ecc->base + JZ_REG_NAND_PAR0 + i);
|
||||
|
||||
/*
|
||||
* If the written data is completely 0xff, we also want to write 0xff as
|
||||
* ECC, otherwise we will get in trouble when doing subpage writes.
|
||||
*/
|
||||
if (memcmp(ecc_code, empty_block_ecc, ARRAY_SIZE(empty_block_ecc)) == 0)
|
||||
memset(ecc_code, 0xff, ARRAY_SIZE(empty_block_ecc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz_nand_correct_data(uint8_t *buf, int index, int mask)
|
||||
{
|
||||
int offset = index & 0x7;
|
||||
uint16_t data;
|
||||
|
||||
index += (index >> 3);
|
||||
|
||||
data = buf[index];
|
||||
data |= buf[index + 1] << 8;
|
||||
|
||||
mask ^= (data >> offset) & 0x1ff;
|
||||
data &= ~(0x1ff << offset);
|
||||
data |= (mask << offset);
|
||||
|
||||
buf[index] = data & 0xff;
|
||||
buf[index + 1] = (data >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static int jz4740_ecc_correct(struct ingenic_ecc *ecc,
|
||||
struct ingenic_ecc_params *params,
|
||||
u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
int i, error_count, index;
|
||||
uint32_t reg, status, error;
|
||||
unsigned int timeout = 1000;
|
||||
|
||||
jz4740_ecc_reset(ecc, false);
|
||||
|
||||
for (i = 0; i < params->bytes; ++i)
|
||||
writeb(ecc_code[i], ecc->base + JZ_REG_NAND_PAR0 + i);
|
||||
|
||||
reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
reg |= JZ_NAND_ECC_CTRL_PAR_READY;
|
||||
writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
|
||||
do {
|
||||
status = readl(ecc->base + JZ_REG_NAND_IRQ_STAT);
|
||||
} while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout);
|
||||
|
||||
if (timeout == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
|
||||
writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
|
||||
if (status & JZ_NAND_STATUS_ERROR) {
|
||||
if (status & JZ_NAND_STATUS_UNCOR_ERROR)
|
||||
return -EBADMSG;
|
||||
|
||||
error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29;
|
||||
|
||||
for (i = 0; i < error_count; ++i) {
|
||||
error = readl(ecc->base + JZ_REG_NAND_ERR(i));
|
||||
index = ((error >> 16) & 0x1ff) - 1;
|
||||
if (index >= 0 && index < params->size)
|
||||
jz_nand_correct_data(buf, index, error & 0x1ff);
|
||||
}
|
||||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4740_ecc_disable(struct ingenic_ecc *ecc)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
writel(0, ecc->base + JZ_REG_NAND_IRQ_STAT);
|
||||
reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
|
||||
writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
|
||||
}
|
||||
|
||||
static const struct ingenic_ecc_ops jz4740_ecc_ops = {
|
||||
.disable = jz4740_ecc_disable,
|
||||
.calculate = jz4740_ecc_calculate,
|
||||
.correct = jz4740_ecc_correct,
|
||||
};
|
||||
|
||||
static const struct of_device_id jz4740_ecc_dt_match[] = {
|
||||
{ .compatible = "ingenic,jz4740-ecc", .data = &jz4740_ecc_ops },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, jz4740_ecc_dt_match);
|
||||
|
||||
static struct platform_driver jz4740_ecc_driver = {
|
||||
.probe = ingenic_ecc_probe,
|
||||
.driver = {
|
||||
.name = "jz4740-ecc",
|
||||
.of_match_table = jz4740_ecc_dt_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(jz4740_ecc_driver);
|
||||
|
||||
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
|
||||
MODULE_DESCRIPTION("Ingenic JZ4740 ECC controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -313,8 +313,11 @@ static int jz_nand_detect_bank(struct platform_device *pdev,
|
||||
uint32_t ctrl;
|
||||
struct nand_chip *chip = &nand->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
u8 id[2];
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/* Request I/O resource. */
|
||||
sprintf(res_name, "bank%d", bank);
|
||||
ret = jz_nand_ioremap_resource(pdev, res_name,
|
||||
@@ -351,8 +354,8 @@ static int jz_nand_detect_bank(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
/* Update size of the MTD. */
|
||||
chip->numchips++;
|
||||
mtd->size += chip->chipsize;
|
||||
memorg->ntargets++;
|
||||
mtd->size += nanddev_target_size(&chip->base);
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Found chip %zu on bank %i\n", chipnr, bank);
|
@@ -1,28 +1,22 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* JZ4780 BCH controller
|
||||
* JZ4780 BCH controller driver
|
||||
*
|
||||
* Copyright (c) 2015 Imagination Technologies
|
||||
* Author: Alex Smith <alex.smith@imgtec.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 <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "jz4780_bch.h"
|
||||
#include "ingenic_ecc.h"
|
||||
|
||||
#define BCH_BHCR 0x0
|
||||
#define BCH_BHCCR 0x8
|
||||
@@ -65,15 +59,8 @@
|
||||
/* Timeout for BCH calculation/correction. */
|
||||
#define BCH_TIMEOUT_US 100000
|
||||
|
||||
struct jz4780_bch {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static void jz4780_bch_init(struct jz4780_bch *bch,
|
||||
struct jz4780_bch_params *params, bool encode)
|
||||
static void jz4780_bch_reset(struct ingenic_ecc *bch,
|
||||
struct ingenic_ecc_params *params, bool encode)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
@@ -93,13 +80,13 @@ static void jz4780_bch_init(struct jz4780_bch *bch,
|
||||
writel(reg, bch->base + BCH_BHCR);
|
||||
}
|
||||
|
||||
static void jz4780_bch_disable(struct jz4780_bch *bch)
|
||||
static void jz4780_bch_disable(struct ingenic_ecc *bch)
|
||||
{
|
||||
writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
|
||||
writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR);
|
||||
}
|
||||
|
||||
static void jz4780_bch_write_data(struct jz4780_bch *bch, const void *buf,
|
||||
static void jz4780_bch_write_data(struct ingenic_ecc *bch, const void *buf,
|
||||
size_t size)
|
||||
{
|
||||
size_t size32 = size / sizeof(u32);
|
||||
@@ -116,7 +103,7 @@ static void jz4780_bch_write_data(struct jz4780_bch *bch, const void *buf,
|
||||
writeb(*src8++, bch->base + BCH_BHDR);
|
||||
}
|
||||
|
||||
static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf,
|
||||
static void jz4780_bch_read_parity(struct ingenic_ecc *bch, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
size_t size32 = size / sizeof(u32);
|
||||
@@ -146,7 +133,7 @@ static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf,
|
||||
}
|
||||
}
|
||||
|
||||
static bool jz4780_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq,
|
||||
static bool jz4780_bch_wait_complete(struct ingenic_ecc *bch, unsigned int irq,
|
||||
u32 *status)
|
||||
{
|
||||
u32 reg;
|
||||
@@ -170,23 +157,15 @@ static bool jz4780_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq,
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* jz4780_bch_calculate() - calculate ECC for a data buffer
|
||||
* @bch: BCH device.
|
||||
* @params: BCH parameters.
|
||||
* @buf: input buffer with raw data.
|
||||
* @ecc_code: output buffer with ECC.
|
||||
*
|
||||
* Return: 0 on success, -ETIMEDOUT if timed out while waiting for BCH
|
||||
* controller.
|
||||
*/
|
||||
int jz4780_bch_calculate(struct jz4780_bch *bch, struct jz4780_bch_params *params,
|
||||
const u8 *buf, u8 *ecc_code)
|
||||
static int jz4780_calculate(struct ingenic_ecc *bch,
|
||||
struct ingenic_ecc_params *params,
|
||||
const u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&bch->lock);
|
||||
jz4780_bch_init(bch, params, true);
|
||||
|
||||
jz4780_bch_reset(bch, params, true);
|
||||
jz4780_bch_write_data(bch, buf, params->size);
|
||||
|
||||
if (jz4780_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) {
|
||||
@@ -200,30 +179,17 @@ int jz4780_bch_calculate(struct jz4780_bch *bch, struct jz4780_bch_params *param
|
||||
mutex_unlock(&bch->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(jz4780_bch_calculate);
|
||||
|
||||
/**
|
||||
* jz4780_bch_correct() - detect and correct bit errors
|
||||
* @bch: BCH device.
|
||||
* @params: BCH parameters.
|
||||
* @buf: raw data read from the chip.
|
||||
* @ecc_code: ECC read from the chip.
|
||||
*
|
||||
* Given the raw data and the ECC read from the NAND device, detects and
|
||||
* corrects errors in the data.
|
||||
*
|
||||
* Return: the number of bit errors corrected, -EBADMSG if there are too many
|
||||
* errors to correct or -ETIMEDOUT if we timed out waiting for the controller.
|
||||
*/
|
||||
int jz4780_bch_correct(struct jz4780_bch *bch, struct jz4780_bch_params *params,
|
||||
u8 *buf, u8 *ecc_code)
|
||||
static int jz4780_correct(struct ingenic_ecc *bch,
|
||||
struct ingenic_ecc_params *params,
|
||||
u8 *buf, u8 *ecc_code)
|
||||
{
|
||||
u32 reg, mask, index;
|
||||
int i, ret, count;
|
||||
|
||||
mutex_lock(&bch->lock);
|
||||
|
||||
jz4780_bch_init(bch, params, false);
|
||||
jz4780_bch_reset(bch, params, false);
|
||||
jz4780_bch_write_data(bch, buf, params->size);
|
||||
jz4780_bch_write_data(bch, ecc_code, params->bytes);
|
||||
|
||||
@@ -262,110 +228,30 @@ out:
|
||||
mutex_unlock(&bch->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(jz4780_bch_correct);
|
||||
|
||||
/**
|
||||
* jz4780_bch_get() - get the BCH controller device
|
||||
* @np: BCH device tree node.
|
||||
*
|
||||
* Gets the BCH controller device from the specified device tree node. The
|
||||
* device must be released with jz4780_bch_release() when it is no longer being
|
||||
* used.
|
||||
*
|
||||
* Return: a pointer to jz4780_bch, errors are encoded into the pointer.
|
||||
* PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
|
||||
*/
|
||||
static struct jz4780_bch *jz4780_bch_get(struct device_node *np)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct jz4780_bch *bch;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
bch = platform_get_drvdata(pdev);
|
||||
if (!bch) {
|
||||
put_device(&pdev->dev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
clk_prepare_enable(bch->clk);
|
||||
|
||||
return bch;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_jz4780_bch_get() - get the BCH controller from a DT node
|
||||
* @of_node: the node that contains a bch-controller property.
|
||||
*
|
||||
* Get the bch-controller property from the given device tree
|
||||
* node and pass it to jz4780_bch_get to do the work.
|
||||
*
|
||||
* Return: a pointer to jz4780_bch, errors are encoded into the pointer.
|
||||
* PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
|
||||
*/
|
||||
struct jz4780_bch *of_jz4780_bch_get(struct device_node *of_node)
|
||||
{
|
||||
struct jz4780_bch *bch = NULL;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(of_node, "ingenic,bch-controller", 0);
|
||||
|
||||
if (np) {
|
||||
bch = jz4780_bch_get(np);
|
||||
of_node_put(np);
|
||||
}
|
||||
return bch;
|
||||
}
|
||||
EXPORT_SYMBOL(of_jz4780_bch_get);
|
||||
|
||||
/**
|
||||
* jz4780_bch_release() - release the BCH controller device
|
||||
* @bch: BCH device.
|
||||
*/
|
||||
void jz4780_bch_release(struct jz4780_bch *bch)
|
||||
{
|
||||
clk_disable_unprepare(bch->clk);
|
||||
put_device(bch->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(jz4780_bch_release);
|
||||
|
||||
static int jz4780_bch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct jz4780_bch *bch;
|
||||
struct resource *res;
|
||||
struct ingenic_ecc *bch;
|
||||
int ret;
|
||||
|
||||
bch = devm_kzalloc(dev, sizeof(*bch), GFP_KERNEL);
|
||||
if (!bch)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
bch->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(bch->base))
|
||||
return PTR_ERR(bch->base);
|
||||
|
||||
jz4780_bch_disable(bch);
|
||||
|
||||
bch->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(bch->clk)) {
|
||||
dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(bch->clk));
|
||||
return PTR_ERR(bch->clk);
|
||||
}
|
||||
ret = ingenic_ecc_probe(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bch = platform_get_drvdata(pdev);
|
||||
clk_set_rate(bch->clk, BCH_CLK_RATE);
|
||||
|
||||
mutex_init(&bch->lock);
|
||||
|
||||
bch->dev = dev;
|
||||
platform_set_drvdata(pdev, bch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ingenic_ecc_ops jz4780_bch_ops = {
|
||||
.disable = jz4780_bch_disable,
|
||||
.calculate = jz4780_calculate,
|
||||
.correct = jz4780_correct,
|
||||
};
|
||||
|
||||
static const struct of_device_id jz4780_bch_dt_match[] = {
|
||||
{ .compatible = "ingenic,jz4780-bch" },
|
||||
{ .compatible = "ingenic,jz4780-bch", .data = &jz4780_bch_ops },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, jz4780_bch_dt_match);
|
@@ -76,6 +76,7 @@ extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
|
||||
|
||||
/* Core functions */
|
||||
const struct nand_manufacturer *nand_get_manufacturer(u8 id);
|
||||
int nand_bbm_get_next_page(struct nand_chip *chip, int page);
|
||||
int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs);
|
||||
int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
@@ -110,7 +111,7 @@ static inline int nand_exec_op(struct nand_chip *chip,
|
||||
if (!nand_has_exec_op(chip))
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (WARN_ON(op->cs >= chip->numchips))
|
||||
if (WARN_ON(op->cs >= nanddev_ntargets(&chip->base)))
|
||||
return -EINVAL;
|
||||
|
||||
return chip->controller->ops->exec_op(chip, op, false);
|
||||
|
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* JZ4780 BCH controller
|
||||
*
|
||||
* Copyright (c) 2015 Imagination Technologies
|
||||
* Author: Alex Smith <alex.smith@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_MTD_NAND_JZ4780_BCH_H__
|
||||
#define __DRIVERS_MTD_NAND_JZ4780_BCH_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device;
|
||||
struct device_node;
|
||||
struct jz4780_bch;
|
||||
|
||||
/**
|
||||
* struct jz4780_bch_params - BCH parameters
|
||||
* @size: data bytes per ECC step.
|
||||
* @bytes: ECC bytes per step.
|
||||
* @strength: number of correctable bits per ECC step.
|
||||
*/
|
||||
struct jz4780_bch_params {
|
||||
int size;
|
||||
int bytes;
|
||||
int strength;
|
||||
};
|
||||
|
||||
int jz4780_bch_calculate(struct jz4780_bch *bch,
|
||||
struct jz4780_bch_params *params,
|
||||
const u8 *buf, u8 *ecc_code);
|
||||
int jz4780_bch_correct(struct jz4780_bch *bch,
|
||||
struct jz4780_bch_params *params, u8 *buf,
|
||||
u8 *ecc_code);
|
||||
|
||||
void jz4780_bch_release(struct jz4780_bch *bch);
|
||||
struct jz4780_bch *of_jz4780_bch_get(struct device_node *np);
|
||||
|
||||
#endif /* __DRIVERS_MTD_NAND_JZ4780_BCH_H__ */
|
@@ -1,415 +0,0 @@
|
||||
/*
|
||||
* JZ4780 NAND driver
|
||||
*
|
||||
* Copyright (c) 2015 Imagination Technologies
|
||||
* Author: Alex Smith <alex.smith@imgtec.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 <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <linux/jz4780-nemc.h>
|
||||
|
||||
#include "jz4780_bch.h"
|
||||
|
||||
#define DRV_NAME "jz4780-nand"
|
||||
|
||||
#define OFFSET_DATA 0x00000000
|
||||
#define OFFSET_CMD 0x00400000
|
||||
#define OFFSET_ADDR 0x00800000
|
||||
|
||||
/* Command delay when there is no R/B pin. */
|
||||
#define RB_DELAY_US 100
|
||||
|
||||
struct jz4780_nand_cs {
|
||||
unsigned int bank;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
struct jz4780_nand_controller {
|
||||
struct device *dev;
|
||||
struct jz4780_bch *bch;
|
||||
struct nand_controller controller;
|
||||
unsigned int num_banks;
|
||||
struct list_head chips;
|
||||
int selected;
|
||||
struct jz4780_nand_cs cs[];
|
||||
};
|
||||
|
||||
struct jz4780_nand_chip {
|
||||
struct nand_chip chip;
|
||||
struct list_head chip_list;
|
||||
|
||||
struct gpio_desc *busy_gpio;
|
||||
struct gpio_desc *wp_gpio;
|
||||
unsigned int reading: 1;
|
||||
};
|
||||
|
||||
static inline struct jz4780_nand_chip *to_jz4780_nand_chip(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd_to_nand(mtd), struct jz4780_nand_chip, chip);
|
||||
}
|
||||
|
||||
static inline struct jz4780_nand_controller
|
||||
*to_jz4780_nand_controller(struct nand_controller *ctrl)
|
||||
{
|
||||
return container_of(ctrl, struct jz4780_nand_controller, controller);
|
||||
}
|
||||
|
||||
static void jz4780_nand_select_chip(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
struct jz4780_nand_chip *nand = to_jz4780_nand_chip(nand_to_mtd(chip));
|
||||
struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
|
||||
struct jz4780_nand_cs *cs;
|
||||
|
||||
/* Ensure the currently selected chip is deasserted. */
|
||||
if (chipnr == -1 && nfc->selected >= 0) {
|
||||
cs = &nfc->cs[nfc->selected];
|
||||
jz4780_nemc_assert(nfc->dev, cs->bank, false);
|
||||
}
|
||||
|
||||
nfc->selected = chipnr;
|
||||
}
|
||||
|
||||
static void jz4780_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct jz4780_nand_chip *nand = to_jz4780_nand_chip(nand_to_mtd(chip));
|
||||
struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
|
||||
struct jz4780_nand_cs *cs;
|
||||
|
||||
if (WARN_ON(nfc->selected < 0))
|
||||
return;
|
||||
|
||||
cs = &nfc->cs[nfc->selected];
|
||||
|
||||
jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE);
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_ALE)
|
||||
writeb(cmd, cs->base + OFFSET_ADDR);
|
||||
else if (ctrl & NAND_CLE)
|
||||
writeb(cmd, cs->base + OFFSET_CMD);
|
||||
}
|
||||
|
||||
static int jz4780_nand_dev_ready(struct nand_chip *chip)
|
||||
{
|
||||
struct jz4780_nand_chip *nand = to_jz4780_nand_chip(nand_to_mtd(chip));
|
||||
|
||||
return !gpiod_get_value_cansleep(nand->busy_gpio);
|
||||
}
|
||||
|
||||
static void jz4780_nand_ecc_hwctl(struct nand_chip *chip, int mode)
|
||||
{
|
||||
struct jz4780_nand_chip *nand = to_jz4780_nand_chip(nand_to_mtd(chip));
|
||||
|
||||
nand->reading = (mode == NAND_ECC_READ);
|
||||
}
|
||||
|
||||
static int jz4780_nand_ecc_calculate(struct nand_chip *chip, const u8 *dat,
|
||||
u8 *ecc_code)
|
||||
{
|
||||
struct jz4780_nand_chip *nand = to_jz4780_nand_chip(nand_to_mtd(chip));
|
||||
struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
|
||||
struct jz4780_bch_params params;
|
||||
|
||||
/*
|
||||
* Don't need to generate the ECC when reading, BCH does it for us as
|
||||
* part of decoding/correction.
|
||||
*/
|
||||
if (nand->reading)
|
||||
return 0;
|
||||
|
||||
params.size = nand->chip.ecc.size;
|
||||
params.bytes = nand->chip.ecc.bytes;
|
||||
params.strength = nand->chip.ecc.strength;
|
||||
|
||||
return jz4780_bch_calculate(nfc->bch, ¶ms, dat, ecc_code);
|
||||
}
|
||||
|
||||
static int jz4780_nand_ecc_correct(struct nand_chip *chip, u8 *dat,
|
||||
u8 *read_ecc, u8 *calc_ecc)
|
||||
{
|
||||
struct jz4780_nand_chip *nand = to_jz4780_nand_chip(nand_to_mtd(chip));
|
||||
struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
|
||||
struct jz4780_bch_params params;
|
||||
|
||||
params.size = nand->chip.ecc.size;
|
||||
params.bytes = nand->chip.ecc.bytes;
|
||||
params.strength = nand->chip.ecc.strength;
|
||||
|
||||
return jz4780_bch_correct(nfc->bch, ¶ms, dat, read_ecc);
|
||||
}
|
||||
|
||||
static int jz4780_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller);
|
||||
int eccbytes;
|
||||
|
||||
chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) *
|
||||
(chip->ecc.strength / 8);
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
if (!nfc->bch) {
|
||||
dev_err(nfc->dev,
|
||||
"HW BCH selected, but BCH controller not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip->ecc.hwctl = jz4780_nand_ecc_hwctl;
|
||||
chip->ecc.calculate = jz4780_nand_ecc_calculate;
|
||||
chip->ecc.correct = jz4780_nand_ecc_correct;
|
||||
/* fall through */
|
||||
case NAND_ECC_SOFT:
|
||||
dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n",
|
||||
(nfc->bch) ? "hardware BCH" : "software ECC",
|
||||
chip->ecc.strength, chip->ecc.size, chip->ecc.bytes);
|
||||
break;
|
||||
case NAND_ECC_NONE:
|
||||
dev_info(nfc->dev, "not using ECC\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(nfc->dev, "ECC mode %d not supported\n",
|
||||
chip->ecc.mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The NAND core will generate the ECC layout for SW ECC */
|
||||
if (chip->ecc.mode != NAND_ECC_HW)
|
||||
return 0;
|
||||
|
||||
/* Generate ECC layout. ECC codes are right aligned in the OOB area. */
|
||||
eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
|
||||
|
||||
if (eccbytes > mtd->oobsize - 2) {
|
||||
dev_err(nfc->dev,
|
||||
"invalid ECC config: required %d ECC bytes, but only %d are available",
|
||||
eccbytes, mtd->oobsize - 2);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops jz4780_nand_controller_ops = {
|
||||
.attach_chip = jz4780_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int jz4780_nand_init_chip(struct platform_device *pdev,
|
||||
struct jz4780_nand_controller *nfc,
|
||||
struct device_node *np,
|
||||
unsigned int chipnr)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct jz4780_nand_chip *nand;
|
||||
struct jz4780_nand_cs *cs;
|
||||
struct resource *res;
|
||||
struct nand_chip *chip;
|
||||
struct mtd_info *mtd;
|
||||
const __be32 *reg;
|
||||
int ret = 0;
|
||||
|
||||
cs = &nfc->cs[chipnr];
|
||||
|
||||
reg = of_get_property(np, "reg", NULL);
|
||||
if (!reg)
|
||||
return -EINVAL;
|
||||
|
||||
cs->bank = be32_to_cpu(*reg);
|
||||
|
||||
jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr);
|
||||
cs->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(cs->base))
|
||||
return PTR_ERR(cs->base);
|
||||
|
||||
nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
|
||||
if (!nand)
|
||||
return -ENOMEM;
|
||||
|
||||
nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN);
|
||||
|
||||
if (IS_ERR(nand->busy_gpio)) {
|
||||
ret = PTR_ERR(nand->busy_gpio);
|
||||
dev_err(dev, "failed to request busy GPIO: %d\n", ret);
|
||||
return ret;
|
||||
} else if (nand->busy_gpio) {
|
||||
nand->chip.legacy.dev_ready = jz4780_nand_dev_ready;
|
||||
}
|
||||
|
||||
nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW);
|
||||
|
||||
if (IS_ERR(nand->wp_gpio)) {
|
||||
ret = PTR_ERR(nand->wp_gpio);
|
||||
dev_err(dev, "failed to request WP GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip = &nand->chip;
|
||||
mtd = nand_to_mtd(chip);
|
||||
mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev),
|
||||
cs->bank);
|
||||
if (!mtd->name)
|
||||
return -ENOMEM;
|
||||
mtd->dev.parent = dev;
|
||||
|
||||
chip->legacy.IO_ADDR_R = cs->base + OFFSET_DATA;
|
||||
chip->legacy.IO_ADDR_W = cs->base + OFFSET_DATA;
|
||||
chip->legacy.chip_delay = RB_DELAY_US;
|
||||
chip->options = NAND_NO_SUBPAGE_WRITE;
|
||||
chip->legacy.select_chip = jz4780_nand_select_chip;
|
||||
chip->legacy.cmd_ctrl = jz4780_nand_cmd_ctrl;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->controller = &nfc->controller;
|
||||
nand_set_flash_node(chip, np);
|
||||
|
||||
chip->controller->ops = &jz4780_nand_controller_ops;
|
||||
ret = nand_scan(chip, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
nand_release(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&nand->chip_list, &nfc->chips);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4780_nand_cleanup_chips(struct jz4780_nand_controller *nfc)
|
||||
{
|
||||
struct jz4780_nand_chip *chip;
|
||||
|
||||
while (!list_empty(&nfc->chips)) {
|
||||
chip = list_first_entry(&nfc->chips, struct jz4780_nand_chip, chip_list);
|
||||
nand_release(&chip->chip);
|
||||
list_del(&chip->chip_list);
|
||||
}
|
||||
}
|
||||
|
||||
static int jz4780_nand_init_chips(struct jz4780_nand_controller *nfc,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np;
|
||||
int i = 0;
|
||||
int ret;
|
||||
int num_chips = of_get_child_count(dev->of_node);
|
||||
|
||||
if (num_chips > nfc->num_banks) {
|
||||
dev_err(dev, "found %d chips but only %d banks\n", num_chips, nfc->num_banks);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_child_of_node(dev->of_node, np) {
|
||||
ret = jz4780_nand_init_chip(pdev, nfc, np, i);
|
||||
if (ret) {
|
||||
jz4780_nand_cleanup_chips(nfc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4780_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned int num_banks;
|
||||
struct jz4780_nand_controller *nfc;
|
||||
int ret;
|
||||
|
||||
num_banks = jz4780_nemc_num_banks(dev);
|
||||
if (num_banks == 0) {
|
||||
dev_err(dev, "no banks found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
nfc = devm_kzalloc(dev, struct_size(nfc, cs, num_banks), GFP_KERNEL);
|
||||
if (!nfc)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Check for BCH HW before we call nand_scan_ident, to prevent us from
|
||||
* having to call it again if the BCH driver returns -EPROBE_DEFER.
|
||||
*/
|
||||
nfc->bch = of_jz4780_bch_get(dev->of_node);
|
||||
if (IS_ERR(nfc->bch))
|
||||
return PTR_ERR(nfc->bch);
|
||||
|
||||
nfc->dev = dev;
|
||||
nfc->num_banks = num_banks;
|
||||
|
||||
nand_controller_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
|
||||
ret = jz4780_nand_init_chips(nfc, pdev);
|
||||
if (ret) {
|
||||
if (nfc->bch)
|
||||
jz4780_bch_release(nfc->bch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, nfc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4780_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct jz4780_nand_controller *nfc = platform_get_drvdata(pdev);
|
||||
|
||||
if (nfc->bch)
|
||||
jz4780_bch_release(nfc->bch);
|
||||
|
||||
jz4780_nand_cleanup_chips(nfc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id jz4780_nand_dt_match[] = {
|
||||
{ .compatible = "ingenic,jz4780-nand" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, jz4780_nand_dt_match);
|
||||
|
||||
static struct platform_driver jz4780_nand_driver = {
|
||||
.probe = jz4780_nand_probe,
|
||||
.remove = jz4780_nand_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(jz4780_nand_dt_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(jz4780_nand_driver);
|
||||
|
||||
MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
|
||||
MODULE_AUTHOR("Harvey Hunt <harveyhuntnexus@gmail.com>");
|
||||
MODULE_DESCRIPTION("Ingenic JZ4780 NAND driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -1083,12 +1083,11 @@ static int marvell_nfc_hw_ecc_hmg_read_page(struct nand_chip *chip, u8 *buf,
|
||||
*/
|
||||
static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct nand_chip *chip, int page)
|
||||
{
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
u8 *buf = nand_get_data_buf(chip);
|
||||
|
||||
marvell_nfc_select_target(chip, chip->cur_cs);
|
||||
return marvell_nfc_hw_ecc_hmg_do_read_page(chip, chip->data_buf,
|
||||
chip->oob_poi, true, page);
|
||||
return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi,
|
||||
true, page);
|
||||
}
|
||||
|
||||
/* Hamming write helpers */
|
||||
@@ -1179,15 +1178,13 @@ static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 *buf = nand_get_data_buf(chip);
|
||||
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
|
||||
memset(chip->data_buf, 0xFF, mtd->writesize);
|
||||
memset(buf, 0xFF, mtd->writesize);
|
||||
|
||||
marvell_nfc_select_target(chip, chip->cur_cs);
|
||||
return marvell_nfc_hw_ecc_hmg_do_write_page(chip, chip->data_buf,
|
||||
chip->oob_poi, true, page);
|
||||
return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi,
|
||||
true, page);
|
||||
}
|
||||
|
||||
/* BCH read helpers */
|
||||
@@ -1434,18 +1431,16 @@ static int marvell_nfc_hw_ecc_bch_read_page(struct nand_chip *chip,
|
||||
|
||||
static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct nand_chip *chip, int page)
|
||||
{
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
u8 *buf = nand_get_data_buf(chip);
|
||||
|
||||
return chip->ecc.read_page_raw(chip, chip->data_buf, true, page);
|
||||
return chip->ecc.read_page_raw(chip, buf, true, page);
|
||||
}
|
||||
|
||||
static int marvell_nfc_hw_ecc_bch_read_oob(struct nand_chip *chip, int page)
|
||||
{
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
u8 *buf = nand_get_data_buf(chip);
|
||||
|
||||
return chip->ecc.read_page(chip, chip->data_buf, true, page);
|
||||
return chip->ecc.read_page(chip, buf, true, page);
|
||||
}
|
||||
|
||||
/* BCH write helpers */
|
||||
@@ -1619,25 +1614,21 @@ static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 *buf = nand_get_data_buf(chip);
|
||||
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
memset(buf, 0xFF, mtd->writesize);
|
||||
|
||||
memset(chip->data_buf, 0xFF, mtd->writesize);
|
||||
|
||||
return chip->ecc.write_page_raw(chip, chip->data_buf, true, page);
|
||||
return chip->ecc.write_page_raw(chip, buf, true, page);
|
||||
}
|
||||
|
||||
static int marvell_nfc_hw_ecc_bch_write_oob(struct nand_chip *chip, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 *buf = nand_get_data_buf(chip);
|
||||
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
memset(buf, 0xFF, mtd->writesize);
|
||||
|
||||
memset(chip->data_buf, 0xFF, mtd->writesize);
|
||||
|
||||
return chip->ecc.write_page(chip, chip->data_buf, true, page);
|
||||
return chip->ecc.write_page(chip, buf, true, page);
|
||||
}
|
||||
|
||||
/* NAND framework ->exec_op() hooks and related helpers */
|
||||
@@ -2257,9 +2248,9 @@ static int marvell_nand_ecc_init(struct mtd_info *mtd,
|
||||
int ret;
|
||||
|
||||
if (ecc->mode != NAND_ECC_NONE && (!ecc->size || !ecc->strength)) {
|
||||
if (chip->ecc_step_ds && chip->ecc_strength_ds) {
|
||||
ecc->size = chip->ecc_step_ds;
|
||||
ecc->strength = chip->ecc_strength_ds;
|
||||
if (chip->base.eccreq.step_size && chip->base.eccreq.strength) {
|
||||
ecc->size = chip->base.eccreq.step_size;
|
||||
ecc->strength = chip->base.eccreq.strength;
|
||||
} else {
|
||||
dev_info(nfc->dev,
|
||||
"No minimum ECC strength, using 1b/512B\n");
|
||||
@@ -2989,7 +2980,7 @@ static int __maybe_unused marvell_nfc_resume(struct device *dev)
|
||||
|
||||
/*
|
||||
* Reset nfc->selected_chip so the next command will cause the timing
|
||||
* registers to be restored in marvell_nfc_select_chip().
|
||||
* registers to be restored in marvell_nfc_select_target().
|
||||
*/
|
||||
nfc->selected_chip = NULL;
|
||||
|
||||
|
@@ -400,7 +400,7 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
|
||||
cfg |= NFC_RB_IRQ_EN;
|
||||
writel(cfg, nfc->reg_base + NFC_REG_CFG);
|
||||
|
||||
init_completion(&nfc->completion);
|
||||
reinit_completion(&nfc->completion);
|
||||
|
||||
/* use the max erase time as the maximum clock for waiting R/B */
|
||||
cmd = NFC_CMD_RB | NFC_CMD_RB_INT
|
||||
@@ -470,15 +470,15 @@ static int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, u8 *databuf,
|
||||
int datalen, u8 *infobuf, int infolen,
|
||||
static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf,
|
||||
int datalen, void *infobuf, int infolen,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct meson_nfc *nfc = nand_get_controller_data(nand);
|
||||
u32 cmd;
|
||||
int ret = 0;
|
||||
|
||||
nfc->daddr = dma_map_single(nfc->dev, (void *)databuf, datalen, dir);
|
||||
nfc->daddr = dma_map_single(nfc->dev, databuf, datalen, dir);
|
||||
ret = dma_mapping_error(nfc->dev, nfc->daddr);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "DMA mapping error\n");
|
||||
@@ -528,10 +528,13 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
|
||||
u8 *info;
|
||||
|
||||
info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = meson_nfc_dma_buffer_setup(nand, buf, len, info,
|
||||
PER_INFO_BYTE, DMA_FROM_DEVICE);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
cmd = NFC_CMD_N2M | (len & GENMASK(5, 0));
|
||||
writel(cmd, nfc->reg_base + NFC_REG_CMD);
|
||||
@@ -539,6 +542,8 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
|
||||
meson_nfc_drain_cmd(nfc);
|
||||
meson_nfc_wait_cmd_finish(nfc, 1000);
|
||||
meson_nfc_dma_buffer_release(nand, len, PER_INFO_BYTE, DMA_FROM_DEVICE);
|
||||
|
||||
out:
|
||||
kfree(info);
|
||||
|
||||
return ret;
|
||||
@@ -640,7 +645,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
|
||||
return ret;
|
||||
|
||||
ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
|
||||
data_len, (u8 *)meson_chip->info_buf,
|
||||
data_len, meson_chip->info_buf,
|
||||
info_len, DMA_TO_DEVICE);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -724,7 +729,7 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
|
||||
return ret;
|
||||
|
||||
ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
|
||||
data_len, (u8 *)meson_chip->info_buf,
|
||||
data_len, meson_chip->info_buf,
|
||||
info_len, DMA_FROM_DEVICE);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1183,6 +1188,8 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtd_set_ooblayout(mtd, &meson_ooblayout_ops);
|
||||
|
||||
ret = meson_nand_bch_mode(nand);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
@@ -1226,17 +1233,13 @@ meson_nfc_nand_chip_init(struct device *dev,
|
||||
int ret, i;
|
||||
u32 tmp, nsels;
|
||||
|
||||
if (!of_get_property(np, "reg", &nsels))
|
||||
return -EINVAL;
|
||||
|
||||
nsels /= sizeof(u32);
|
||||
nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32));
|
||||
if (!nsels || nsels > MAX_CE_NUM) {
|
||||
dev_err(dev, "invalid register property size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
meson_chip = devm_kzalloc(dev,
|
||||
sizeof(*meson_chip) + (nsels * sizeof(u8)),
|
||||
meson_chip = devm_kzalloc(dev, struct_size(meson_chip, sels, nsels),
|
||||
GFP_KERNEL);
|
||||
if (!meson_chip)
|
||||
return -ENOMEM;
|
||||
@@ -1377,6 +1380,7 @@ static int meson_nfc_probe(struct platform_device *pdev)
|
||||
|
||||
nand_controller_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
init_completion(&nfc->completion);
|
||||
|
||||
nfc->dev = dev;
|
||||
|
||||
|
@@ -1197,8 +1197,8 @@ static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
|
||||
/* if optional dt settings not present */
|
||||
if (!nand->ecc.size || !nand->ecc.strength) {
|
||||
/* use datasheet requirements */
|
||||
nand->ecc.strength = nand->ecc_strength_ds;
|
||||
nand->ecc.size = nand->ecc_step_ds;
|
||||
nand->ecc.strength = nand->base.eccreq.strength;
|
||||
nand->ecc.size = nand->base.eccreq.step_size;
|
||||
|
||||
/*
|
||||
* align eccstrength and eccsize
|
||||
|
@@ -20,6 +20,9 @@
|
||||
static void amd_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
nand_decode_ext_id(chip);
|
||||
|
||||
@@ -31,16 +34,24 @@ static void amd_nand_decode_id(struct nand_chip *chip)
|
||||
*/
|
||||
if (chip->id.data[4] != 0x00 && chip->id.data[5] == 0x00 &&
|
||||
chip->id.data[6] == 0x00 && chip->id.data[7] == 0x00 &&
|
||||
mtd->writesize == 512) {
|
||||
mtd->erasesize = 128 * 1024;
|
||||
mtd->erasesize <<= ((chip->id.data[3] & 0x03) << 1);
|
||||
memorg->pagesize == 512) {
|
||||
memorg->pages_per_eraseblock = 256;
|
||||
memorg->pages_per_eraseblock <<= ((chip->id.data[3] & 0x03) << 1);
|
||||
mtd->erasesize = memorg->pages_per_eraseblock *
|
||||
memorg->pagesize;
|
||||
}
|
||||
}
|
||||
|
||||
static int amd_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
/*
|
||||
* According to the datasheet of some Cypress SLC NANDs,
|
||||
* the bad block markers can be in the first, second or last
|
||||
* page of a block. So let's check all three locations.
|
||||
*/
|
||||
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE |
|
||||
NAND_BBM_LASTPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -240,10 +240,10 @@ static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
|
||||
void nand_select_target(struct nand_chip *chip, unsigned int cs)
|
||||
{
|
||||
/*
|
||||
* cs should always lie between 0 and chip->numchips, when that's not
|
||||
* the case it's a bug and the caller should be fixed.
|
||||
* cs should always lie between 0 and nanddev_ntargets(), when that's
|
||||
* not the case it's a bug and the caller should be fixed.
|
||||
*/
|
||||
if (WARN_ON(cs > chip->numchips))
|
||||
if (WARN_ON(cs > nanddev_ntargets(&chip->base)))
|
||||
return;
|
||||
|
||||
chip->cur_cs = cs;
|
||||
@@ -282,6 +282,31 @@ static void nand_release_device(struct nand_chip *chip)
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_bbm_get_next_page - Get the next page for bad block markers
|
||||
* @chip: NAND chip object
|
||||
* @page: First page to start checking for bad block marker usage
|
||||
*
|
||||
* Returns an integer that corresponds to the page offset within a block, for
|
||||
* a page that is used to store bad block markers. If no more pages are
|
||||
* available, -EINVAL is returned.
|
||||
*/
|
||||
int nand_bbm_get_next_page(struct nand_chip *chip, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int last_page = ((mtd->erasesize - mtd->writesize) >>
|
||||
chip->page_shift) & chip->pagemask;
|
||||
|
||||
if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE)
|
||||
return 0;
|
||||
else if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE)
|
||||
return 1;
|
||||
else if (page <= last_page && chip->options & NAND_BBM_LASTPAGE)
|
||||
return last_page;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
|
||||
* @chip: NAND chip object
|
||||
@@ -291,18 +316,15 @@ static void nand_release_device(struct nand_chip *chip)
|
||||
*/
|
||||
static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int page, page_end, res;
|
||||
int first_page, page_offset;
|
||||
int res;
|
||||
u8 bad;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||
ofs += mtd->erasesize - mtd->writesize;
|
||||
first_page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
||||
page_offset = nand_bbm_get_next_page(chip, 0);
|
||||
|
||||
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
||||
page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1);
|
||||
|
||||
for (; page < page_end; page++) {
|
||||
res = chip->ecc.read_oob(chip, page);
|
||||
while (page_offset >= 0) {
|
||||
res = chip->ecc.read_oob(chip, first_page + page_offset);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
@@ -314,6 +336,8 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
|
||||
res = hweight8(bad) < chip->badblockbits;
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -459,8 +483,8 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
|
||||
}
|
||||
|
||||
/* Invalidate the page cache, if we write to the cached page */
|
||||
if (page == chip->pagebuf)
|
||||
chip->pagebuf = -1;
|
||||
if (page == chip->pagecache.page)
|
||||
chip->pagecache.page = -1;
|
||||
|
||||
nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
|
||||
|
||||
@@ -493,7 +517,7 @@ static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct mtd_oob_ops ops;
|
||||
uint8_t buf[2] = { 0, 0 };
|
||||
int ret = 0, res, i = 0;
|
||||
int ret = 0, res, page_offset;
|
||||
|
||||
memset(&ops, 0, sizeof(ops));
|
||||
ops.oobbuf = buf;
|
||||
@@ -506,17 +530,18 @@ static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
|
||||
}
|
||||
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(chip, ofs, &ops);
|
||||
page_offset = nand_bbm_get_next_page(chip, 0);
|
||||
|
||||
while (page_offset >= 0) {
|
||||
res = nand_do_write_oob(chip,
|
||||
ofs + (page_offset * mtd->writesize),
|
||||
&ops);
|
||||
|
||||
if (!ret)
|
||||
ret = res;
|
||||
|
||||
i++;
|
||||
ofs += mtd->writesize;
|
||||
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
|
||||
page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -3173,7 +3198,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
|
||||
use_bufpoi = 0;
|
||||
|
||||
/* Is the current page in the buffer? */
|
||||
if (realpage != chip->pagebuf || oob) {
|
||||
if (realpage != chip->pagecache.page || oob) {
|
||||
bufpoi = use_bufpoi ? chip->data_buf : buf;
|
||||
|
||||
if (use_bufpoi && aligned)
|
||||
@@ -3199,7 +3224,7 @@ read_retry:
|
||||
if (ret < 0) {
|
||||
if (use_bufpoi)
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
chip->pagecache.page = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3208,11 +3233,11 @@ read_retry:
|
||||
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;
|
||||
chip->pagecache.page = realpage;
|
||||
chip->pagecache.bitflips = ret;
|
||||
} else {
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
chip->pagecache.page = -1;
|
||||
}
|
||||
memcpy(buf, chip->data_buf + col, bytes);
|
||||
}
|
||||
@@ -3252,7 +3277,7 @@ read_retry:
|
||||
memcpy(buf, chip->data_buf + col, bytes);
|
||||
buf += bytes;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips,
|
||||
chip->pagebuf_bitflips);
|
||||
chip->pagecache.bitflips);
|
||||
}
|
||||
|
||||
readlen -= bytes;
|
||||
@@ -3973,9 +3998,9 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
|
||||
page = realpage & chip->pagemask;
|
||||
|
||||
/* Invalidate the page cache, when we write to the cached page */
|
||||
if (to <= ((loff_t)chip->pagebuf << chip->page_shift) &&
|
||||
((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len))
|
||||
chip->pagebuf = -1;
|
||||
if (to <= ((loff_t)chip->pagecache.page << chip->page_shift) &&
|
||||
((loff_t)chip->pagecache.page << chip->page_shift) < (to + ops->len))
|
||||
chip->pagecache.page = -1;
|
||||
|
||||
/* Don't allow multipage oob writes with offset */
|
||||
if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) {
|
||||
@@ -4004,10 +4029,9 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
|
||||
__func__, buf);
|
||||
if (part_pagewr)
|
||||
bytes = min_t(int, bytes - column, writelen);
|
||||
chip->pagebuf = -1;
|
||||
memset(chip->data_buf, 0xff, mtd->writesize);
|
||||
memcpy(&chip->data_buf[column], buf, bytes);
|
||||
wbuf = chip->data_buf;
|
||||
wbuf = nand_get_data_buf(chip);
|
||||
memset(wbuf, 0xff, mtd->writesize);
|
||||
memcpy(&wbuf[column], buf, bytes);
|
||||
}
|
||||
|
||||
if (unlikely(oob)) {
|
||||
@@ -4197,9 +4221,9 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
||||
* Invalidate the page cache, if we erase the block which
|
||||
* contains the current cached page.
|
||||
*/
|
||||
if (page <= chip->pagebuf && chip->pagebuf <
|
||||
if (page <= chip->pagecache.page && chip->pagecache.page <
|
||||
(page + pages_per_block))
|
||||
chip->pagebuf = -1;
|
||||
chip->pagecache.page = -1;
|
||||
|
||||
ret = nand_erase_op(chip, (page & chip->pagemask) >>
|
||||
(chip->phys_erase_shift - chip->page_shift));
|
||||
@@ -4298,42 +4322,6 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
return nand_block_markbad_lowlevel(mtd_to_nand(mtd), ofs);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset relative to mtd start
|
||||
* @len: length of mtd
|
||||
*/
|
||||
static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
u32 part_start_block;
|
||||
u32 part_end_block;
|
||||
u32 part_start_die;
|
||||
u32 part_end_die;
|
||||
|
||||
/*
|
||||
* max_bb_per_die and blocks_per_die used to determine
|
||||
* the maximum bad block count.
|
||||
*/
|
||||
if (!chip->max_bb_per_die || !chip->blocks_per_die)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Get the start and end of the partition in erase blocks. */
|
||||
part_start_block = mtd_div_by_eb(ofs, mtd);
|
||||
part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1;
|
||||
|
||||
/* Get the start and end LUNs of the partition. */
|
||||
part_start_die = part_start_block / chip->blocks_per_die;
|
||||
part_end_die = part_end_block / chip->blocks_per_die;
|
||||
|
||||
/*
|
||||
* Look up the bad blocks per unit and multiply by the number of units
|
||||
* that the partition spans.
|
||||
*/
|
||||
return chip->max_bb_per_die * (part_end_die - part_start_die + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_suspend - [MTD Interface] Suspend the NAND flash
|
||||
* @mtd: MTD device structure
|
||||
@@ -4485,21 +4473,29 @@ static int nand_get_bits_per_cell(u8 cellinfo)
|
||||
*/
|
||||
void nand_decode_ext_id(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_memory_organization *memorg;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int extid;
|
||||
u8 *id_data = chip->id.data;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/* The 3rd id byte holds MLC / multichip data */
|
||||
chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
|
||||
memorg->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
|
||||
/* The 4th id byte is the important one */
|
||||
extid = id_data[3];
|
||||
|
||||
/* Calc pagesize */
|
||||
mtd->writesize = 1024 << (extid & 0x03);
|
||||
memorg->pagesize = 1024 << (extid & 0x03);
|
||||
mtd->writesize = memorg->pagesize;
|
||||
extid >>= 2;
|
||||
/* Calc oobsize */
|
||||
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
|
||||
memorg->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
extid >>= 2;
|
||||
/* Calc blocksize. Blocksize is multiples of 64KiB */
|
||||
memorg->pages_per_eraseblock = ((64 * 1024) << (extid & 0x03)) /
|
||||
memorg->pagesize;
|
||||
mtd->erasesize = (64 * 1024) << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Get buswidth information */
|
||||
@@ -4516,13 +4512,19 @@ EXPORT_SYMBOL_GPL(nand_decode_ext_id);
|
||||
static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
memorg->pages_per_eraseblock = type->erasesize / type->pagesize;
|
||||
mtd->erasesize = type->erasesize;
|
||||
mtd->writesize = type->pagesize;
|
||||
mtd->oobsize = mtd->writesize / 32;
|
||||
memorg->pagesize = type->pagesize;
|
||||
mtd->writesize = memorg->pagesize;
|
||||
memorg->oobsize = memorg->pagesize / 32;
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
|
||||
/* All legacy ID NAND are small-page, SLC */
|
||||
chip->bits_per_cell = 1;
|
||||
memorg->bits_per_cell = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4536,9 +4538,9 @@ static void nand_decode_bbm_options(struct nand_chip *chip)
|
||||
|
||||
/* Set the bad block position */
|
||||
if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
|
||||
chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
|
||||
chip->badblockpos = NAND_BBM_POS_LARGE;
|
||||
else
|
||||
chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
|
||||
chip->badblockpos = NAND_BBM_POS_SMALL;
|
||||
}
|
||||
|
||||
static inline bool is_full_id_nand(struct nand_flash_dev *type)
|
||||
@@ -4550,18 +4552,28 @@ static bool find_full_id_nand(struct nand_chip *chip,
|
||||
struct nand_flash_dev *type)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
u8 *id_data = chip->id.data;
|
||||
|
||||
if (!strncmp(type->id, id_data, type->id_len)) {
|
||||
mtd->writesize = type->pagesize;
|
||||
mtd->erasesize = type->erasesize;
|
||||
mtd->oobsize = type->oobsize;
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
if (!strncmp(type->id, id_data, type->id_len)) {
|
||||
memorg->pagesize = type->pagesize;
|
||||
mtd->writesize = memorg->pagesize;
|
||||
memorg->pages_per_eraseblock = type->erasesize /
|
||||
type->pagesize;
|
||||
mtd->erasesize = type->erasesize;
|
||||
memorg->oobsize = type->oobsize;
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
|
||||
memorg->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
|
||||
memorg->eraseblocks_per_lun =
|
||||
DIV_ROUND_DOWN_ULL((u64)type->chipsize << 20,
|
||||
memorg->pagesize *
|
||||
memorg->pages_per_eraseblock);
|
||||
chip->options |= type->options;
|
||||
chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
|
||||
chip->ecc_step_ds = NAND_ECC_STEP(type);
|
||||
chip->base.eccreq.strength = NAND_ECC_STRENGTH(type);
|
||||
chip->base.eccreq.step_size = NAND_ECC_STEP(type);
|
||||
chip->onfi_timing_mode_default =
|
||||
type->onfi_timing_mode_default;
|
||||
|
||||
@@ -4587,8 +4599,12 @@ static void nand_manufacturer_detect(struct nand_chip *chip)
|
||||
*/
|
||||
if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
|
||||
chip->manufacturer.desc->ops->detect) {
|
||||
struct nand_memory_organization *memorg;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/* The 3rd id byte holds MLC / multichip data */
|
||||
chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]);
|
||||
memorg->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]);
|
||||
chip->manufacturer.desc->ops->detect(chip);
|
||||
} else {
|
||||
nand_decode_ext_id(chip);
|
||||
@@ -4637,9 +4653,20 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
{
|
||||
const struct nand_manufacturer *manufacturer;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
int busw, ret;
|
||||
u8 *id_data = chip->id.data;
|
||||
u8 maf_id, dev_id;
|
||||
u64 targetsize;
|
||||
|
||||
/*
|
||||
* Let's start by initializing memorg fields that might be left
|
||||
* unassigned by the ID-based detection logic.
|
||||
*/
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
memorg->planes_per_lun = 1;
|
||||
memorg->luns_per_target = 1;
|
||||
memorg->ntargets = 1;
|
||||
|
||||
/*
|
||||
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
|
||||
@@ -4735,8 +4762,6 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
if (!chip->parameters.model)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
|
||||
if (!type->pagesize)
|
||||
nand_manufacturer_detect(chip);
|
||||
else
|
||||
@@ -4745,6 +4770,11 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
/* Get chip options */
|
||||
chip->options |= type->options;
|
||||
|
||||
memorg->eraseblocks_per_lun =
|
||||
DIV_ROUND_DOWN_ULL((u64)type->chipsize << 20,
|
||||
memorg->pagesize *
|
||||
memorg->pages_per_eraseblock);
|
||||
|
||||
ident_done:
|
||||
if (!mtd->name)
|
||||
mtd->name = chip->parameters.model;
|
||||
@@ -4773,14 +4803,15 @@ ident_done:
|
||||
/* Calculate the address shift from the page size */
|
||||
chip->page_shift = ffs(mtd->writesize) - 1;
|
||||
/* Convert chipsize to number of pages per chip -1 */
|
||||
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
|
||||
targetsize = nanddev_target_size(&chip->base);
|
||||
chip->pagemask = (targetsize >> chip->page_shift) - 1;
|
||||
|
||||
chip->bbt_erase_shift = chip->phys_erase_shift =
|
||||
ffs(mtd->erasesize) - 1;
|
||||
if (chip->chipsize & 0xffffffff)
|
||||
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
|
||||
if (targetsize & 0xffffffff)
|
||||
chip->chip_shift = ffs((unsigned)targetsize) - 1;
|
||||
else {
|
||||
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
|
||||
chip->chip_shift = ffs((unsigned)(targetsize >> 32));
|
||||
chip->chip_shift += 32 - 1;
|
||||
}
|
||||
|
||||
@@ -4796,7 +4827,7 @@ ident_done:
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
|
||||
chip->parameters.model);
|
||||
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
|
||||
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||
(int)(targetsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
|
||||
return 0;
|
||||
|
||||
@@ -4971,10 +5002,13 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
||||
struct nand_flash_dev *table)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
int nand_maf_id, nand_dev_id;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/* Assume all dies are deselected when we enter nand_scan_ident(). */
|
||||
chip->cur_cs = -1;
|
||||
|
||||
@@ -4990,12 +5024,6 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
||||
if (!mtd->name && mtd->dev.parent)
|
||||
mtd->name = dev_name(mtd->dev.parent);
|
||||
|
||||
/*
|
||||
* Start with chips->numchips = maxchips to let nand_select_target() do
|
||||
* its job. chip->numchips will be adjusted after.
|
||||
*/
|
||||
chip->numchips = maxchips;
|
||||
|
||||
/* Set the default functions */
|
||||
nand_set_defaults(chip);
|
||||
|
||||
@@ -5042,8 +5070,8 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
||||
pr_info("%d chips detected\n", i);
|
||||
|
||||
/* Store the number of chips and calc total size for mtd */
|
||||
chip->numchips = i;
|
||||
mtd->size = i * chip->chipsize;
|
||||
memorg->ntargets = i;
|
||||
mtd->size = i * nanddev_target_size(&chip->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5078,13 +5106,13 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
|
||||
ecc->bytes = 3;
|
||||
ecc->strength = 1;
|
||||
|
||||
if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC))
|
||||
if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC))
|
||||
ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
|
||||
|
||||
return 0;
|
||||
case NAND_ECC_BCH:
|
||||
if (!mtd_nand_has_bch()) {
|
||||
WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
|
||||
WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ecc->calculate = nand_bch_calculate_ecc;
|
||||
@@ -5224,8 +5252,8 @@ nand_match_ecc_req(struct nand_chip *chip,
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int req_step = chip->ecc_step_ds;
|
||||
int req_strength = chip->ecc_strength_ds;
|
||||
int req_step = chip->base.eccreq.step_size;
|
||||
int req_strength = chip->base.eccreq.strength;
|
||||
int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
|
||||
int best_step, best_strength, best_ecc_bytes;
|
||||
int best_ecc_bytes_total = INT_MAX;
|
||||
@@ -5418,7 +5446,7 @@ static bool nand_ecc_strength_good(struct nand_chip *chip)
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int corr, ds_corr;
|
||||
|
||||
if (ecc->size == 0 || chip->ecc_step_ds == 0)
|
||||
if (ecc->size == 0 || chip->base.eccreq.step_size == 0)
|
||||
/* Not enough information */
|
||||
return true;
|
||||
|
||||
@@ -5427,11 +5455,56 @@ static bool nand_ecc_strength_good(struct nand_chip *chip)
|
||||
* the correction density.
|
||||
*/
|
||||
corr = (mtd->writesize * ecc->strength) / ecc->size;
|
||||
ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds;
|
||||
ds_corr = (mtd->writesize * chip->base.eccreq.strength) /
|
||||
chip->base.eccreq.step_size;
|
||||
|
||||
return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
|
||||
return corr >= ds_corr && ecc->strength >= chip->base.eccreq.strength;
|
||||
}
|
||||
|
||||
static int rawnand_erase(struct nand_device *nand, const struct nand_pos *pos)
|
||||
{
|
||||
struct nand_chip *chip = container_of(nand, struct nand_chip,
|
||||
base);
|
||||
unsigned int eb = nanddev_pos_to_row(nand, pos);
|
||||
int ret;
|
||||
|
||||
eb >>= nand->rowconv.eraseblock_addr_shift;
|
||||
|
||||
nand_select_target(chip, pos->target);
|
||||
ret = nand_erase_op(chip, eb);
|
||||
nand_deselect_target(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rawnand_markbad(struct nand_device *nand,
|
||||
const struct nand_pos *pos)
|
||||
{
|
||||
struct nand_chip *chip = container_of(nand, struct nand_chip,
|
||||
base);
|
||||
|
||||
return nand_markbad_bbm(chip, nanddev_pos_to_offs(nand, pos));
|
||||
}
|
||||
|
||||
static bool rawnand_isbad(struct nand_device *nand, const struct nand_pos *pos)
|
||||
{
|
||||
struct nand_chip *chip = container_of(nand, struct nand_chip,
|
||||
base);
|
||||
int ret;
|
||||
|
||||
nand_select_target(chip, pos->target);
|
||||
ret = nand_isbad_bbm(chip, nanddev_pos_to_offs(nand, pos));
|
||||
nand_deselect_target(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct nand_ops rawnand_ops = {
|
||||
.erase = rawnand_erase,
|
||||
.markbad = rawnand_markbad,
|
||||
.isbad = rawnand_isbad,
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_scan_tail - Scan for the NAND device
|
||||
* @chip: NAND chip object
|
||||
@@ -5687,7 +5760,7 @@ static int nand_scan_tail(struct nand_chip *chip)
|
||||
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
|
||||
|
||||
/* Invalidate the pagebuffer reference */
|
||||
chip->pagebuf = -1;
|
||||
chip->pagecache.page = -1;
|
||||
|
||||
/* Large page NAND with SOFT_ECC should support subpage reads */
|
||||
switch (ecc->mode) {
|
||||
@@ -5700,10 +5773,15 @@ static int nand_scan_tail(struct nand_chip *chip)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = nanddev_init(&chip->base, &rawnand_ops, mtd->owner);
|
||||
if (ret)
|
||||
goto err_nand_manuf_cleanup;
|
||||
|
||||
/* Adjust the MTD_CAP_ flags when NAND_ROM is set. */
|
||||
if (chip->options & NAND_ROM)
|
||||
mtd->flags = MTD_CAP_ROM;
|
||||
|
||||
/* Fill in remaining MTD driver data */
|
||||
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;
|
||||
@@ -5719,8 +5797,7 @@ static int nand_scan_tail(struct nand_chip *chip)
|
||||
mtd->_block_isreserved = nand_block_isreserved;
|
||||
mtd->_block_isbad = nand_block_isbad;
|
||||
mtd->_block_markbad = nand_block_markbad;
|
||||
mtd->_max_bad_blocks = nand_max_bad_blocks;
|
||||
mtd->writebufsize = mtd->writesize;
|
||||
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
|
||||
|
||||
/*
|
||||
* Initialize bitflip_threshold to its default prior scan_bbt() call.
|
||||
@@ -5733,13 +5810,13 @@ static int nand_scan_tail(struct nand_chip *chip)
|
||||
/* Initialize the ->data_interface field. */
|
||||
ret = nand_init_data_interface(chip);
|
||||
if (ret)
|
||||
goto err_nand_manuf_cleanup;
|
||||
goto err_nanddev_cleanup;
|
||||
|
||||
/* Enter fastest possible mode on all dies. */
|
||||
for (i = 0; i < chip->numchips; i++) {
|
||||
for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
|
||||
ret = nand_setup_data_interface(chip, i);
|
||||
if (ret)
|
||||
goto err_nand_manuf_cleanup;
|
||||
goto err_nanddev_cleanup;
|
||||
}
|
||||
|
||||
/* Check, if we should skip the bad block table scan */
|
||||
@@ -5749,11 +5826,14 @@ static int nand_scan_tail(struct nand_chip *chip)
|
||||
/* Build bad block table */
|
||||
ret = nand_create_bbt(chip);
|
||||
if (ret)
|
||||
goto err_nand_manuf_cleanup;
|
||||
goto err_nanddev_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
err_nanddev_cleanup:
|
||||
nanddev_cleanup(&chip->base);
|
||||
|
||||
err_nand_manuf_cleanup:
|
||||
nand_manufacturer_cleanup(chip);
|
||||
|
||||
|
@@ -264,18 +264,19 @@ static int read_abs_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
struct nand_bbt_descr *td, int chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
u64 targetsize = nanddev_target_size(&this->base);
|
||||
int res = 0, i;
|
||||
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
int offs = 0;
|
||||
for (i = 0; i < this->numchips; i++) {
|
||||
for (i = 0; i < nanddev_ntargets(&this->base); i++) {
|
||||
if (chip == -1 || chip == i)
|
||||
res = read_bbt(this, buf, td->pages[i],
|
||||
this->chipsize >> this->bbt_erase_shift,
|
||||
targetsize >> this->bbt_erase_shift,
|
||||
td, offs);
|
||||
if (res)
|
||||
return res;
|
||||
offs += this->chipsize >> this->bbt_erase_shift;
|
||||
offs += targetsize >> this->bbt_erase_shift;
|
||||
}
|
||||
} else {
|
||||
res = read_bbt(this, buf, td->pages[0],
|
||||
@@ -415,11 +416,12 @@ static void read_abs_bbts(struct nand_chip *this, uint8_t *buf,
|
||||
|
||||
/* Scan a given block partially */
|
||||
static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
|
||||
loff_t offs, uint8_t *buf, int numpages)
|
||||
loff_t offs, uint8_t *buf)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
|
||||
struct mtd_oob_ops ops;
|
||||
int j, ret;
|
||||
int ret, page_offset;
|
||||
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.oobbuf = buf;
|
||||
@@ -427,12 +429,15 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
|
||||
for (j = 0; j < numpages; j++) {
|
||||
page_offset = nand_bbm_get_next_page(this, 0);
|
||||
|
||||
while (page_offset >= 0) {
|
||||
/*
|
||||
* 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);
|
||||
ret = mtd_read_oob(mtd, offs + (page_offset * mtd->writesize),
|
||||
&ops);
|
||||
/* Ignore ECC errors when checking for BBM */
|
||||
if (ret && !mtd_is_bitflip_or_eccerr(ret))
|
||||
return ret;
|
||||
@@ -440,8 +445,9 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
|
||||
if (check_short_pattern(buf, bd))
|
||||
return 1;
|
||||
|
||||
offs += mtd->writesize;
|
||||
page_offset = nand_bbm_get_next_page(this, page_offset + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -459,43 +465,35 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
|
||||
static int create_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
struct nand_bbt_descr *bd, int chip)
|
||||
{
|
||||
u64 targetsize = nanddev_target_size(&this->base);
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
int i, numblocks, numpages;
|
||||
int startblock;
|
||||
int i, numblocks, startblock;
|
||||
loff_t from;
|
||||
|
||||
pr_info("Scanning device for bad blocks\n");
|
||||
|
||||
if (bd->options & NAND_BBT_SCAN2NDPAGE)
|
||||
numpages = 2;
|
||||
else
|
||||
numpages = 1;
|
||||
|
||||
if (chip == -1) {
|
||||
numblocks = mtd->size >> this->bbt_erase_shift;
|
||||
startblock = 0;
|
||||
from = 0;
|
||||
} else {
|
||||
if (chip >= this->numchips) {
|
||||
if (chip >= nanddev_ntargets(&this->base)) {
|
||||
pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
|
||||
chip + 1, this->numchips);
|
||||
chip + 1, nanddev_ntargets(&this->base));
|
||||
return -EINVAL;
|
||||
}
|
||||
numblocks = this->chipsize >> this->bbt_erase_shift;
|
||||
numblocks = targetsize >> this->bbt_erase_shift;
|
||||
startblock = chip * numblocks;
|
||||
numblocks += startblock;
|
||||
from = (loff_t)startblock << this->bbt_erase_shift;
|
||||
}
|
||||
|
||||
if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||
from += mtd->erasesize - (mtd->writesize * numpages);
|
||||
|
||||
for (i = startblock; i < numblocks; i++) {
|
||||
int ret;
|
||||
|
||||
BUG_ON(bd->options & NAND_BBT_NO_OOB);
|
||||
|
||||
ret = scan_block_fast(this, bd, from, buf, numpages);
|
||||
ret = scan_block_fast(this, bd, from, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -529,6 +527,7 @@ static int create_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
static int search_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
struct nand_bbt_descr *td)
|
||||
{
|
||||
u64 targetsize = nanddev_target_size(&this->base);
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
int i, chips;
|
||||
int startblock, block, dir;
|
||||
@@ -547,8 +546,8 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
|
||||
/* Do we have a bbt per chip? */
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
chips = this->numchips;
|
||||
bbtblocks = this->chipsize >> this->bbt_erase_shift;
|
||||
chips = nanddev_ntargets(&this->base);
|
||||
bbtblocks = targetsize >> this->bbt_erase_shift;
|
||||
startblock &= bbtblocks - 1;
|
||||
} else {
|
||||
chips = 1;
|
||||
@@ -576,7 +575,7 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
break;
|
||||
}
|
||||
}
|
||||
startblock += this->chipsize >> this->bbt_erase_shift;
|
||||
startblock += targetsize >> this->bbt_erase_shift;
|
||||
}
|
||||
/* Check, if we found a bbt for each requested chip */
|
||||
for (i = 0; i < chips; i++) {
|
||||
@@ -626,6 +625,7 @@ static void search_read_bbts(struct nand_chip *this, uint8_t *buf,
|
||||
static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
|
||||
struct nand_bbt_descr *md, int chip)
|
||||
{
|
||||
u64 targetsize = nanddev_target_size(&this->base);
|
||||
int startblock, dir, page, numblocks, i;
|
||||
|
||||
/*
|
||||
@@ -637,9 +637,9 @@ static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
|
||||
return td->pages[chip] >>
|
||||
(this->bbt_erase_shift - this->page_shift);
|
||||
|
||||
numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
|
||||
numblocks = (int)(targetsize >> this->bbt_erase_shift);
|
||||
if (!(td->options & NAND_BBT_PERCHIP))
|
||||
numblocks *= this->numchips;
|
||||
numblocks *= nanddev_ntargets(&this->base);
|
||||
|
||||
/*
|
||||
* Automatic placement of the bad block table. Search direction
|
||||
@@ -717,6 +717,7 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
|
||||
int chipsel)
|
||||
{
|
||||
u64 targetsize = nanddev_target_size(&this->base);
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
struct erase_info einfo;
|
||||
int i, res, chip = 0;
|
||||
@@ -737,10 +738,10 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
rcode = 0xff;
|
||||
/* Write bad block table per chip rather than per device? */
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
|
||||
numblocks = (int)(targetsize >> this->bbt_erase_shift);
|
||||
/* Full device write or specific chip? */
|
||||
if (chipsel == -1) {
|
||||
nrchips = this->numchips;
|
||||
nrchips = nanddev_ntargets(&this->base);
|
||||
} else {
|
||||
nrchips = chipsel + 1;
|
||||
chip = chipsel;
|
||||
@@ -901,7 +902,9 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
static inline int nand_memory_bbt(struct nand_chip *this,
|
||||
struct nand_bbt_descr *bd)
|
||||
{
|
||||
return create_bbt(this, this->data_buf, bd, -1);
|
||||
u8 *pagebuf = nand_get_data_buf(this);
|
||||
|
||||
return create_bbt(this, pagebuf, bd, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -925,7 +928,7 @@ static int check_create(struct nand_chip *this, uint8_t *buf,
|
||||
|
||||
/* Do we have a bbt per chip? */
|
||||
if (td->options & NAND_BBT_PERCHIP)
|
||||
chips = this->numchips;
|
||||
chips = nanddev_ntargets(&this->base);
|
||||
else
|
||||
chips = 1;
|
||||
|
||||
@@ -1097,14 +1100,15 @@ static int nand_update_bbt(struct nand_chip *this, loff_t offs)
|
||||
*/
|
||||
static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)
|
||||
{
|
||||
u64 targetsize = nanddev_target_size(&this->base);
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
int i, j, chips, block, nrblocks, update;
|
||||
uint8_t oldval;
|
||||
|
||||
/* Do we have a bbt per chip? */
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
chips = this->numchips;
|
||||
nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
|
||||
chips = nanddev_ntargets(&this->base);
|
||||
nrblocks = (int)(targetsize >> this->bbt_erase_shift);
|
||||
} else {
|
||||
chips = 1;
|
||||
nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
|
||||
@@ -1157,6 +1161,7 @@ static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)
|
||||
*/
|
||||
static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)
|
||||
{
|
||||
u64 targetsize = nanddev_target_size(&this->base);
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
u32 pattern_len;
|
||||
u32 bits;
|
||||
@@ -1185,7 +1190,7 @@ static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)
|
||||
}
|
||||
|
||||
if (bd->options & NAND_BBT_PERCHIP)
|
||||
table_size = this->chipsize >> this->bbt_erase_shift;
|
||||
table_size = targetsize >> this->bbt_erase_shift;
|
||||
else
|
||||
table_size = mtd->size >> this->bbt_erase_shift;
|
||||
table_size >>= 3;
|
||||
|
@@ -14,20 +14,20 @@ static void esmt_nand_decode_id(struct nand_chip *chip)
|
||||
|
||||
/* Extract ECC requirements from 5th id byte. */
|
||||
if (chip->id.len >= 5 && nand_is_slc(chip)) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->base.eccreq.step_size = 512;
|
||||
switch (chip->id.data[4] & 0x3) {
|
||||
case 0x0:
|
||||
chip->ecc_strength_ds = 4;
|
||||
chip->base.eccreq.strength = 4;
|
||||
break;
|
||||
case 0x1:
|
||||
chip->ecc_strength_ds = 2;
|
||||
chip->base.eccreq.strength = 2;
|
||||
break;
|
||||
case 0x2:
|
||||
chip->ecc_strength_ds = 1;
|
||||
chip->base.eccreq.strength = 1;
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Could not get ECC info");
|
||||
chip->ecc_step_ds = 0;
|
||||
chip->base.eccreq.step_size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,14 @@ static void esmt_nand_decode_id(struct nand_chip *chip)
|
||||
static int esmt_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
/*
|
||||
* It is known that some ESMT SLC NANDs have been shipped
|
||||
* with the factory bad block markers in the first or last page
|
||||
* of the block, instead of the first or second page. To be on
|
||||
* the safe side, let's check all three locations.
|
||||
*/
|
||||
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE |
|
||||
NAND_BBM_LASTPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -418,24 +418,27 @@ static void hynix_nand_extract_oobsize(struct nand_chip *chip,
|
||||
bool valid_jedecid)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
u8 oobsize;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
oobsize = ((chip->id.data[3] >> 2) & 0x3) |
|
||||
((chip->id.data[3] >> 4) & 0x4);
|
||||
|
||||
if (valid_jedecid) {
|
||||
switch (oobsize) {
|
||||
case 0:
|
||||
mtd->oobsize = 2048;
|
||||
memorg->oobsize = 2048;
|
||||
break;
|
||||
case 1:
|
||||
mtd->oobsize = 1664;
|
||||
memorg->oobsize = 1664;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 1024;
|
||||
memorg->oobsize = 1024;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 640;
|
||||
memorg->oobsize = 640;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
@@ -450,25 +453,25 @@ static void hynix_nand_extract_oobsize(struct nand_chip *chip,
|
||||
} else {
|
||||
switch (oobsize) {
|
||||
case 0:
|
||||
mtd->oobsize = 128;
|
||||
memorg->oobsize = 128;
|
||||
break;
|
||||
case 1:
|
||||
mtd->oobsize = 224;
|
||||
memorg->oobsize = 224;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 448;
|
||||
memorg->oobsize = 448;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 64;
|
||||
memorg->oobsize = 64;
|
||||
break;
|
||||
case 4:
|
||||
mtd->oobsize = 32;
|
||||
memorg->oobsize = 32;
|
||||
break;
|
||||
case 5:
|
||||
mtd->oobsize = 16;
|
||||
memorg->oobsize = 16;
|
||||
break;
|
||||
case 6:
|
||||
mtd->oobsize = 640;
|
||||
memorg->oobsize = 640;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
@@ -492,8 +495,10 @@ static void hynix_nand_extract_oobsize(struct nand_chip *chip,
|
||||
* the actual OOB size for this chip is: 640 * 16k / 8k).
|
||||
*/
|
||||
if (chip->id.data[1] == 0xde)
|
||||
mtd->oobsize *= mtd->writesize / SZ_8K;
|
||||
memorg->oobsize *= memorg->pagesize / SZ_8K;
|
||||
}
|
||||
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
}
|
||||
|
||||
static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
|
||||
@@ -503,30 +508,30 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
|
||||
|
||||
if (valid_jedecid) {
|
||||
/* Reference: H27UCG8T2E datasheet */
|
||||
chip->ecc_step_ds = 1024;
|
||||
chip->base.eccreq.step_size = 1024;
|
||||
|
||||
switch (ecc_level) {
|
||||
case 0:
|
||||
chip->ecc_step_ds = 0;
|
||||
chip->ecc_strength_ds = 0;
|
||||
chip->base.eccreq.step_size = 0;
|
||||
chip->base.eccreq.strength = 0;
|
||||
break;
|
||||
case 1:
|
||||
chip->ecc_strength_ds = 4;
|
||||
chip->base.eccreq.strength = 4;
|
||||
break;
|
||||
case 2:
|
||||
chip->ecc_strength_ds = 24;
|
||||
chip->base.eccreq.strength = 24;
|
||||
break;
|
||||
case 3:
|
||||
chip->ecc_strength_ds = 32;
|
||||
chip->base.eccreq.strength = 32;
|
||||
break;
|
||||
case 4:
|
||||
chip->ecc_strength_ds = 40;
|
||||
chip->base.eccreq.strength = 40;
|
||||
break;
|
||||
case 5:
|
||||
chip->ecc_strength_ds = 50;
|
||||
chip->base.eccreq.strength = 50;
|
||||
break;
|
||||
case 6:
|
||||
chip->ecc_strength_ds = 60;
|
||||
chip->base.eccreq.strength = 60;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
@@ -547,14 +552,14 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
|
||||
if (nand_tech < 3) {
|
||||
/* > 26nm, reference: H27UBG8T2A datasheet */
|
||||
if (ecc_level < 5) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->ecc_strength_ds = 1 << ecc_level;
|
||||
chip->base.eccreq.step_size = 512;
|
||||
chip->base.eccreq.strength = 1 << ecc_level;
|
||||
} else if (ecc_level < 7) {
|
||||
if (ecc_level == 5)
|
||||
chip->ecc_step_ds = 2048;
|
||||
chip->base.eccreq.step_size = 2048;
|
||||
else
|
||||
chip->ecc_step_ds = 1024;
|
||||
chip->ecc_strength_ds = 24;
|
||||
chip->base.eccreq.step_size = 1024;
|
||||
chip->base.eccreq.strength = 24;
|
||||
} else {
|
||||
/*
|
||||
* We should never reach this case, but if that
|
||||
@@ -567,14 +572,14 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
|
||||
} else {
|
||||
/* <= 26nm, reference: H27UBG8T2B datasheet */
|
||||
if (!ecc_level) {
|
||||
chip->ecc_step_ds = 0;
|
||||
chip->ecc_strength_ds = 0;
|
||||
chip->base.eccreq.step_size = 0;
|
||||
chip->base.eccreq.strength = 0;
|
||||
} else if (ecc_level < 5) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->ecc_strength_ds = 1 << (ecc_level - 1);
|
||||
chip->base.eccreq.step_size = 512;
|
||||
chip->base.eccreq.strength = 1 << (ecc_level - 1);
|
||||
} else {
|
||||
chip->ecc_step_ds = 1024;
|
||||
chip->ecc_strength_ds = 24 +
|
||||
chip->base.eccreq.step_size = 1024;
|
||||
chip->base.eccreq.strength = 24 +
|
||||
(8 * (ecc_level - 5));
|
||||
}
|
||||
}
|
||||
@@ -587,7 +592,7 @@ static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip,
|
||||
u8 nand_tech;
|
||||
|
||||
/* We need scrambling on all TLC NANDs*/
|
||||
if (chip->bits_per_cell > 2)
|
||||
if (nanddev_bits_per_cell(&chip->base) > 2)
|
||||
chip->options |= NAND_NEED_SCRAMBLING;
|
||||
|
||||
/* And on MLC NANDs with sub-3xnm process */
|
||||
@@ -609,9 +614,12 @@ static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip,
|
||||
static void hynix_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
bool valid_jedecid;
|
||||
u8 tmp;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/*
|
||||
* Exclude all SLC NANDs from this advanced detection scheme.
|
||||
* According to the ranges defined in several datasheets, it might
|
||||
@@ -625,7 +633,8 @@ static void hynix_nand_decode_id(struct nand_chip *chip)
|
||||
}
|
||||
|
||||
/* Extract pagesize */
|
||||
mtd->writesize = 2048 << (chip->id.data[3] & 0x03);
|
||||
memorg->pagesize = 2048 << (chip->id.data[3] & 0x03);
|
||||
mtd->writesize = memorg->pagesize;
|
||||
|
||||
tmp = (chip->id.data[3] >> 4) & 0x3;
|
||||
/*
|
||||
@@ -635,12 +644,19 @@ static void hynix_nand_decode_id(struct nand_chip *chip)
|
||||
* The only exception is when ID[3][4:5] == 3 and ID[3][7] == 0, in
|
||||
* this case the erasesize is set to 768KiB.
|
||||
*/
|
||||
if (chip->id.data[3] & 0x80)
|
||||
if (chip->id.data[3] & 0x80) {
|
||||
memorg->pages_per_eraseblock = (SZ_1M << tmp) /
|
||||
memorg->pagesize;
|
||||
mtd->erasesize = SZ_1M << tmp;
|
||||
else if (tmp == 3)
|
||||
} else if (tmp == 3) {
|
||||
memorg->pages_per_eraseblock = (SZ_512K + SZ_256K) /
|
||||
memorg->pagesize;
|
||||
mtd->erasesize = SZ_512K + SZ_256K;
|
||||
else
|
||||
} else {
|
||||
memorg->pages_per_eraseblock = (SZ_128K << tmp) /
|
||||
memorg->pagesize;
|
||||
mtd->erasesize = SZ_128K << tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modern Toggle DDR NANDs have a valid JEDECID even though they are
|
||||
@@ -672,9 +688,9 @@ static int hynix_nand_init(struct nand_chip *chip)
|
||||
int ret;
|
||||
|
||||
if (!nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
|
||||
chip->options |= NAND_BBM_LASTPAGE;
|
||||
else
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
|
||||
|
||||
hynix = kzalloc(sizeof(*hynix), GFP_KERNEL);
|
||||
if (!hynix)
|
||||
|
@@ -22,12 +22,15 @@
|
||||
int nand_jedec_detect(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
struct nand_jedec_params *p;
|
||||
struct jedec_ecc_info *ecc;
|
||||
int jedec_version = 0;
|
||||
char id[5];
|
||||
int i, val, ret;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/* Try JEDEC for unknown chip or LP */
|
||||
ret = nand_readid_op(chip, 0x40, id, sizeof(id));
|
||||
if (ret || strncmp(id, "JEDEC", sizeof(id)))
|
||||
@@ -81,18 +84,24 @@ int nand_jedec_detect(struct nand_chip *chip)
|
||||
goto free_jedec_param_page;
|
||||
}
|
||||
|
||||
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
||||
memorg->pagesize = le32_to_cpu(p->byte_per_page);
|
||||
mtd->writesize = memorg->pagesize;
|
||||
|
||||
/* 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;
|
||||
memorg->pages_per_eraseblock =
|
||||
1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
|
||||
mtd->erasesize = memorg->pages_per_eraseblock * memorg->pagesize;
|
||||
|
||||
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
memorg->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
|
||||
memorg->luns_per_target = p->lun_count;
|
||||
memorg->planes_per_lun = 1 << p->multi_plane_addr;
|
||||
|
||||
/* 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;
|
||||
memorg->eraseblocks_per_lun =
|
||||
1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
|
||||
memorg->bits_per_cell = p->bits_per_cell;
|
||||
|
||||
if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
@@ -101,8 +110,8 @@ int nand_jedec_detect(struct nand_chip *chip)
|
||||
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;
|
||||
chip->base.eccreq.strength = ecc->ecc_bits;
|
||||
chip->base.eccreq.step_size = 1 << ecc->codeword_size;
|
||||
} else {
|
||||
pr_warn("Invalid codeword size\n");
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ static void macronix_nand_fix_broken_get_timings(struct nand_chip *chip)
|
||||
static int macronix_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
|
||||
|
||||
macronix_nand_fix_broken_get_timings(chip);
|
||||
|
||||
|
@@ -385,13 +385,13 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
|
||||
if (!chip->parameters.onfi)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
if (chip->bits_per_cell != 1)
|
||||
if (nanddev_bits_per_cell(&chip->base) != 1)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
/*
|
||||
* We only support on-die ECC of 4/512 or 8/512
|
||||
*/
|
||||
if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8)
|
||||
if (chip->base.eccreq.strength != 4 && chip->base.eccreq.strength != 8)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
/* 0x2 means on-die ECC is available. */
|
||||
@@ -424,7 +424,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
|
||||
/*
|
||||
* We only support on-die ECC of 4/512 or 8/512
|
||||
*/
|
||||
if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8)
|
||||
if (chip->base.eccreq.strength != 4 && chip->base.eccreq.strength != 8)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
return MICRON_ON_DIE_SUPPORTED;
|
||||
@@ -448,7 +448,7 @@ static int micron_nand_init(struct nand_chip *chip)
|
||||
goto err_free_manuf_data;
|
||||
|
||||
if (mtd->writesize == 2048)
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
|
||||
|
||||
ondie = micron_supports_on_die_ecc(chip);
|
||||
|
||||
@@ -479,7 +479,7 @@ static int micron_nand_init(struct nand_chip *chip)
|
||||
* That's not needed for 8-bit ECC, because the status expose
|
||||
* a better approximation of the number of bitflips in a page.
|
||||
*/
|
||||
if (chip->ecc_strength_ds == 4) {
|
||||
if (chip->base.eccreq.strength == 4) {
|
||||
micron->ecc.rawbuf = kmalloc(mtd->writesize +
|
||||
mtd->oobsize,
|
||||
GFP_KERNEL);
|
||||
@@ -489,16 +489,16 @@ static int micron_nand_init(struct nand_chip *chip)
|
||||
}
|
||||
}
|
||||
|
||||
if (chip->ecc_strength_ds == 4)
|
||||
if (chip->base.eccreq.strength == 4)
|
||||
mtd_set_ooblayout(mtd,
|
||||
µn_nand_on_die_4_ooblayout_ops);
|
||||
else
|
||||
mtd_set_ooblayout(mtd,
|
||||
µn_nand_on_die_8_ooblayout_ops);
|
||||
|
||||
chip->ecc.bytes = chip->ecc_strength_ds * 2;
|
||||
chip->ecc.bytes = chip->base.eccreq.strength * 2;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.strength = chip->ecc_strength_ds;
|
||||
chip->ecc.strength = chip->base.eccreq.strength;
|
||||
chip->ecc.algo = NAND_ECC_BCH;
|
||||
chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
|
||||
chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
|
||||
|
@@ -94,8 +94,8 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
|
||||
goto ext_out;
|
||||
}
|
||||
|
||||
chip->ecc_strength_ds = ecc->ecc_bits;
|
||||
chip->ecc_step_ds = 1 << ecc->codeword_size;
|
||||
chip->base.eccreq.strength = ecc->ecc_bits;
|
||||
chip->base.eccreq.step_size = 1 << ecc->codeword_size;
|
||||
ret = 0;
|
||||
|
||||
ext_out:
|
||||
@@ -140,12 +140,15 @@ static void nand_bit_wise_majority(const void **srcbufs,
|
||||
int nand_onfi_detect(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
struct nand_onfi_params *p;
|
||||
struct onfi_params *onfi;
|
||||
int onfi_version = 0;
|
||||
char id[4];
|
||||
int i, ret, val;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/* Try ONFI for unknown chip or LP */
|
||||
ret = nand_readid_op(chip, 0x20, id, sizeof(id));
|
||||
if (ret || strncmp(id, "ONFI", 4))
|
||||
@@ -221,32 +224,36 @@ int nand_onfi_detect(struct nand_chip *chip)
|
||||
goto free_onfi_param_page;
|
||||
}
|
||||
|
||||
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
||||
memorg->pagesize = le32_to_cpu(p->byte_per_page);
|
||||
mtd->writesize = memorg->pagesize;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
memorg->pages_per_eraseblock =
|
||||
1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
|
||||
mtd->erasesize = memorg->pages_per_eraseblock * memorg->pagesize;
|
||||
|
||||
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
memorg->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
|
||||
memorg->luns_per_target = p->lun_count;
|
||||
memorg->planes_per_lun = 1 << p->interleaved_bits;
|
||||
|
||||
/* 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;
|
||||
|
||||
chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun);
|
||||
chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
|
||||
memorg->eraseblocks_per_lun =
|
||||
1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
|
||||
memorg->max_bad_eraseblocks_per_lun = le32_to_cpu(p->blocks_per_lun);
|
||||
memorg->bits_per_cell = p->bits_per_cell;
|
||||
|
||||
if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (p->ecc_bits != 0xff) {
|
||||
chip->ecc_strength_ds = p->ecc_bits;
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->base.eccreq.strength = p->ecc_bits;
|
||||
chip->base.eccreq.step_size = 512;
|
||||
} else if (onfi_version >= 21 &&
|
||||
(le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
|
||||
|
||||
|
@@ -20,6 +20,9 @@
|
||||
static void samsung_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
/* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */
|
||||
if (chip->id.len == 6 && !nand_is_slc(chip) &&
|
||||
@@ -27,29 +30,30 @@ static void samsung_nand_decode_id(struct nand_chip *chip)
|
||||
u8 extid = chip->id.data[3];
|
||||
|
||||
/* Get pagesize */
|
||||
mtd->writesize = 2048 << (extid & 0x03);
|
||||
memorg->pagesize = 2048 << (extid & 0x03);
|
||||
mtd->writesize = memorg->pagesize;
|
||||
|
||||
extid >>= 2;
|
||||
|
||||
/* Get oobsize */
|
||||
switch (((extid >> 2) & 0x4) | (extid & 0x3)) {
|
||||
case 1:
|
||||
mtd->oobsize = 128;
|
||||
memorg->oobsize = 128;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 218;
|
||||
memorg->oobsize = 218;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 400;
|
||||
memorg->oobsize = 400;
|
||||
break;
|
||||
case 4:
|
||||
mtd->oobsize = 436;
|
||||
memorg->oobsize = 436;
|
||||
break;
|
||||
case 5:
|
||||
mtd->oobsize = 512;
|
||||
memorg->oobsize = 512;
|
||||
break;
|
||||
case 6:
|
||||
mtd->oobsize = 640;
|
||||
memorg->oobsize = 640;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
@@ -62,31 +66,37 @@ static void samsung_nand_decode_id(struct nand_chip *chip)
|
||||
break;
|
||||
}
|
||||
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
|
||||
/* Get blocksize */
|
||||
extid >>= 2;
|
||||
memorg->pages_per_eraseblock = (128 * 1024) <<
|
||||
(((extid >> 1) & 0x04) |
|
||||
(extid & 0x03)) /
|
||||
memorg->pagesize;
|
||||
mtd->erasesize = (128 * 1024) <<
|
||||
(((extid >> 1) & 0x04) | (extid & 0x03));
|
||||
|
||||
/* Extract ECC requirements from 5th id byte*/
|
||||
extid = (chip->id.data[4] >> 4) & 0x07;
|
||||
if (extid < 5) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->ecc_strength_ds = 1 << extid;
|
||||
chip->base.eccreq.step_size = 512;
|
||||
chip->base.eccreq.strength = 1 << extid;
|
||||
} else {
|
||||
chip->ecc_step_ds = 1024;
|
||||
chip->base.eccreq.step_size = 1024;
|
||||
switch (extid) {
|
||||
case 5:
|
||||
chip->ecc_strength_ds = 24;
|
||||
chip->base.eccreq.strength = 24;
|
||||
break;
|
||||
case 6:
|
||||
chip->ecc_strength_ds = 40;
|
||||
chip->base.eccreq.strength = 40;
|
||||
break;
|
||||
case 7:
|
||||
chip->ecc_strength_ds = 60;
|
||||
chip->base.eccreq.strength = 60;
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Could not decode ECC info");
|
||||
chip->ecc_step_ds = 0;
|
||||
chip->base.eccreq.step_size = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -96,8 +106,8 @@ static void samsung_nand_decode_id(struct nand_chip *chip)
|
||||
switch (chip->id.data[1]) {
|
||||
/* K9F4G08U0D-S[I|C]B0(T00) */
|
||||
case 0xDC:
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->ecc_strength_ds = 1;
|
||||
chip->base.eccreq.step_size = 512;
|
||||
chip->base.eccreq.strength = 1;
|
||||
break;
|
||||
|
||||
/* K9F1G08U0E 21nm chips do not support subpage write */
|
||||
@@ -121,9 +131,9 @@ static int samsung_nand_init(struct nand_chip *chip)
|
||||
chip->options |= NAND_SAMSUNG_LP_OPTIONS;
|
||||
|
||||
if (!nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
|
||||
chip->options |= NAND_BBM_LASTPAGE;
|
||||
else
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -101,6 +101,9 @@ static void toshiba_nand_benand_init(struct nand_chip *chip)
|
||||
static void toshiba_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
nand_decode_ext_id(chip);
|
||||
|
||||
@@ -114,8 +117,10 @@ static void toshiba_nand_decode_id(struct nand_chip *chip)
|
||||
*/
|
||||
if (chip->id.len >= 6 && nand_is_slc(chip) &&
|
||||
(chip->id.data[5] & 0x7) == 0x6 /* 24nm */ &&
|
||||
!(chip->id.data[4] & 0x80) /* !BENAND */)
|
||||
mtd->oobsize = 32 * mtd->writesize >> 9;
|
||||
!(chip->id.data[4] & 0x80) /* !BENAND */) {
|
||||
memorg->oobsize = 32 * memorg->pagesize >> 9;
|
||||
mtd->oobsize = memorg->oobsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract ECC requirements from 6th id byte.
|
||||
@@ -125,20 +130,20 @@ static void toshiba_nand_decode_id(struct nand_chip *chip)
|
||||
* - 24nm: 8 bit ECC for each 512Byte is required.
|
||||
*/
|
||||
if (chip->id.len >= 6 && nand_is_slc(chip)) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->base.eccreq.step_size = 512;
|
||||
switch (chip->id.data[5] & 0x7) {
|
||||
case 0x4:
|
||||
chip->ecc_strength_ds = 1;
|
||||
chip->base.eccreq.strength = 1;
|
||||
break;
|
||||
case 0x5:
|
||||
chip->ecc_strength_ds = 4;
|
||||
chip->base.eccreq.strength = 4;
|
||||
break;
|
||||
case 0x6:
|
||||
chip->ecc_strength_ds = 8;
|
||||
chip->base.eccreq.strength = 8;
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Could not get ECC info");
|
||||
chip->ecc_step_ds = 0;
|
||||
chip->base.eccreq.step_size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +152,7 @@ static void toshiba_nand_decode_id(struct nand_chip *chip)
|
||||
static int toshiba_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
|
||||
|
||||
/* Check that chip is BENAND and ECC mode is on-die */
|
||||
if (nand_is_slc(chip) && chip->ecc.mode == NAND_ECC_ON_DIE &&
|
||||
|
@@ -298,6 +298,8 @@ union ns_mem {
|
||||
* The structure which describes all the internal simulator data.
|
||||
*/
|
||||
struct nandsim {
|
||||
struct nand_chip chip;
|
||||
struct nand_controller base;
|
||||
struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS];
|
||||
unsigned int nbparts;
|
||||
|
||||
@@ -644,9 +646,6 @@ static int __init init_nandsim(struct mtd_info *mtd)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Force mtd to not do delays */
|
||||
chip->legacy.chip_delay = 0;
|
||||
|
||||
/* Initialize the NAND flash parameters */
|
||||
ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
|
||||
ns->geom.totsz = mtd->size;
|
||||
@@ -2076,24 +2075,6 @@ static void ns_nand_write_byte(struct nand_chip *chip, u_char byte)
|
||||
return;
|
||||
}
|
||||
|
||||
static void ns_hwcontrol(struct nand_chip *chip, int cmd, unsigned int bitmask)
|
||||
{
|
||||
struct nandsim *ns = nand_get_controller_data(chip);
|
||||
|
||||
ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
|
||||
ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
|
||||
ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
ns_nand_write_byte(chip, cmd);
|
||||
}
|
||||
|
||||
static int ns_device_ready(struct nand_chip *chip)
|
||||
{
|
||||
NS_DBG("device_ready\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ns_nand_write_buf(struct nand_chip *chip, const u_char *buf,
|
||||
int len)
|
||||
{
|
||||
@@ -2145,7 +2126,7 @@ static void ns_nand_read_buf(struct nand_chip *chip, u_char *buf, int len)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = chip->legacy.read_byte(chip);
|
||||
buf[i] = ns_nand_read_byte(chip);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -2168,6 +2149,46 @@ static void ns_nand_read_buf(struct nand_chip *chip, u_char *buf, int len)
|
||||
return;
|
||||
}
|
||||
|
||||
static int ns_exec_op(struct nand_chip *chip, const struct nand_operation *op,
|
||||
bool check_only)
|
||||
{
|
||||
int i;
|
||||
unsigned int op_id;
|
||||
const struct nand_op_instr *instr = NULL;
|
||||
struct nandsim *ns = nand_get_controller_data(chip);
|
||||
|
||||
ns->lines.ce = 1;
|
||||
|
||||
for (op_id = 0; op_id < op->ninstrs; op_id++) {
|
||||
instr = &op->instrs[op_id];
|
||||
ns->lines.cle = 0;
|
||||
ns->lines.ale = 0;
|
||||
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
ns->lines.cle = 1;
|
||||
ns_nand_write_byte(chip, instr->ctx.cmd.opcode);
|
||||
break;
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
ns->lines.ale = 1;
|
||||
for (i = 0; i < instr->ctx.addr.naddrs; i++)
|
||||
ns_nand_write_byte(chip, instr->ctx.addr.addrs[i]);
|
||||
break;
|
||||
case NAND_OP_DATA_IN_INSTR:
|
||||
ns_nand_read_buf(chip, instr->ctx.data.buf.in, instr->ctx.data.len);
|
||||
break;
|
||||
case NAND_OP_DATA_OUT_INSTR:
|
||||
ns_nand_write_buf(chip, instr->ctx.data.buf.out, instr->ctx.data.len);
|
||||
break;
|
||||
case NAND_OP_WAITRDY_INSTR:
|
||||
/* we are always ready */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ns_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
unsigned int eccsteps, eccbytes;
|
||||
@@ -2208,6 +2229,7 @@ static int ns_attach_chip(struct nand_chip *chip)
|
||||
|
||||
static const struct nand_controller_ops ns_controller_ops = {
|
||||
.attach_chip = ns_attach_chip,
|
||||
.exec_op = ns_exec_op,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2216,7 +2238,7 @@ static const struct nand_controller_ops ns_controller_ops = {
|
||||
static int __init ns_init_module(void)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
struct nandsim *nand;
|
||||
struct nandsim *ns;
|
||||
int retval = -ENOMEM, i;
|
||||
|
||||
if (bus_width != 8 && bus_width != 16) {
|
||||
@@ -2224,25 +2246,15 @@ static int __init ns_init_module(void)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Allocate and initialize mtd_info, nand_chip and nandsim structures */
|
||||
chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim),
|
||||
GFP_KERNEL);
|
||||
if (!chip) {
|
||||
ns = kzalloc(sizeof(struct nandsim), GFP_KERNEL);
|
||||
if (!ns) {
|
||||
NS_ERR("unable to allocate core structures.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
chip = &ns->chip;
|
||||
nsmtd = nand_to_mtd(chip);
|
||||
nand = (struct nandsim *)(chip + 1);
|
||||
nand_set_controller_data(chip, (void *)nand);
|
||||
nand_set_controller_data(chip, (void *)ns);
|
||||
|
||||
/*
|
||||
* Register simulator's callbacks.
|
||||
*/
|
||||
chip->legacy.cmd_ctrl = ns_hwcontrol;
|
||||
chip->legacy.read_byte = ns_nand_read_byte;
|
||||
chip->legacy.dev_ready = ns_device_ready;
|
||||
chip->legacy.write_buf = ns_nand_write_buf;
|
||||
chip->legacy.read_buf = ns_nand_read_buf;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
||||
/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
|
||||
@@ -2251,9 +2263,11 @@ static int __init ns_init_module(void)
|
||||
|
||||
switch (bbt) {
|
||||
case 2:
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
/* fall through */
|
||||
case 1:
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
/* fall through */
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
@@ -2266,19 +2280,19 @@ static int __init ns_init_module(void)
|
||||
* the initial ID read command correctly
|
||||
*/
|
||||
if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF)
|
||||
nand->geom.idbytes = 8;
|
||||
ns->geom.idbytes = 8;
|
||||
else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF)
|
||||
nand->geom.idbytes = 6;
|
||||
ns->geom.idbytes = 6;
|
||||
else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF)
|
||||
nand->geom.idbytes = 4;
|
||||
ns->geom.idbytes = 4;
|
||||
else
|
||||
nand->geom.idbytes = 2;
|
||||
nand->regs.status = NS_STATUS_OK(nand);
|
||||
nand->nxstate = STATE_UNKNOWN;
|
||||
nand->options |= OPT_PAGE512; /* temporary value */
|
||||
memcpy(nand->ids, id_bytes, sizeof(nand->ids));
|
||||
ns->geom.idbytes = 2;
|
||||
ns->regs.status = NS_STATUS_OK(ns);
|
||||
ns->nxstate = STATE_UNKNOWN;
|
||||
ns->options |= OPT_PAGE512; /* temporary value */
|
||||
memcpy(ns->ids, id_bytes, sizeof(ns->ids));
|
||||
if (bus_width == 16) {
|
||||
nand->busw = 16;
|
||||
ns->busw = 16;
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
}
|
||||
|
||||
@@ -2293,7 +2307,10 @@ static int __init ns_init_module(void)
|
||||
if ((retval = parse_gravepages()) != 0)
|
||||
goto error;
|
||||
|
||||
chip->legacy.dummy_controller.ops = &ns_controller_ops;
|
||||
nand_controller_init(&ns->base);
|
||||
ns->base.ops = &ns_controller_ops;
|
||||
chip->controller = &ns->base;
|
||||
|
||||
retval = nand_scan(chip, 1);
|
||||
if (retval) {
|
||||
NS_ERR("Could not scan NAND Simulator device\n");
|
||||
@@ -2302,16 +2319,23 @@ static int __init ns_init_module(void)
|
||||
|
||||
if (overridesize) {
|
||||
uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
|
||||
struct nand_memory_organization *memorg;
|
||||
u64 targetsize;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
|
||||
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 */
|
||||
nsmtd->size = new_size;
|
||||
chip->chipsize = new_size;
|
||||
memorg->eraseblocks_per_lun = 1 << overridesize;
|
||||
targetsize = nanddev_target_size(&chip->base);
|
||||
chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1;
|
||||
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
|
||||
chip->pagemask = (targetsize >> chip->page_shift) - 1;
|
||||
}
|
||||
|
||||
if ((retval = setup_wear_reporting(nsmtd)) != 0)
|
||||
@@ -2323,27 +2347,27 @@ static int __init ns_init_module(void)
|
||||
if ((retval = nand_create_bbt(chip)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
if ((retval = parse_badblocks(nand, nsmtd)) != 0)
|
||||
if ((retval = parse_badblocks(ns, nsmtd)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
/* Register NAND partitions */
|
||||
retval = mtd_device_register(nsmtd, &nand->partitions[0],
|
||||
nand->nbparts);
|
||||
retval = mtd_device_register(nsmtd, &ns->partitions[0],
|
||||
ns->nbparts);
|
||||
if (retval != 0)
|
||||
goto err_exit;
|
||||
|
||||
if ((retval = nandsim_debugfs_create(nand)) != 0)
|
||||
if ((retval = nandsim_debugfs_create(ns)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
free_nandsim(nand);
|
||||
free_nandsim(ns);
|
||||
nand_release(chip);
|
||||
for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
|
||||
kfree(nand->partitions[i].name);
|
||||
for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
|
||||
kfree(ns->partitions[i].name);
|
||||
error:
|
||||
kfree(chip);
|
||||
kfree(ns);
|
||||
free_lists();
|
||||
|
||||
return retval;
|
||||
@@ -2364,7 +2388,7 @@ static void __exit ns_cleanup_module(void)
|
||||
nand_release(chip); /* Unregister driver */
|
||||
for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
|
||||
kfree(ns->partitions[i].name);
|
||||
kfree(mtd_to_nand(nsmtd)); /* Free other structures */
|
||||
kfree(ns); /* Free other structures */
|
||||
free_lists();
|
||||
}
|
||||
|
||||
|
@@ -192,8 +192,9 @@ static void nuc900_nand_command_lp(struct nand_chip *chip,
|
||||
return;
|
||||
|
||||
case NAND_CMD_READ0:
|
||||
|
||||
write_cmd_reg(nand, NAND_CMD_READSTART);
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
|
||||
if (!chip->legacy.dev_ready) {
|
||||
|
@@ -1725,9 +1725,9 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) {
|
||||
if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"CONFIG_MTD_NAND_ECC_BCH not enabled\n");
|
||||
"CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
|
||||
return false;
|
||||
}
|
||||
if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) {
|
||||
|
@@ -465,11 +465,13 @@ static int elm_context_save(struct elm_info *info)
|
||||
ELM_SYNDROME_FRAGMENT_5 + offset);
|
||||
regs->elm_syndrome_fragment_4[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_4 + offset);
|
||||
/* fall through */
|
||||
case BCH8_ECC:
|
||||
regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_3 + offset);
|
||||
regs->elm_syndrome_fragment_2[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_2 + offset);
|
||||
/* fall through */
|
||||
case BCH4_ECC:
|
||||
regs->elm_syndrome_fragment_1[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_1 + offset);
|
||||
@@ -511,11 +513,13 @@ static int elm_context_restore(struct elm_info *info)
|
||||
regs->elm_syndrome_fragment_5[i]);
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset,
|
||||
regs->elm_syndrome_fragment_4[i]);
|
||||
/* fall through */
|
||||
case BCH8_ECC:
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
|
||||
regs->elm_syndrome_fragment_3[i]);
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset,
|
||||
regs->elm_syndrome_fragment_2[i]);
|
||||
/* fall through */
|
||||
case BCH4_ECC:
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset,
|
||||
regs->elm_syndrome_fragment_1[i]);
|
||||
|
@@ -1680,14 +1680,12 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
|
||||
u8 *cw_data_buf, *cw_oob_buf;
|
||||
int cw, data_size, oob_size, ret = 0;
|
||||
|
||||
if (!data_buf) {
|
||||
data_buf = chip->data_buf;
|
||||
chip->pagebuf = -1;
|
||||
}
|
||||
if (!data_buf)
|
||||
data_buf = nand_get_data_buf(chip);
|
||||
|
||||
if (!oob_buf) {
|
||||
nand_get_data_buf(chip);
|
||||
oob_buf = chip->oob_poi;
|
||||
chip->pagebuf = -1;
|
||||
}
|
||||
|
||||
for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
|
||||
|
@@ -101,14 +101,12 @@ static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = {
|
||||
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
|
||||
|
||||
static struct nand_bbt_descr flctl_4secc_smallpage = {
|
||||
.options = NAND_BBT_SCAN2NDPAGE,
|
||||
.offs = 11,
|
||||
.len = 1,
|
||||
.pattern = scan_ff_pattern,
|
||||
};
|
||||
|
||||
static struct nand_bbt_descr flctl_4secc_largepage = {
|
||||
.options = NAND_BBT_SCAN2NDPAGE,
|
||||
.offs = 0,
|
||||
.len = 2,
|
||||
.pattern = scan_ff_pattern,
|
||||
@@ -986,6 +984,7 @@ static void flctl_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
|
||||
|
||||
static int flctl_chip_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
u64 targetsize = nanddev_target_size(&chip->base);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct sh_flctl *flctl = mtd_to_flctl(mtd);
|
||||
|
||||
@@ -998,11 +997,11 @@ static int flctl_chip_attach_chip(struct nand_chip *chip)
|
||||
|
||||
if (mtd->writesize == 512) {
|
||||
flctl->page_size = 0;
|
||||
if (chip->chipsize > (32 << 20)) {
|
||||
if (targetsize > (32 << 20)) {
|
||||
/* big than 32MB */
|
||||
flctl->rw_ADRCNT = ADRCNT_4;
|
||||
flctl->erase_ADRCNT = ADRCNT_3;
|
||||
} else if (chip->chipsize > (2 << 16)) {
|
||||
} else if (targetsize > (2 << 16)) {
|
||||
/* big than 128KB */
|
||||
flctl->rw_ADRCNT = ADRCNT_3;
|
||||
flctl->erase_ADRCNT = ADRCNT_2;
|
||||
@@ -1012,11 +1011,11 @@ static int flctl_chip_attach_chip(struct nand_chip *chip)
|
||||
}
|
||||
} else {
|
||||
flctl->page_size = 1;
|
||||
if (chip->chipsize > (128 << 20)) {
|
||||
if (targetsize > (128 << 20)) {
|
||||
/* big than 128MB */
|
||||
flctl->rw_ADRCNT = ADRCNT2_E;
|
||||
flctl->erase_ADRCNT = ADRCNT_3;
|
||||
} else if (chip->chipsize > (8 << 16)) {
|
||||
} else if (targetsize > (8 << 16)) {
|
||||
/* big than 512KB */
|
||||
flctl->rw_ADRCNT = ADRCNT_4;
|
||||
flctl->erase_ADRCNT = ADRCNT_2;
|
||||
@@ -1178,6 +1177,8 @@ static int flctl_probe(struct platform_device *pdev)
|
||||
if (pdata->flcmncr_val & SEL_16BIT)
|
||||
nand->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
nand->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_resume(&pdev->dev);
|
||||
|
||||
|
@@ -42,7 +42,8 @@
|
||||
#define NFC_REG_CMD 0x0024
|
||||
#define NFC_REG_RCMD_SET 0x0028
|
||||
#define NFC_REG_WCMD_SET 0x002C
|
||||
#define NFC_REG_IO_DATA 0x0030
|
||||
#define NFC_REG_A10_IO_DATA 0x0030
|
||||
#define NFC_REG_A23_IO_DATA 0x0300
|
||||
#define NFC_REG_ECC_CTL 0x0034
|
||||
#define NFC_REG_ECC_ST 0x0038
|
||||
#define NFC_REG_DEBUG 0x003C
|
||||
@@ -200,6 +201,22 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
|
||||
return container_of(nand, struct sunxi_nand_chip, nand);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAND Controller capabilities structure: stores NAND controller capabilities
|
||||
* for distinction between compatible strings.
|
||||
*
|
||||
* @sram_through_ahb: On A23, we choose to access the internal RAM through AHB
|
||||
* instead of MBUS (less configuration). A10, A10s, A13 and
|
||||
* A20 use the MBUS but no extra configuration is needed.
|
||||
* @reg_io_data: I/O data register
|
||||
* @dma_maxburst: DMA maxburst
|
||||
*/
|
||||
struct sunxi_nfc_caps {
|
||||
bool sram_through_ahb;
|
||||
unsigned int reg_io_data;
|
||||
unsigned int dma_maxburst;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sunxi_nfc - stores sunxi NAND controller information
|
||||
*
|
||||
@@ -228,6 +245,7 @@ struct sunxi_nfc {
|
||||
struct list_head chips;
|
||||
struct completion complete;
|
||||
struct dma_chan *dmac;
|
||||
const struct sunxi_nfc_caps *caps;
|
||||
};
|
||||
|
||||
static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl)
|
||||
@@ -350,10 +368,29 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
|
||||
goto err_unmap_buf;
|
||||
}
|
||||
|
||||
writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
|
||||
nfc->regs + NFC_REG_CTL);
|
||||
/*
|
||||
* On A23, we suppose the "internal RAM" (p.12 of the NFC user manual)
|
||||
* refers to the NAND controller's internal SRAM. This memory is mapped
|
||||
* and so is accessible from the AHB. It seems that it can also be
|
||||
* accessed by the MBUS. MBUS accesses are mandatory when using the
|
||||
* internal DMA instead of the external DMA engine.
|
||||
*
|
||||
* During DMA I/O operation, either we access this memory from the AHB
|
||||
* by clearing the NFC_RAM_METHOD bit, or we set the bit and use the
|
||||
* MBUS. In this case, we should also configure the MBUS DMA length
|
||||
* NFC_REG_MDMA_CNT(0xC4) to be chunksize * nchunks. NAND I/O over MBUS
|
||||
* are also limited to 32kiB pages.
|
||||
*/
|
||||
if (nfc->caps->sram_through_ahb)
|
||||
writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
|
||||
nfc->regs + NFC_REG_CTL);
|
||||
else
|
||||
writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
|
||||
nfc->regs + NFC_REG_CTL);
|
||||
|
||||
writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
|
||||
writel(chunksize, nfc->regs + NFC_REG_CNT);
|
||||
|
||||
dmat = dmaengine_submit(dmad);
|
||||
|
||||
ret = dma_submit_error(dmat);
|
||||
@@ -1313,20 +1350,19 @@ pio_fallback:
|
||||
|
||||
static int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page)
|
||||
{
|
||||
nand->pagebuf = -1;
|
||||
u8 *buf = nand_get_data_buf(nand);
|
||||
|
||||
return nand->ecc.read_page(nand, nand->data_buf, 1, page);
|
||||
return nand->ecc.read_page(nand, buf, 1, page);
|
||||
}
|
||||
|
||||
static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
u8 *buf = nand_get_data_buf(nand);
|
||||
int ret;
|
||||
|
||||
nand->pagebuf = -1;
|
||||
|
||||
memset(nand->data_buf, 0xff, mtd->writesize);
|
||||
ret = nand->ecc.write_page(nand, nand->data_buf, 1, page);
|
||||
memset(buf, 0xff, mtd->writesize);
|
||||
ret = nand->ecc.write_page(nand, buf, 1, page);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1724,8 +1760,8 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand)
|
||||
nand->options |= NAND_SUBPAGE_READ;
|
||||
|
||||
if (!ecc->size) {
|
||||
ecc->size = nand->ecc_step_ds;
|
||||
ecc->strength = nand->ecc_strength_ds;
|
||||
ecc->size = nand->base.eccreq.step_size;
|
||||
ecc->strength = nand->base.eccreq.strength;
|
||||
}
|
||||
|
||||
if (!ecc->size || !ecc->strength)
|
||||
@@ -2088,6 +2124,12 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
||||
goto out_mod_clk_unprepare;
|
||||
}
|
||||
|
||||
nfc->caps = of_device_get_match_data(&pdev->dev);
|
||||
if (!nfc->caps) {
|
||||
ret = -EINVAL;
|
||||
goto out_ahb_reset_reassert;
|
||||
}
|
||||
|
||||
ret = sunxi_nfc_rst(nfc);
|
||||
if (ret)
|
||||
goto out_ahb_reset_reassert;
|
||||
@@ -2102,12 +2144,12 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
||||
if (nfc->dmac) {
|
||||
struct dma_slave_config dmac_cfg = { };
|
||||
|
||||
dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA;
|
||||
dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
|
||||
dmac_cfg.dst_addr = dmac_cfg.src_addr;
|
||||
dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
|
||||
dmac_cfg.src_maxburst = 4;
|
||||
dmac_cfg.dst_maxburst = 4;
|
||||
dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
|
||||
dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
|
||||
dmaengine_slave_config(nfc->dmac, &dmac_cfg);
|
||||
} else {
|
||||
dev_warn(dev, "failed to request rxtx DMA channel\n");
|
||||
@@ -2152,8 +2194,26 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
|
||||
.reg_io_data = NFC_REG_A10_IO_DATA,
|
||||
.dma_maxburst = 4,
|
||||
};
|
||||
|
||||
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
|
||||
.sram_through_ahb = true,
|
||||
.reg_io_data = NFC_REG_A23_IO_DATA,
|
||||
.dma_maxburst = 8,
|
||||
};
|
||||
|
||||
static const struct of_device_id sunxi_nfc_ids[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-nand" },
|
||||
{
|
||||
.compatible = "allwinner,sun4i-a10-nand",
|
||||
.data = &sunxi_nfc_a10_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "allwinner,sun8i-a23-nand-controller",
|
||||
.data = &sunxi_nfc_a23_caps,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
|
||||
|
@@ -853,7 +853,7 @@ static int tegra_nand_get_strength(struct nand_chip *chip, const int *strength,
|
||||
} else {
|
||||
strength_sel = strength[i];
|
||||
|
||||
if (strength_sel < chip->ecc_strength_ds)
|
||||
if (strength_sel < chip->base.eccreq.strength)
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -917,9 +917,9 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.steps = mtd->writesize / chip->ecc.size;
|
||||
if (chip->ecc_step_ds != 512) {
|
||||
if (chip->base.eccreq.step_size != 512) {
|
||||
dev_err(ctrl->dev, "Unsupported step size %d\n",
|
||||
chip->ecc_step_ds);
|
||||
chip->base.eccreq.step_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -950,7 +950,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
|
||||
if (ret < 0) {
|
||||
dev_err(ctrl->dev,
|
||||
"No valid strength found, minimum %d\n",
|
||||
chip->ecc_strength_ds);
|
||||
chip->base.eccreq.strength);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -850,6 +850,9 @@ static int vf610_nfc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev);
|
||||
if (!of_id)
|
||||
return -ENODEV;
|
||||
|
||||
nfc->variant = (enum vf610_nfc_variant)of_id->data;
|
||||
|
||||
for_each_available_child_of_node(nfc->dev->of_node, child) {
|
||||
|
@@ -19,21 +19,6 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
static void spinand_cache_op_adjust_colum(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req,
|
||||
u16 *column)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
unsigned int shift;
|
||||
|
||||
if (nand->memorg.planes_per_lun < 2)
|
||||
return;
|
||||
|
||||
/* The plane number is passed in MSB just above the column address */
|
||||
shift = fls(nand->memorg.pagesize);
|
||||
*column |= req->pos.plane << shift;
|
||||
}
|
||||
|
||||
static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
|
||||
{
|
||||
struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg,
|
||||
@@ -227,27 +212,21 @@ static int spinand_load_page_op(struct spinand_device *spinand,
|
||||
static int spinand_read_from_cache_op(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct spi_mem_op op = *spinand->op_templates.read_cache;
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct nand_page_io_req adjreq = *req;
|
||||
struct spi_mem_dirmap_desc *rdesc;
|
||||
unsigned int nbytes = 0;
|
||||
void *buf = NULL;
|
||||
u16 column = 0;
|
||||
int ret;
|
||||
ssize_t ret;
|
||||
|
||||
if (req->datalen) {
|
||||
adjreq.datalen = nanddev_page_size(nand);
|
||||
adjreq.dataoffs = 0;
|
||||
adjreq.databuf.in = spinand->databuf;
|
||||
buf = spinand->databuf;
|
||||
nbytes = adjreq.datalen;
|
||||
nbytes = nanddev_page_size(nand);
|
||||
column = 0;
|
||||
}
|
||||
|
||||
if (req->ooblen) {
|
||||
adjreq.ooblen = nanddev_per_page_oobsize(nand);
|
||||
adjreq.ooboffs = 0;
|
||||
adjreq.oobbuf.in = spinand->oobbuf;
|
||||
nbytes += nanddev_per_page_oobsize(nand);
|
||||
if (!buf) {
|
||||
buf = spinand->oobbuf;
|
||||
@@ -255,28 +234,19 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
|
||||
}
|
||||
}
|
||||
|
||||
spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
|
||||
op.addr.val = column;
|
||||
rdesc = spinand->dirmaps[req->pos.plane].rdesc;
|
||||
|
||||
/*
|
||||
* Some controllers are limited in term of max RX data size. In this
|
||||
* case, just repeat the READ_CACHE operation after updating the
|
||||
* column.
|
||||
*/
|
||||
while (nbytes) {
|
||||
op.data.buf.in = buf;
|
||||
op.data.nbytes = nbytes;
|
||||
ret = spi_mem_adjust_op_size(spinand->spimem, &op);
|
||||
if (ret)
|
||||
ret = spi_mem_dirmap_read(rdesc, column, nbytes, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = spi_mem_exec_op(spinand->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!ret || ret > nbytes)
|
||||
return -EIO;
|
||||
|
||||
buf += op.data.nbytes;
|
||||
nbytes -= op.data.nbytes;
|
||||
op.addr.val += op.data.nbytes;
|
||||
nbytes -= ret;
|
||||
column += ret;
|
||||
buf += ret;
|
||||
}
|
||||
|
||||
if (req->datalen)
|
||||
@@ -300,14 +270,12 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
|
||||
static int spinand_write_to_cache_op(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct spi_mem_op op = *spinand->op_templates.write_cache;
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct nand_page_io_req adjreq = *req;
|
||||
struct spi_mem_dirmap_desc *wdesc;
|
||||
unsigned int nbytes, column = 0;
|
||||
void *buf = spinand->databuf;
|
||||
unsigned int nbytes;
|
||||
u16 column = 0;
|
||||
int ret;
|
||||
ssize_t ret;
|
||||
|
||||
/*
|
||||
* Looks like PROGRAM LOAD (AKA write cache) does not necessarily reset
|
||||
@@ -318,12 +286,6 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
|
||||
*/
|
||||
nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
|
||||
memset(spinand->databuf, 0xff, nbytes);
|
||||
adjreq.dataoffs = 0;
|
||||
adjreq.datalen = nanddev_page_size(nand);
|
||||
adjreq.databuf.out = spinand->databuf;
|
||||
adjreq.ooblen = nanddev_per_page_oobsize(nand);
|
||||
adjreq.ooboffs = 0;
|
||||
adjreq.oobbuf.out = spinand->oobbuf;
|
||||
|
||||
if (req->datalen)
|
||||
memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
|
||||
@@ -340,42 +302,19 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
|
||||
req->ooblen);
|
||||
}
|
||||
|
||||
spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
|
||||
wdesc = spinand->dirmaps[req->pos.plane].wdesc;
|
||||
|
||||
op = *spinand->op_templates.write_cache;
|
||||
op.addr.val = column;
|
||||
|
||||
/*
|
||||
* Some controllers are limited in term of max TX data size. In this
|
||||
* case, split the operation into one LOAD CACHE and one or more
|
||||
* LOAD RANDOM CACHE.
|
||||
*/
|
||||
while (nbytes) {
|
||||
op.data.buf.out = buf;
|
||||
op.data.nbytes = nbytes;
|
||||
|
||||
ret = spi_mem_adjust_op_size(spinand->spimem, &op);
|
||||
if (ret)
|
||||
ret = spi_mem_dirmap_write(wdesc, column, nbytes, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = spi_mem_exec_op(spinand->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!ret || ret > nbytes)
|
||||
return -EIO;
|
||||
|
||||
buf += op.data.nbytes;
|
||||
nbytes -= op.data.nbytes;
|
||||
op.addr.val += op.data.nbytes;
|
||||
|
||||
/*
|
||||
* We need to use the RANDOM LOAD CACHE operation if there's
|
||||
* more than one iteration, because the LOAD operation might
|
||||
* reset the cache to 0xff.
|
||||
*/
|
||||
if (nbytes) {
|
||||
column = op.addr.val;
|
||||
op = *spinand->op_templates.update_cache;
|
||||
op.addr.val = column;
|
||||
}
|
||||
nbytes -= ret;
|
||||
column += ret;
|
||||
buf += ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -755,6 +694,59 @@ static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spinand_create_dirmap(struct spinand_device *spinand,
|
||||
unsigned int plane)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct spi_mem_dirmap_info info = {
|
||||
.length = nanddev_page_size(nand) +
|
||||
nanddev_per_page_oobsize(nand),
|
||||
};
|
||||
struct spi_mem_dirmap_desc *desc;
|
||||
|
||||
/* The plane number is passed in MSB just above the column address */
|
||||
info.offset = plane << fls(nand->memorg.pagesize);
|
||||
|
||||
info.op_tmpl = *spinand->op_templates.update_cache;
|
||||
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
|
||||
spinand->spimem, &info);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
spinand->dirmaps[plane].wdesc = desc;
|
||||
|
||||
info.op_tmpl = *spinand->op_templates.read_cache;
|
||||
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
|
||||
spinand->spimem, &info);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
spinand->dirmaps[plane].rdesc = desc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_create_dirmaps(struct spinand_device *spinand)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
int i, ret;
|
||||
|
||||
spinand->dirmaps = devm_kzalloc(&spinand->spimem->spi->dev,
|
||||
sizeof(*spinand->dirmaps) *
|
||||
nand->memorg.planes_per_lun,
|
||||
GFP_KERNEL);
|
||||
if (!spinand->dirmaps)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nand->memorg.planes_per_lun; i++) {
|
||||
ret = spinand_create_dirmap(spinand, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_ops spinand_ops = {
|
||||
.erase = spinand_erase,
|
||||
.markbad = spinand_markbad,
|
||||
@@ -1012,6 +1004,14 @@ static int spinand_init(struct spinand_device *spinand)
|
||||
goto err_free_bufs;
|
||||
}
|
||||
|
||||
ret = spinand_create_dirmaps(spinand);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to create direct mappings for read/write operations (err = %d)\n",
|
||||
ret);
|
||||
goto err_manuf_cleanup;
|
||||
}
|
||||
|
||||
/* After power up, all blocks are locked, so unlock them here. */
|
||||
for (i = 0; i < nand->memorg.ntargets; i++) {
|
||||
ret = spinand_select_target(spinand, i);
|
||||
@@ -1037,6 +1037,7 @@ static int spinand_init(struct spinand_device *spinand)
|
||||
mtd->_block_markbad = spinand_mtd_block_markbad;
|
||||
mtd->_block_isreserved = spinand_mtd_block_isreserved;
|
||||
mtd->_erase = spinand_mtd_erase;
|
||||
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
|
||||
|
||||
if (spinand->eccinfo.ooblayout)
|
||||
mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
|
||||
|
@@ -162,7 +162,7 @@ static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = {
|
||||
|
||||
static const struct spinand_info gigadevice_spinand_table[] = {
|
||||
SPINAND_INFO("GD5F1GQ4xA", 0xF1,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -171,7 +171,7 @@ static const struct spinand_info gigadevice_spinand_table[] = {
|
||||
SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
|
||||
gd5fxgq4xa_ecc_get_status)),
|
||||
SPINAND_INFO("GD5F2GQ4xA", 0xF2,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -180,7 +180,7 @@ static const struct spinand_info gigadevice_spinand_table[] = {
|
||||
SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
|
||||
gd5fxgq4xa_ecc_get_status)),
|
||||
SPINAND_INFO("GD5F4GQ4xA", 0xF4,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 4096, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -189,7 +189,7 @@ static const struct spinand_info gigadevice_spinand_table[] = {
|
||||
SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
|
||||
gd5fxgq4xa_ecc_get_status)),
|
||||
SPINAND_INFO("GD5F1GQ4UExxG", 0xd1,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
|
@@ -100,7 +100,7 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
|
||||
|
||||
static const struct spinand_info macronix_spinand_table[] = {
|
||||
SPINAND_INFO("MX35LF1GE4AB", 0x12,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -109,7 +109,7 @@ static const struct spinand_info macronix_spinand_table[] = {
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35LF2GE4AB", 0x22,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 20, 2, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
|
@@ -92,7 +92,7 @@ static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand,
|
||||
|
||||
static const struct spinand_info micron_spinand_table[] = {
|
||||
SPINAND_INFO("MT29F2G01ABAGD", 0x24,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
|
@@ -96,7 +96,7 @@ static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand,
|
||||
static const struct spinand_info toshiba_spinand_table[] = {
|
||||
/* 3.3V 1Gb */
|
||||
SPINAND_INFO("TC58CVG0S3", 0xC2,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -106,7 +106,7 @@ static const struct spinand_info toshiba_spinand_table[] = {
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 3.3V 2Gb */
|
||||
SPINAND_INFO("TC58CVG1S3", 0xCB,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -116,7 +116,7 @@ static const struct spinand_info toshiba_spinand_table[] = {
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 3.3V 4Gb */
|
||||
SPINAND_INFO("TC58CVG2S0", 0xCD,
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -126,7 +126,7 @@ static const struct spinand_info toshiba_spinand_table[] = {
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 1.8V 1Gb */
|
||||
SPINAND_INFO("TC58CYG0S3", 0xB2,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -136,7 +136,7 @@ static const struct spinand_info toshiba_spinand_table[] = {
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 1.8V 2Gb */
|
||||
SPINAND_INFO("TC58CYG1S3", 0xBB,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -146,7 +146,7 @@ static const struct spinand_info toshiba_spinand_table[] = {
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 1.8V 4Gb */
|
||||
SPINAND_INFO("TC58CYG2S0", 0xBD,
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
|
@@ -76,7 +76,7 @@ static int w25m02gv_select_target(struct spinand_device *spinand,
|
||||
|
||||
static const struct spinand_info winbond_spinand_table[] = {
|
||||
SPINAND_INFO("W25M02GV", 0xAB,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
@@ -85,7 +85,7 @@ static const struct spinand_info winbond_spinand_table[] = {
|
||||
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
|
||||
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
|
||||
SPINAND_INFO("W25N01GV", 0xAA,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
|
Reference in New Issue
Block a user