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:
Linus Torvalds
2019-05-12 17:57:52 -04:00
187 changed files with 4976 additions and 3075 deletions

View File

@@ -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

View File

@@ -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/

View File

@@ -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 = &microchip_sam9x60_nc_caps,
},
/* Support for old/deprecated bindings: */
{
.compatible = "atmel,at91rm9200-nand",

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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__ */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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__,

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View 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

View 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

View 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");

View 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__ */

View 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, &params, 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, &params, 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");

View 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, &reg);
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");

View 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");

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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__ */

View File

@@ -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, &params, 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, &params, 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");

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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");
}

View File

@@ -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);

View File

@@ -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,
&micron_nand_on_die_4_ooblayout_ops);
else
mtd_set_ooblayout(mtd,
&micron_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;

View File

@@ -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)) {

View File

@@ -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;
}

View File

@@ -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 &&

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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)) {

View File

@@ -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]);

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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) {