whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit eaa76499711535fd64d747cc4ef0d78ab0fd41c6
parent b3cc2bfe7244e848f5e8caa77bbdc72c04abd17c
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Tue, 25 Dec 2018 12:49:46 -0800

Merge tag 'mtd/for-4.21' of git://git.infradead.org/linux-mtd

Pull mtd updates from Boris Brezillon:
 "SPI NOR Core changes:
   - Parse the 4BAIT SFDP section
   - Add a bunch of SPI NOR entries to the flash_info table
   - Add the concept of SFDP fixups and use it to fix a bug on MX25L25635F
   - A bunch of minor cleanups/comestic changes

  NAND core changes:
   - kernel-doc miscellaneous fixes.
   - Third batch of fixes/cleanup to the raw NAND core impacting various
     controller drivers (ams-delta, marvell, fsmc, denali, tegra,
     vf610):
      * Stop to pass mtd_info objects to internal functions
      * Reorganize code to avoid forward declarations
      * Drop useless test in nand_legacy_set_defaults()
      * Move nand_exec_op() to internal.h
      * Add nand_[de]select_target() helpers
      * Pass the CS line to be selected in struct nand_operation
      * Make ->select_chip() optional when ->exec_op() is implemented
      * Deprecate the ->select_chip() hook
      * Move the ->exec_op() method to nand_controller_ops
      * Move ->setup_data_interface() to nand_controller_ops
      * Deprecate the dummy_controller field
      * Fix JEDEC detection
      * Provide a helper for polling GPIO R/B pin

  Raw NAND chip drivers changes:
   - Macronix:
      * Flag 1.8V AC chips with a broken GET_FEATURES(TIMINGS)

  Raw NAND controllers drivers changes:
   - Ams-delta:
      * Fix the error path
      * SPDX tag added
      * May be compiled with COMPILE_TEST=y
      * Conversion to ->exec_op() interface
      * Drop .IOADDR_R/W use
      * Use GPIO API for data I/O
   - Denali:
      * Remove denali_reset_banks()
      * Remove ->dev_ready() hook
      * Include <linux/bits.h> instead of <linux/bitops.h>
      * Changes to comply with the above fixes/cleanup done in the core.
   - FSMC:
      * Add an SPDX tag to replace the license text
      * Make conversion from chip to fsmc consistent
      * Fix unchecked return value in fsmc_read_page_hwecc
      * Changes to comply with the above fixes/cleanup done in the core.
   - Marvell:
      * Prevent timeouts on a loaded machine (fix)
      * Changes to comply with the above fixes/cleanup done in the core.
   - OMAP2:
      * Pass the parent of pdev to dma_request_chan() (fix)
   - R852:
      * Use generic DMA API
   - sh_flctl:
      * Convert to SPDX identifiers
   - Sunxi:
      * Write pageprog related opcodes to the right register: WCMD_SET (fix)
   - Tegra:
      * Stop implementing ->select_chip()
   - VF610:
      * Add an SPDX tag to replace the license text
      * Changes to comply with the above fixes/cleanup done in the core.
   - Various trivial/spelling/coding style fixes.

  SPI-NAND drivers changes:
   - Remove the depreacated mt29f_spinand driver from staging.
   - Add support for:
      * Toshiba TC58CVG2S0H
      * GigaDevice GD5FxGQ4xA
      * Winbond W25N01GV

  JFFS2 changes:
   - Fix a lockdep issue

  MTD changes:
   - Rework the physmap driver to merge gpio-addr-flash and physmap_of
     in it
   - Add a new compatible for RedBoot partitions
   - Make sub-partitions RW if the parent partition was RO because of a
     mis-alignment
   - Add pinctrl support to the
   - Addition of /* fall-through */ comments where appropriate
   - Various minor fixes and cleanups

  Other changes:
   - Update my email address"

* tag 'mtd/for-4.21' of git://git.infradead.org/linux-mtd: (108 commits)
  mtd: rawnand: sunxi: Write pageprog related opcodes to WCMD_SET
  MAINTAINERS: Update my email address
  mtd: rawnand: marvell: prevent timeouts on a loaded machine
  mtd: rawnand: omap2: Pass the parent of pdev to dma_request_chan()
  mtd: rawnand: Fix JEDEC detection
  mtd: spi-nor: Add support for is25lp016d
  mtd: spi-nor: parse SFDP 4-byte Address Instruction Table
  mtd: spi-nor: Add 4B_OPCODES flag to is25lp256
  mtd: spi-nor: Add an SPDX tag to spi-nor.{c,h}
  mtd: spi-nor: Make the enable argument passed to set_byte() a bool
  mtd: spi-nor: Stop passing flash_info around
  mtd: spi-nor: Avoid forward declaration of internal functions
  mtd: spi-nor: Drop inline on all internal helpers
  mtd: spi-nor: Add a post BFPT fixup for MX25L25635E
  mtd: spi-nor: Add a post BFPT parsing fixup hook
  mtd: spi-nor: Add the SNOR_F_4B_OPCODES flag
  mtd: spi-nor: cast to u64 to avoid uint overflows
  mtd: spi-nor: Add support for IS25LP032/064
  mtd: spi-nor: add entry for mt35xu512aba flash
  mtd: spi-nor: add macros related to MICRON flash
  ...

Diffstat:
M.mailmap | 7++++---
MDocumentation/devicetree/bindings/mtd/mtd-physmap.txt | 2++
ADocumentation/devicetree/bindings/mtd/partitions/redboot-fis.txt | 27+++++++++++++++++++++++++++
MMAINTAINERS | 8++++----
March/arm/mach-omap1/board-ams-delta.c | 22++++++++++------------
Mdrivers/mtd/Kconfig | 52+---------------------------------------------------
Mdrivers/mtd/Makefile | 1-
Mdrivers/mtd/chips/cfi_cmdset_0020.c | 6++++++
Mdrivers/mtd/devices/block2mtd.c | 2++
Mdrivers/mtd/devices/docg3.c | 16++++++++--------
Mdrivers/mtd/devices/docg3.h | 11-----------
Mdrivers/mtd/maps/Kconfig | 37++++++++++++++-----------------------
Mdrivers/mtd/maps/Makefile | 11++++-------
Ddrivers/mtd/maps/gpio-addr-flash.c | 281-------------------------------------------------------------------------------
Ddrivers/mtd/maps/latch-addr-flash.c | 229-------------------------------------------------------------------------------
Adrivers/mtd/maps/physmap-core.c | 665+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/mtd/maps/physmap-gemini.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/mtd/maps/physmap-gemini.h | 17+++++++++++++++++
Adrivers/mtd/maps/physmap-versatile.c | 254+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/mtd/maps/physmap-versatile.h | 17+++++++++++++++++
Ddrivers/mtd/maps/physmap.c | 280-------------------------------------------------------------------------------
Ddrivers/mtd/maps/physmap_of_core.c | 368-------------------------------------------------------------------------------
Ddrivers/mtd/maps/physmap_of_gemini.c | 97-------------------------------------------------------------------------------
Ddrivers/mtd/maps/physmap_of_gemini.h | 17-----------------
Ddrivers/mtd/maps/physmap_of_versatile.c | 254-------------------------------------------------------------------------------
Ddrivers/mtd/maps/physmap_of_versatile.h | 17-----------------
Mdrivers/mtd/mtdblock.c | 2+-
Mdrivers/mtd/mtdcore.c | 8+++++---
Mdrivers/mtd/mtdpart.c | 16+++++++++++++---
Mdrivers/mtd/mtdswap.c | 13+------------
Mdrivers/mtd/nand/raw/Kconfig | 2+-
Mdrivers/mtd/nand/raw/ams-delta.c | 263++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mdrivers/mtd/nand/raw/atmel/nand-controller.c | 9+++++----
Mdrivers/mtd/nand/raw/au1550nd.c | 2+-
Mdrivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c | 2+-
Mdrivers/mtd/nand/raw/cafe_nand.c | 4++--
Mdrivers/mtd/nand/raw/davinci_nand.c | 4++--
Mdrivers/mtd/nand/raw/denali.c | 59+++++------------------------------------------------------
Mdrivers/mtd/nand/raw/denali.h | 2+-
Mdrivers/mtd/nand/raw/diskonchip.c | 4++--
Mdrivers/mtd/nand/raw/fsl_elbc_nand.c | 2+-
Mdrivers/mtd/nand/raw/fsl_ifc_nand.c | 2+-
Mdrivers/mtd/nand/raw/fsl_upm.c | 2+-
Mdrivers/mtd/nand/raw/fsmc_nand.c | 303+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdrivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 29++++++++++++++++++-----------
Mdrivers/mtd/nand/raw/hisi504_nand.c | 4++--
Mdrivers/mtd/nand/raw/internals.h | 33+++++++++++++++++++++++++++++++++
Mdrivers/mtd/nand/raw/jz4740_nand.c | 8++++----
Mdrivers/mtd/nand/raw/jz4780_bch.c | 2++
Mdrivers/mtd/nand/raw/jz4780_nand.c | 2+-
Mdrivers/mtd/nand/raw/lpc32xx_mlc.c | 2+-
Mdrivers/mtd/nand/raw/lpc32xx_slc.c | 2+-
Mdrivers/mtd/nand/raw/marvell_nand.c | 51++++++++++++++++++++++++++++++++++++---------------
Mdrivers/mtd/nand/raw/mpc5121_nfc.c | 4++--
Mdrivers/mtd/nand/raw/mtk_nand.c | 4++--
Mdrivers/mtd/nand/raw/mxc_nand.c | 16+++++++++++++---
Mdrivers/mtd/nand/raw/nand_base.c | 769+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mdrivers/mtd/nand/raw/nand_bbt.c | 285++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mdrivers/mtd/nand/raw/nand_hynix.c | 8++++----
Mdrivers/mtd/nand/raw/nand_jedec.c | 2++
Mdrivers/mtd/nand/raw/nand_legacy.c | 35++++++++++++++++-------------------
Mdrivers/mtd/nand/raw/nand_macronix.c | 7+++++++
Mdrivers/mtd/nand/raw/nandsim.c | 19++++---------------
Mdrivers/mtd/nand/raw/ndfc.c | 2+-
Mdrivers/mtd/nand/raw/omap2.c | 2+-
Mdrivers/mtd/nand/raw/plat_nand.c | 2+-
Mdrivers/mtd/nand/raw/qcom_nandc.c | 2+-
Mdrivers/mtd/nand/raw/r852.c | 30+++++++++++++++---------------
Mdrivers/mtd/nand/raw/s3c2410.c | 7++++---
Mdrivers/mtd/nand/raw/sh_flctl.c | 21++++-----------------
Mdrivers/mtd/nand/raw/sm_common.c | 2+-
Mdrivers/mtd/nand/raw/sunxi_nand.c | 6+++---
Mdrivers/mtd/nand/raw/tango_nand.c | 4++--
Mdrivers/mtd/nand/raw/tegra_nand.c | 32++++++++++++++------------------
Mdrivers/mtd/nand/raw/vf610_nfc.c | 98++++++++++++++++++++++++++++++++++++-------------------------------------------
Mdrivers/mtd/nand/raw/xway_nand.c | 2+-
Mdrivers/mtd/nand/spi/Makefile | 2+-
Mdrivers/mtd/nand/spi/core.c | 2++
Adrivers/mtd/nand/spi/gigadevice.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/mtd/nand/spi/toshiba.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/mtd/nand/spi/winbond.c | 8++++++++
Mdrivers/mtd/nftlmount.c | 39++++++++++++++++++++-------------------
Mdrivers/mtd/parsers/Kconfig | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/mtd/parsers/Makefile | 1+
Adrivers/mtd/parsers/redboot.c | 332+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddrivers/mtd/redboot.c | 302------------------------------------------------------------------------------
Mdrivers/mtd/spi-nor/spi-nor.c | 1824+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mdrivers/staging/Kconfig | 2--
Mdrivers/staging/Makefile | 1-
Ddrivers/staging/mt29f_spinand/Kconfig | 16----------------
Ddrivers/staging/mt29f_spinand/Makefile | 1-
Ddrivers/staging/mt29f_spinand/TODO | 13-------------
Ddrivers/staging/mt29f_spinand/mt29f_spinand.c | 980-------------------------------------------------------------------------------
Ddrivers/staging/mt29f_spinand/mt29f_spinand.h | 106-------------------------------------------------------------------------------
Mfs/jffs2/super.c | 3++-
Minclude/linux/mtd/cfi.h | 1+
Minclude/linux/mtd/mtd.h | 3++-
Minclude/linux/mtd/rawnand.h | 158++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Minclude/linux/mtd/sh_flctl.h | 16++--------------
Minclude/linux/mtd/spi-nor.h | 11+++++------
Minclude/linux/mtd/spinand.h | 2++
101 files changed, 4223 insertions(+), 5029 deletions(-)

diff --git a/.mailmap b/.mailmap @@ -36,9 +36,10 @@ Bart Van Assche <bvanassche@acm.org> <bart.vanassche@sandisk.com> Ben Gardner <bgardner@wabtec.com> Ben M Cahill <ben.m.cahill@intel.com> Björn Steinbrink <B.Steinbrink@gmx.de> -Boris Brezillon <boris.brezillon@bootlin.com> <boris.brezillon@free-electrons.com> -Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon.dev@gmail.com> -Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon@overkiz.com> +Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com> +Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@free-electrons.com> +Boris Brezillon <bbrezillon@kernel.org> <b.brezillon.dev@gmail.com> +Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com> Brian Avery <b.avery@hp.com> Brian King <brking@us.ibm.com> Christoph Hellwig <hch@lst.de> diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt @@ -29,6 +29,8 @@ file systems on embedded devices. - use-advanced-sector-protection: boolean to enable support for the advanced sector protection (Spansion: PPB - Persistent Protection Bits) locking. + - addr-gpios : (optional) List of GPIO descriptors that will be used to + address the MSBs address lines. The order goes from LSB to MSB. For JEDEC compatible devices, the following additional properties are defined: diff --git a/Documentation/devicetree/bindings/mtd/partitions/redboot-fis.txt b/Documentation/devicetree/bindings/mtd/partitions/redboot-fis.txt @@ -0,0 +1,27 @@ +RedBoot FLASH Image System (FIS) Partitions +=========================================== + +The FLASH Image System (FIS) directory is a flash description +format closely associated with the RedBoot boot loader. + +It uses one single flash eraseblock in the flash to store an index of +all images in the flash. + +This block size will vary depending on flash but is typically +32 KB in size. + +Required properties: +- compatible : (required) must be "redboot-fis" +- fis-index-block : (required) a index to the eraseblock containing + the FIS directory on this device. On a flash memory with 32KB + eraseblocks, 0 means the first eraseblock at 0x00000000, 1 means the + second eraseblock at 0x00008000 and so on. + +Example: + +flash@0 { + partitions { + compatible = "redboot-fis"; + fis-index-block = <0>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS @@ -4915,7 +4915,7 @@ F: Documentation/gpu/meson.rst T: git git://anongit.freedesktop.org/drm/drm-misc DRM DRIVERS FOR ATMEL HLCDC -M: Boris Brezillon <boris.brezillon@bootlin.com> +M: Boris Brezillon <bbrezillon@kernel.org> L: dri-devel@lists.freedesktop.org S: Supported F: drivers/gpu/drm/atmel-hlcdc/ @@ -8998,7 +8998,7 @@ F: include/uapi/drm/armada_drm.h F: Documentation/devicetree/bindings/display/armada/ MARVELL CRYPTO DRIVER -M: Boris Brezillon <boris.brezillon@bootlin.com> +M: Boris Brezillon <bbrezillon@kernel.org> M: Arnaud Ebalard <arno@natisbad.org> F: drivers/crypto/marvell/ S: Maintained @@ -9709,7 +9709,7 @@ F: mm/ MEMORY TECHNOLOGY DEVICES (MTD) M: David Woodhouse <dwmw2@infradead.org> M: Brian Norris <computersforpeace@gmail.com> -M: Boris Brezillon <boris.brezillon@bootlin.com> +M: Boris Brezillon <bbrezillon@kernel.org> M: Marek Vasut <marek.vasut@gmail.com> M: Richard Weinberger <richard@nod.at> L: linux-mtd@lists.infradead.org @@ -10296,7 +10296,7 @@ S: Supported F: drivers/net/ethernet/myricom/myri10ge/ NAND FLASH SUBSYSTEM -M: Boris Brezillon <boris.brezillon@bootlin.com> +M: Boris Brezillon <bbrezillon@kernel.org> M: Miquel Raynal <miquel.raynal@bootlin.com> R: Richard Weinberger <richard@nod.at> L: linux-mtd@lists.infradead.org diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c @@ -296,23 +296,13 @@ struct modem_private_data { static struct modem_private_data modem_priv; -static struct resource ams_delta_nand_resources[] = { - [0] = { - .start = OMAP1_MPUIO_BASE, - .end = OMAP1_MPUIO_BASE + - OMAP_MPUIO_IO_CNTL + sizeof(u32) - 1, - .flags = IORESOURCE_MEM, - }, -}; - static struct platform_device ams_delta_nand_device = { .name = "ams-delta-nand", .id = -1, - .num_resources = ARRAY_SIZE(ams_delta_nand_resources), - .resource = ams_delta_nand_resources, }; -#define OMAP_GPIO_LABEL "gpio-0-15" +#define OMAP_GPIO_LABEL "gpio-0-15" +#define OMAP_MPUIO_LABEL "mpuio" static struct gpiod_lookup_table ams_delta_nand_gpio_table = { .table = { @@ -324,6 +314,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = { GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0), GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0), GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0), { }, }, }; diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig @@ -22,56 +22,6 @@ config MTD_TESTS WARNING: some of the tests will ERASE entire MTD device which they test. Do not use these tests unless you really know what you do. -config MTD_REDBOOT_PARTS - tristate "RedBoot partition table parsing" - help - RedBoot is a ROM monitor and bootloader which deals with multiple - 'images' in flash devices by putting a table one of the erase - blocks on the device, similar to a partition table, which gives - the offsets, lengths and names of all the images stored in the - flash. - - If you need code which can detect and parse this table, and register - MTD 'partitions' corresponding to each image in the table, enable - this option. - - You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The - SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for - example. - -if MTD_REDBOOT_PARTS - -config MTD_REDBOOT_DIRECTORY_BLOCK - int "Location of RedBoot partition table" - default "-1" - help - This option is the Linux counterpart to the - CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time - option. - - The option specifies which Flash sectors holds the RedBoot - partition table. A zero or positive value gives an absolute - erase block number. A negative value specifies a number of - sectors before the end of the device. - - For example "2" means block number 2, "-1" means the last - block and "-2" means the penultimate block. - -config MTD_REDBOOT_PARTS_UNALLOCATED - bool "Include unallocated flash regions" - help - If you need to register each unallocated flash region as a MTD - 'partition', enable this option. - -config MTD_REDBOOT_PARTS_READONLY - bool "Force read-only for RedBoot system images" - help - If you need to force read-only for 'RedBoot', 'RedBoot Config' and - 'FIS directory' images, enable this option. - -endif # MTD_REDBOOT_PARTS - config MTD_CMDLINE_PARTS tristate "Command line partition table parsing" depends on MTD @@ -144,7 +94,7 @@ config MTD_BCM63XX_PARTS depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST select CRC32 help - This provides partions parsing for BCM63xx devices with CFE + This provides partition parsing for BCM63xx devices with CFE bootloaders. config MTD_BCM47XX_PARTS diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_MTD) += mtd.o mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o -obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -324,6 +324,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof case FL_JEDEC_QUERY: map_write(map, CMD(0x70), cmd_addr); chip->state = FL_STATUS; + /* Fall through */ case FL_STATUS: status = map_read(map, cmd_addr); @@ -461,6 +462,7 @@ static int do_write_buffer(struct map_info *map, struct flchip *chip, #ifdef DEBUG_CFI_FEATURES printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr)); #endif + /* Fall through */ case FL_STATUS: status = map_read(map, cmd_adr); @@ -754,6 +756,7 @@ retry: case FL_READY: map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; + /* Fall through */ case FL_STATUS: status = map_read(map, adr); @@ -995,6 +998,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) * as the whole point is that nobody can do anything * with the chip now anyway. */ + /* Fall through */ case FL_SYNCING: mutex_unlock(&chip->mutex); break; @@ -1050,6 +1054,7 @@ retry: case FL_READY: map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; + /* Fall through */ case FL_STATUS: status = map_read(map, adr); @@ -1196,6 +1201,7 @@ retry: case FL_READY: map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; + /* Fall through */ case FL_STATUS: status = map_read(map, adr); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c @@ -329,8 +329,10 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base) switch (**endp) { case 'G' : result *= 1024; + /* fall through */ case 'M': result *= 1024; + /* fall through */ case 'K': case 'k': result *= 1024; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c @@ -1603,7 +1603,7 @@ static void doc_unregister_sysfs(struct platform_device *pdev, /* * Debug sysfs entries */ -static int dbg_flashctrl_show(struct seq_file *s, void *p) +static int flashcontrol_show(struct seq_file *s, void *p) { struct docg3 *docg3 = (struct docg3 *)s->private; @@ -1623,9 +1623,9 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p) return 0; } -DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show); +DEFINE_SHOW_ATTRIBUTE(flashcontrol); -static int dbg_asicmode_show(struct seq_file *s, void *p) +static int asic_mode_show(struct seq_file *s, void *p) { struct docg3 *docg3 = (struct docg3 *)s->private; @@ -1660,9 +1660,9 @@ static int dbg_asicmode_show(struct seq_file *s, void *p) seq_puts(s, ")\n"); return 0; } -DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show); +DEFINE_SHOW_ATTRIBUTE(asic_mode); -static int dbg_device_id_show(struct seq_file *s, void *p) +static int device_id_show(struct seq_file *s, void *p) { struct docg3 *docg3 = (struct docg3 *)s->private; int id; @@ -1674,9 +1674,9 @@ static int dbg_device_id_show(struct seq_file *s, void *p) seq_printf(s, "DeviceId = %d\n", id); return 0; } -DEBUGFS_RO_ATTR(device_id, dbg_device_id_show); +DEFINE_SHOW_ATTRIBUTE(device_id); -static int dbg_protection_show(struct seq_file *s, void *p) +static int protection_show(struct seq_file *s, void *p) { struct docg3 *docg3 = (struct docg3 *)s->private; int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; @@ -1726,7 +1726,7 @@ static int dbg_protection_show(struct seq_file *s, void *p) !!(dps1 & DOC_DPS_KEY_OK)); return 0; } -DEBUGFS_RO_ATTR(protection, dbg_protection_show); +DEFINE_SHOW_ATTRIBUTE(protection); static void __init doc_dbg_register(struct mtd_info *floor) { diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h @@ -317,17 +317,6 @@ struct docg3 { #define doc_info(fmt, arg...) dev_info(docg3->dev, (fmt), ## arg) #define doc_dbg(fmt, arg...) dev_dbg(docg3->dev, (fmt), ## arg) #define doc_vdbg(fmt, arg...) dev_vdbg(docg3->dev, (fmt), ## arg) - -#define DEBUGFS_RO_ATTR(name, show_fct) \ - static int name##_open(struct inode *inode, struct file *file) \ - { return single_open(file, show_fct, inode->i_private); } \ - static const struct file_operations name##_fops = { \ - .owner = THIS_MODULE, \ - .open = name##_open, \ - .llseek = seq_lseek, \ - .read = seq_read, \ - .release = single_release \ - }; #endif /* diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig @@ -66,15 +66,15 @@ config MTD_PHYSMAP_BANKWIDTH used internally by the CFI drivers. config MTD_PHYSMAP_OF - tristate "Memory device in physical memory map based on OF description" - depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM) + bool "Memory device in physical memory map based on OF description" + depends on OF && MTD_PHYSMAP help This provides a 'mapping' driver which allows the NOR Flash, ROM and RAM driver code to communicate with chips which are mapped physically into the CPU's memory. The mapping description here is taken from OF device tree. -config MTD_PHYSMAP_OF_VERSATILE +config MTD_PHYSMAP_VERSATILE bool "ARM Versatile OF-based physical memory map handling" depends on MTD_PHYSMAP_OF depends on MFD_SYSCON @@ -84,16 +84,26 @@ config MTD_PHYSMAP_OF_VERSATILE platforms, basically to add a VPP (write protection) callback so the flash can be taken out of write protection. -config MTD_PHYSMAP_OF_GEMINI +config MTD_PHYSMAP_GEMINI bool "Cortina Gemini OF-based physical memory map handling" depends on MTD_PHYSMAP_OF depends on MFD_SYSCON + select MTD_COMPLEX_MAPPINGS default ARCH_GEMINI help This provides some extra DT physmap parsing for the Gemini platforms, some detection and setting up parallel mode on the external interface. +config MTD_PHYSMAP_GPIO_ADDR + bool "GPIO-assisted Flash Chip Support" + depends on MTD_PHYSMAP + depends on GPIOLIB || COMPILE_TEST + depends on MTD_COMPLEX_MAPPINGS + help + Extend the physmap driver to allow flashes to be partially + physically addressed and assisted by GPIOs. + config MTD_PMC_MSP_EVM tristate "CFI Flash device mapped on PMC-Sierra MSP" depends on PMC_MSP && MTD_CFI @@ -334,16 +344,6 @@ config MTD_PCMCIA_ANONYMOUS If unsure, say N. -config MTD_GPIO_ADDR - tristate "GPIO-assisted Flash Chip Support" - depends on GPIOLIB || COMPILE_TEST - depends on MTD_COMPLEX_MAPPINGS - help - Map driver which allows flashes to be partially physically addressed - and assisted by GPIOs. - - If compiled as a module, it will be called gpio-addr-flash. - config MTD_UCLINUX bool "Generic uClinux RAM/ROM filesystem support" depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE) @@ -400,13 +400,4 @@ config MTD_PISMO When built as a module, it will be called pismo.ko -config MTD_LATCH_ADDR - tristate "Latch-assisted Flash Chip Support" - depends on MTD_COMPLEX_MAPPINGS - help - Map driver which allows flashes to be partially physically addressed - and have the upper address lines set by a board specific code. - - If compiled as a module, it will be called latch-addr-flash. - endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile @@ -17,12 +17,11 @@ obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o +physmap-objs-y += physmap-core.o +physmap-objs-$(CONFIG_MTD_PHYSMAP_VERSATILE) += physmap-versatile.o +physmap-objs-$(CONFIG_MTD_PHYSMAP_GEMINI) += physmap-gemini.o +physmap-objs := $(physmap-objs-y) obj-$(CONFIG_MTD_PHYSMAP) += physmap.o -physmap_of-objs-y += physmap_of_core.o -physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_VERSATILE) += physmap_of_versatile.o -physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_GEMINI) += physmap_of_gemini.o -physmap_of-objs := $(physmap_of-objs-y) -obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PISMO) += pismo.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o @@ -44,6 +43,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o -obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o -obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c @@ -1,281 +0,0 @@ -/* - * drivers/mtd/maps/gpio-addr-flash.c - * - * Handle the case where a flash device is mostly addressed using physical - * line and supplemented by GPIOs. This way you can hook up say a 8MiB flash - * to a 2MiB memory range and use the GPIOs to select a particular range. - * - * Copyright © 2000 Nicolas Pitre <nico@cam.org> - * Copyright © 2005-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#include <linux/gpio.h> -#include <linux/gpio/consumer.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> -#include <linux/mtd/physmap.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/types.h> - -#define win_mask(x) ((BIT(x)) - 1) - -#define DRIVER_NAME "gpio-addr-flash" - -/** - * struct async_state - keep GPIO flash state - * @mtd: MTD state for this mapping - * @map: MTD map state for this flash - * @gpios: Struct containing the array of GPIO descriptors - * @gpio_values: cached GPIO values - * @win_order: dedicated memory size (if no GPIOs) - */ -struct async_state { - struct mtd_info *mtd; - struct map_info map; - struct gpio_descs *gpios; - unsigned int gpio_values; - unsigned int win_order; -}; -#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1) - -/** - * gf_set_gpios() - set GPIO address lines to access specified flash offset - * @state: GPIO flash state - * @ofs: desired offset to access - * - * Rather than call the GPIO framework every time, cache the last-programmed - * value. This speeds up sequential accesses (which are by far the most common - * type). - */ -static void gf_set_gpios(struct async_state *state, unsigned long ofs) -{ - int i; - - ofs >>= state->win_order; - - if (ofs == state->gpio_values) - return; - - for (i = 0; i < state->gpios->ndescs; i++) { - if ((ofs & BIT(i)) == (state->gpio_values & BIT(i))) - continue; - - gpiod_set_value(state->gpios->desc[i], !!(ofs & BIT(i))); - } - - state->gpio_values = ofs; -} - -/** - * gf_read() - read a word at the specified offset - * @map: MTD map state - * @ofs: desired offset to read - */ -static map_word gf_read(struct map_info *map, unsigned long ofs) -{ - struct async_state *state = gf_map_info_to_state(map); - uint16_t word; - map_word test; - - gf_set_gpios(state, ofs); - - word = readw(map->virt + (ofs & win_mask(state->win_order))); - test.x[0] = word; - return test; -} - -/** - * gf_copy_from() - copy a chunk of data from the flash - * @map: MTD map state - * @to: memory to copy to - * @from: flash offset to copy from - * @len: how much to copy - * - * The "from" region may straddle more than one window, so toggle the GPIOs for - * each window region before reading its data. - */ -static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - struct async_state *state = gf_map_info_to_state(map); - - int this_len; - - while (len) { - this_len = from & win_mask(state->win_order); - this_len = BIT(state->win_order) - this_len; - this_len = min_t(int, len, this_len); - - gf_set_gpios(state, from); - memcpy_fromio(to, - map->virt + (from & win_mask(state->win_order)), - this_len); - len -= this_len; - from += this_len; - to += this_len; - } -} - -/** - * gf_write() - write a word at the specified offset - * @map: MTD map state - * @ofs: desired offset to write - */ -static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) -{ - struct async_state *state = gf_map_info_to_state(map); - uint16_t d; - - gf_set_gpios(state, ofs); - - d = d1.x[0]; - writew(d, map->virt + (ofs & win_mask(state->win_order))); -} - -/** - * gf_copy_to() - copy a chunk of data to the flash - * @map: MTD map state - * @to: flash offset to copy to - * @from: memory to copy from - * @len: how much to copy - * - * See gf_copy_from() caveat. - */ -static void gf_copy_to(struct map_info *map, unsigned long to, - const void *from, ssize_t len) -{ - struct async_state *state = gf_map_info_to_state(map); - - int this_len; - - while (len) { - this_len = to & win_mask(state->win_order); - this_len = BIT(state->win_order) - this_len; - this_len = min_t(int, len, this_len); - - gf_set_gpios(state, to); - memcpy_toio(map->virt + (to & win_mask(state->win_order)), - from, len); - - len -= this_len; - to += this_len; - from += this_len; - } -} - -static const char * const part_probe_types[] = { - "cmdlinepart", "RedBoot", NULL }; - -/** - * gpio_flash_probe() - setup a mapping for a GPIO assisted flash - * @pdev: platform device - * - * The platform resource layout expected looks something like: - * struct mtd_partition partitions[] = { ... }; - * struct physmap_flash_data flash_data = { ... }; - * static struct gpiod_lookup_table addr_flash_gpios = { - * .dev_id = "gpio-addr-flash.0", - * .table = { - * GPIO_LOOKUP_IDX("gpio.0", 15, "addr", 0, GPIO_ACTIVE_HIGH), - * GPIO_LOOKUP_IDX("gpio.0", 16, "addr", 1, GPIO_ACTIVE_HIGH), - * ); - * }; - * gpiod_add_lookup_table(&addr_flash_gpios); - * - * struct resource flash_resource[] = { - * { - * .name = "cfi_probe", - * .start = 0x20000000, - * .end = 0x201fffff, - * .flags = IORESOURCE_MEM, - * }, - * }; - * struct platform_device flash_device = { - * .name = "gpio-addr-flash", - * .dev = { .platform_data = &flash_data, }, - * .num_resources = ARRAY_SIZE(flash_resource), - * .resource = flash_resource, - * ... - * }; - */ -static int gpio_flash_probe(struct platform_device *pdev) -{ - struct physmap_flash_data *pdata; - struct resource *memory; - struct async_state *state; - - pdata = dev_get_platdata(&pdev->dev); - memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - if (!memory) - return -EINVAL; - - state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - state->gpios = devm_gpiod_get_array(&pdev->dev, "addr", GPIOD_OUT_LOW); - if (IS_ERR(state->gpios)) - return PTR_ERR(state->gpios); - - state->win_order = get_bitmask_order(resource_size(memory)) - 1; - - state->map.name = DRIVER_NAME; - state->map.read = gf_read; - state->map.copy_from = gf_copy_from; - state->map.write = gf_write; - state->map.copy_to = gf_copy_to; - state->map.bankwidth = pdata->width; - state->map.size = BIT(state->win_order + state->gpios->ndescs); - state->map.virt = devm_ioremap_resource(&pdev->dev, memory); - if (IS_ERR(state->map.virt)) - return PTR_ERR(state->map.virt); - - state->map.phys = NO_XIP; - state->map.map_priv_1 = (unsigned long)state; - - platform_set_drvdata(pdev, state); - - dev_notice(&pdev->dev, "probing %d-bit flash bus\n", - state->map.bankwidth * 8); - state->mtd = do_map_probe(memory->name, &state->map); - if (!state->mtd) - return -ENXIO; - state->mtd->dev.parent = &pdev->dev; - - mtd_device_parse_register(state->mtd, part_probe_types, NULL, - pdata->parts, pdata->nr_parts); - - return 0; -} - -static int gpio_flash_remove(struct platform_device *pdev) -{ - struct async_state *state = platform_get_drvdata(pdev); - - mtd_device_unregister(state->mtd); - map_destroy(state->mtd); - return 0; -} - -static struct platform_driver gpio_flash_driver = { - .probe = gpio_flash_probe, - .remove = gpio_flash_remove, - .driver = { - .name = DRIVER_NAME, - }, -}; - -module_platform_driver(gpio_flash_driver); - -MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); -MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c @@ -1,229 +0,0 @@ -/* - * Interface for NOR flash driver whose high address lines are latched - * - * Copyright © 2000 Nicolas Pitre <nico@cam.org> - * Copyright © 2005-2008 Analog Devices Inc. - * Copyright © 2008 MontaVista Software, Inc. <source@mvista.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> -#include <linux/platform_device.h> -#include <linux/mtd/latch-addr-flash.h> -#include <linux/slab.h> - -#define DRIVER_NAME "latch-addr-flash" - -struct latch_addr_flash_info { - struct mtd_info *mtd; - struct map_info map; - struct resource *res; - - void (*set_window)(unsigned long offset, void *data); - void *data; - - /* cache; could be found out of res */ - unsigned long win_mask; - - spinlock_t lock; -}; - -static map_word lf_read(struct map_info *map, unsigned long ofs) -{ - struct latch_addr_flash_info *info; - map_word datum; - - info = (struct latch_addr_flash_info *)map->map_priv_1; - - spin_lock(&info->lock); - - info->set_window(ofs, info->data); - datum = inline_map_read(map, info->win_mask & ofs); - - spin_unlock(&info->lock); - - return datum; -} - -static void lf_write(struct map_info *map, map_word datum, unsigned long ofs) -{ - struct latch_addr_flash_info *info; - - info = (struct latch_addr_flash_info *)map->map_priv_1; - - spin_lock(&info->lock); - - info->set_window(ofs, info->data); - inline_map_write(map, datum, info->win_mask & ofs); - - spin_unlock(&info->lock); -} - -static void lf_copy_from(struct map_info *map, void *to, - unsigned long from, ssize_t len) -{ - struct latch_addr_flash_info *info = - (struct latch_addr_flash_info *) map->map_priv_1; - unsigned n; - - while (len > 0) { - n = info->win_mask + 1 - (from & info->win_mask); - if (n > len) - n = len; - - spin_lock(&info->lock); - - info->set_window(from, info->data); - memcpy_fromio(to, map->virt + (from & info->win_mask), n); - - spin_unlock(&info->lock); - - to += n; - from += n; - len -= n; - } -} - -static char *rom_probe_types[] = { "cfi_probe", NULL }; - -static int latch_addr_flash_remove(struct platform_device *dev) -{ - struct latch_addr_flash_info *info; - struct latch_addr_flash_data *latch_addr_data; - - info = platform_get_drvdata(dev); - if (info == NULL) - return 0; - - latch_addr_data = dev_get_platdata(&dev->dev); - - if (info->mtd != NULL) { - mtd_device_unregister(info->mtd); - map_destroy(info->mtd); - } - - if (info->map.virt != NULL) - iounmap(info->map.virt); - - if (info->res != NULL) - release_mem_region(info->res->start, resource_size(info->res)); - - kfree(info); - - if (latch_addr_data->done) - latch_addr_data->done(latch_addr_data->data); - - return 0; -} - -static int latch_addr_flash_probe(struct platform_device *dev) -{ - struct latch_addr_flash_data *latch_addr_data; - struct latch_addr_flash_info *info; - resource_size_t win_base = dev->resource->start; - resource_size_t win_size = resource_size(dev->resource); - char **probe_type; - int chipsel; - int err; - - latch_addr_data = dev_get_platdata(&dev->dev); - if (latch_addr_data == NULL) - return -ENODEV; - - pr_notice("latch-addr platform flash device: %#llx byte " - "window at %#.8llx\n", - (unsigned long long)win_size, (unsigned long long)win_base); - - chipsel = dev->id; - - if (latch_addr_data->init) { - err = latch_addr_data->init(latch_addr_data->data, chipsel); - if (err != 0) - return err; - } - - info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL); - if (info == NULL) { - err = -ENOMEM; - goto done; - } - - platform_set_drvdata(dev, info); - - info->res = request_mem_region(win_base, win_size, DRIVER_NAME); - if (info->res == NULL) { - dev_err(&dev->dev, "Could not reserve memory region\n"); - err = -EBUSY; - goto free_info; - } - - info->map.name = DRIVER_NAME; - info->map.size = latch_addr_data->size; - info->map.bankwidth = latch_addr_data->width; - - info->map.phys = NO_XIP; - info->map.virt = ioremap(win_base, win_size); - if (!info->map.virt) { - err = -ENOMEM; - goto free_res; - } - - info->map.map_priv_1 = (unsigned long)info; - - info->map.read = lf_read; - info->map.copy_from = lf_copy_from; - info->map.write = lf_write; - info->set_window = latch_addr_data->set_window; - info->data = latch_addr_data->data; - info->win_mask = win_size - 1; - - spin_lock_init(&info->lock); - - for (probe_type = rom_probe_types; !info->mtd && *probe_type; - probe_type++) - info->mtd = do_map_probe(*probe_type, &info->map); - - if (info->mtd == NULL) { - dev_err(&dev->dev, "map_probe failed\n"); - err = -ENODEV; - goto iounmap; - } - info->mtd->dev.parent = &dev->dev; - - mtd_device_register(info->mtd, latch_addr_data->parts, - latch_addr_data->nr_parts); - return 0; - -iounmap: - iounmap(info->map.virt); -free_res: - release_mem_region(info->res->start, resource_size(info->res)); -free_info: - kfree(info); -done: - if (latch_addr_data->done) - latch_addr_data->done(latch_addr_data->data); - return err; -} - -static struct platform_driver latch_addr_flash_driver = { - .probe = latch_addr_flash_probe, - .remove = latch_addr_flash_remove, - .driver = { - .name = DRIVER_NAME, - }, -}; - -module_platform_driver(latch_addr_flash_driver); - -MODULE_AUTHOR("David Griego <dgriego@mvista.com>"); -MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper " - "address lines being set board specifically"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Normal mappings of chips in physical memory + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 031022 - [jsun] add run-time configure and partition setup + * + * Device tree support: + * Copyright (C) 2006 MontaVista Software Inc. + * Author: Vitaly Wool <vwool@ru.mvista.com> + * + * Revised to handle newer style flash binding by: + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * GPIO address extension: + * Handle the case where a flash device is mostly addressed using physical + * line and supplemented by GPIOs. This way you can hook up say a 8MiB flash + * to a 2MiB memory range and use the GPIOs to select a particular range. + * + * Copyright © 2000 Nicolas Pitre <nico@cam.org> + * Copyright © 2005-2009 Analog Devices Inc. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/mtd/concat.h> +#include <linux/mtd/cfi_endian.h> +#include <linux/io.h> +#include <linux/of_device.h> +#include <linux/gpio/consumer.h> + +#include "physmap-gemini.h" +#include "physmap-versatile.h" + +struct physmap_flash_info { + unsigned int nmaps; + struct mtd_info **mtds; + struct mtd_info *cmtd; + struct map_info *maps; + spinlock_t vpp_lock; + int vpp_refcnt; + const char *probe_type; + const char * const *part_types; + unsigned int nparts; + const struct mtd_partition *parts; + struct gpio_descs *gpios; + unsigned int gpio_values; + unsigned int win_order; +}; + +static int physmap_flash_remove(struct platform_device *dev) +{ + struct physmap_flash_info *info; + struct physmap_flash_data *physmap_data; + int i, err; + + info = platform_get_drvdata(dev); + if (!info) + return 0; + + if (info->cmtd) { + err = mtd_device_unregister(info->cmtd); + if (err) + return err; + + if (info->cmtd != info->mtds[0]) + mtd_concat_destroy(info->cmtd); + } + + for (i = 0; i < info->nmaps; i++) { + if (info->mtds[i]) + map_destroy(info->mtds[i]); + } + + physmap_data = dev_get_platdata(&dev->dev); + if (physmap_data && physmap_data->exit) + physmap_data->exit(dev); + + return 0; +} + +static void physmap_set_vpp(struct map_info *map, int state) +{ + struct platform_device *pdev; + struct physmap_flash_data *physmap_data; + struct physmap_flash_info *info; + unsigned long flags; + + pdev = (struct platform_device *)map->map_priv_1; + physmap_data = dev_get_platdata(&pdev->dev); + + if (!physmap_data->set_vpp) + return; + + info = platform_get_drvdata(pdev); + + spin_lock_irqsave(&info->vpp_lock, flags); + if (state) { + if (++info->vpp_refcnt == 1) /* first nested 'on' */ + physmap_data->set_vpp(pdev, 1); + } else { + if (--info->vpp_refcnt == 0) /* last nested 'off' */ + physmap_data->set_vpp(pdev, 0); + } + spin_unlock_irqrestore(&info->vpp_lock, flags); +} + +#if IS_ENABLED(CONFIG_MTD_PHYSMAP_GPIO_ADDR) +static void physmap_set_addr_gpios(struct physmap_flash_info *info, + unsigned long ofs) +{ + unsigned int i; + + ofs >>= info->win_order; + if (info->gpio_values == ofs) + return; + + for (i = 0; i < info->gpios->ndescs; i++) { + if ((BIT(i) & ofs) == (BIT(i) & info->gpio_values)) + continue; + + gpiod_set_value(info->gpios->desc[i], !!(BIT(i) & ofs)); + } +} + +#define win_mask(order) (BIT(order) - 1) + +static map_word physmap_addr_gpios_read(struct map_info *map, + unsigned long ofs) +{ + struct platform_device *pdev; + struct physmap_flash_info *info; + map_word mw; + u16 word; + + pdev = (struct platform_device *)map->map_priv_1; + info = platform_get_drvdata(pdev); + physmap_set_addr_gpios(info, ofs); + + word = readw(map->virt + (ofs & win_mask(info->win_order))); + mw.x[0] = word; + return mw; +} + +static void physmap_addr_gpios_copy_from(struct map_info *map, void *buf, + unsigned long ofs, ssize_t len) +{ + struct platform_device *pdev; + struct physmap_flash_info *info; + + pdev = (struct platform_device *)map->map_priv_1; + info = platform_get_drvdata(pdev); + + while (len) { + unsigned int winofs = ofs & win_mask(info->win_order); + unsigned int chunklen = min_t(unsigned int, len, + BIT(info->win_order) - winofs); + + physmap_set_addr_gpios(info, ofs); + memcpy_fromio(buf, map->virt + winofs, chunklen); + len -= chunklen; + buf += chunklen; + ofs += chunklen; + } +} + +static void physmap_addr_gpios_write(struct map_info *map, map_word mw, + unsigned long ofs) +{ + struct platform_device *pdev; + struct physmap_flash_info *info; + u16 word; + + pdev = (struct platform_device *)map->map_priv_1; + info = platform_get_drvdata(pdev); + physmap_set_addr_gpios(info, ofs); + + word = mw.x[0]; + writew(word, map->virt + (ofs & win_mask(info->win_order))); +} + +static void physmap_addr_gpios_copy_to(struct map_info *map, unsigned long ofs, + const void *buf, ssize_t len) +{ + struct platform_device *pdev; + struct physmap_flash_info *info; + + pdev = (struct platform_device *)map->map_priv_1; + info = platform_get_drvdata(pdev); + + while (len) { + unsigned int winofs = ofs & win_mask(info->win_order); + unsigned int chunklen = min_t(unsigned int, len, + BIT(info->win_order) - winofs); + + physmap_set_addr_gpios(info, ofs); + memcpy_toio(map->virt + winofs, buf, chunklen); + len -= chunklen; + buf += chunklen; + ofs += chunklen; + } +} + +static int physmap_addr_gpios_map_init(struct map_info *map) +{ + map->phys = NO_XIP; + map->read = physmap_addr_gpios_read; + map->copy_from = physmap_addr_gpios_copy_from; + map->write = physmap_addr_gpios_write; + map->copy_to = physmap_addr_gpios_copy_to; + + return 0; +} +#else +static int physmap_addr_gpios_map_init(struct map_info *map) +{ + return -ENOTSUPP; +} +#endif + +#if IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) +static const struct of_device_id of_flash_match[] = { + { + .compatible = "cfi-flash", + .data = "cfi_probe", + }, + { + /* + * FIXME: JEDEC chips can't be safely and reliably + * probed, although the mtd code gets it right in + * practice most of the time. We should use the + * vendor and device ids specified by the binding to + * bypass the heuristic probe code, but the mtd layer + * provides, at present, no interface for doing so + * :(. + */ + .compatible = "jedec-flash", + .data = "jedec_probe", + }, + { + .compatible = "mtd-ram", + .data = "map_ram", + }, + { + .compatible = "mtd-rom", + .data = "map_rom", + }, + { + .type = "rom", + .compatible = "direct-mapped" + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, of_flash_match); + +static const char * const of_default_part_probes[] = { + "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL +}; + +static const char * const *of_get_part_probes(struct platform_device *dev) +{ + struct device_node *dp = dev->dev.of_node; + const char **res; + int count; + + count = of_property_count_strings(dp, "linux,part-probe"); + if (count < 0) + return of_default_part_probes; + + res = devm_kcalloc(&dev->dev, count + 1, sizeof(*res), GFP_KERNEL); + if (!res) + return NULL; + + count = of_property_read_string_array(dp, "linux,part-probe", res, + count); + if (count < 0) + return NULL; + + return res; +} + +static const char *of_select_probe_type(struct platform_device *dev) +{ + struct device_node *dp = dev->dev.of_node; + const struct of_device_id *match; + const char *probe_type; + + match = of_match_device(of_flash_match, &dev->dev); + probe_type = match->data; + if (probe_type) + return probe_type; + + dev_warn(&dev->dev, + "Device tree uses obsolete \"direct-mapped\" flash binding\n"); + + of_property_read_string(dp, "probe-type", &probe_type); + if (!probe_type) + return NULL; + + if (!strcmp(probe_type, "CFI")) { + probe_type = "cfi_probe"; + } else if (!strcmp(probe_type, "JEDEC")) { + probe_type = "jedec_probe"; + } else if (!strcmp(probe_type, "ROM")) { + probe_type = "map_rom"; + } else { + dev_warn(&dev->dev, + "obsolete_probe: don't know probe type '%s', mapping as rom\n", + probe_type); + probe_type = "map_rom"; + } + + return probe_type; +} + +static int physmap_flash_of_init(struct platform_device *dev) +{ + struct physmap_flash_info *info = platform_get_drvdata(dev); + struct device_node *dp = dev->dev.of_node; + const char *mtd_name = NULL; + int err, swap = 0; + bool map_indirect; + unsigned int i; + u32 bankwidth; + + if (!dp) + return -EINVAL; + + info->probe_type = of_select_probe_type(dev); + + info->part_types = of_get_part_probes(dev); + if (!info->part_types) + return -ENOMEM; + + of_property_read_string(dp, "linux,mtd-name", &mtd_name); + + map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access"); + + err = of_property_read_u32(dp, "bank-width", &bankwidth); + if (err) { + dev_err(&dev->dev, "Can't get bank width from device tree\n"); + return err; + } + + if (of_property_read_bool(dp, "big-endian")) + swap = CFI_BIG_ENDIAN; + else if (of_property_read_bool(dp, "little-endian")) + swap = CFI_LITTLE_ENDIAN; + + for (i = 0; i < info->nmaps; i++) { + info->maps[i].name = mtd_name; + info->maps[i].swap = swap; + info->maps[i].bankwidth = bankwidth; + info->maps[i].device_node = dp; + + err = of_flash_probe_gemini(dev, dp, &info->maps[i]); + if (err) + return err; + + err = of_flash_probe_versatile(dev, dp, &info->maps[i]); + if (err) + return err; + + /* + * On some platforms (e.g. MPC5200) a direct 1:1 mapping + * may cause problems with JFFS2 usage, as the local bus (LPB) + * doesn't support unaligned accesses as implemented in the + * JFFS2 code via memcpy(). By setting NO_XIP, the + * flash will not be exposed directly to the MTD users + * (e.g. JFFS2) any more. + */ + if (map_indirect) + info->maps[i].phys = NO_XIP; + } + + return 0; +} +#else /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */ +#define of_flash_match NULL + +static int physmap_flash_of_init(struct platform_device *dev) +{ + return -ENOTSUPP; +} +#endif /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */ + +static const char * const rom_probe_types[] = { + "cfi_probe", "jedec_probe", "qinfo_probe", "map_rom", +}; + +static const char * const part_probe_types[] = { + "cmdlinepart", "RedBoot", "afs", NULL +}; + +static int physmap_flash_pdata_init(struct platform_device *dev) +{ + struct physmap_flash_info *info = platform_get_drvdata(dev); + struct physmap_flash_data *physmap_data; + unsigned int i; + int err; + + physmap_data = dev_get_platdata(&dev->dev); + if (!physmap_data) + return -EINVAL; + + info->probe_type = physmap_data->probe_type; + info->part_types = physmap_data->part_probe_types ? : part_probe_types; + info->parts = physmap_data->parts; + info->nparts = physmap_data->nr_parts; + + if (physmap_data->init) { + err = physmap_data->init(dev); + if (err) + return err; + } + + for (i = 0; i < info->nmaps; i++) { + info->maps[i].bankwidth = physmap_data->width; + info->maps[i].pfow_base = physmap_data->pfow_base; + info->maps[i].set_vpp = physmap_set_vpp; + } + + return 0; +} + +static int physmap_flash_probe(struct platform_device *dev) +{ + struct physmap_flash_info *info; + int err = 0; + int i; + + if (!dev->dev.of_node && !dev_get_platdata(&dev->dev)) + return -EINVAL; + + info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + while (platform_get_resource(dev, IORESOURCE_MEM, info->nmaps)) + info->nmaps++; + + if (!info->nmaps) + return -ENODEV; + + info->maps = devm_kzalloc(&dev->dev, + sizeof(*info->maps) * info->nmaps, + GFP_KERNEL); + if (!info->maps) + return -ENOMEM; + + info->mtds = devm_kzalloc(&dev->dev, + sizeof(*info->mtds) * info->nmaps, + GFP_KERNEL); + if (!info->mtds) + return -ENOMEM; + + platform_set_drvdata(dev, info); + + info->gpios = devm_gpiod_get_array_optional(&dev->dev, "addr", + GPIOD_OUT_LOW); + if (IS_ERR(info->gpios)) + return PTR_ERR(info->gpios); + + if (info->gpios && info->nmaps > 1) { + dev_err(&dev->dev, "addr-gpios only supported for nmaps == 1\n"); + return -EINVAL; + } + + if (dev->dev.of_node) + err = physmap_flash_of_init(dev); + else + err = physmap_flash_pdata_init(dev); + + if (err) + return err; + + for (i = 0; i < info->nmaps; i++) { + struct resource *res; + + res = platform_get_resource(dev, IORESOURCE_MEM, i); + info->maps[i].virt = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(info->maps[i].virt)) { + err = PTR_ERR(info->maps[i].virt); + goto err_out; + } + + dev_notice(&dev->dev, "physmap platform flash device: %pR\n", + res); + + info->maps[i].name = dev_name(&dev->dev); + + if (!info->maps[i].phys) + info->maps[i].phys = res->start; + + info->win_order = get_bitmask_order(resource_size(res)) - 1; + info->maps[i].size = BIT(info->win_order + + (info->gpios ? + info->gpios->ndescs : 0)); + + info->maps[i].map_priv_1 = (unsigned long)dev; + + if (info->gpios) { + err = physmap_addr_gpios_map_init(&info->maps[i]); + if (err) + goto err_out; + } + +#ifdef CONFIG_MTD_COMPLEX_MAPPINGS + /* + * Only use the simple_map implementation if map hooks are not + * implemented. Since map->read() is mandatory checking for its + * presence is enough. + */ + if (!info->maps[i].read) + simple_map_init(&info->maps[i]); +#else + simple_map_init(&info->maps[i]); +#endif + + if (info->probe_type) { + info->mtds[i] = do_map_probe(info->probe_type, + &info->maps[i]); + } else { + int j; + + for (j = 0; j < ARRAY_SIZE(rom_probe_types); j++) { + info->mtds[i] = do_map_probe(rom_probe_types[j], + &info->maps[i]); + if (info->mtds[i]) + break; + } + } + + if (!info->mtds[i]) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENXIO; + goto err_out; + } + info->mtds[i]->dev.parent = &dev->dev; + } + + if (info->nmaps == 1) { + info->cmtd = info->mtds[0]; + } else { + /* + * We detected multiple devices. Concatenate them together. + */ + info->cmtd = mtd_concat_create(info->mtds, info->nmaps, + dev_name(&dev->dev)); + if (!info->cmtd) + err = -ENXIO; + } + if (err) + goto err_out; + + spin_lock_init(&info->vpp_lock); + + mtd_set_of_node(info->cmtd, dev->dev.of_node); + err = mtd_device_parse_register(info->cmtd, info->part_types, NULL, + info->parts, info->nparts); + if (err) + goto err_out; + + return 0; + +err_out: + physmap_flash_remove(dev); + return err; +} + +#ifdef CONFIG_PM +static void physmap_flash_shutdown(struct platform_device *dev) +{ + struct physmap_flash_info *info = platform_get_drvdata(dev); + int i; + + for (i = 0; i < info->nmaps && info->mtds[i]; i++) + if (mtd_suspend(info->mtds[i]) == 0) + mtd_resume(info->mtds[i]); +} +#else +#define physmap_flash_shutdown NULL +#endif + +static struct platform_driver physmap_flash_driver = { + .probe = physmap_flash_probe, + .remove = physmap_flash_remove, + .shutdown = physmap_flash_shutdown, + .driver = { + .name = "physmap-flash", + .of_match_table = of_flash_match, + }, +}; + +#ifdef CONFIG_MTD_PHYSMAP_COMPAT +static struct physmap_flash_data physmap_flash_data = { + .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, +}; + +static struct resource physmap_flash_resource = { + .start = CONFIG_MTD_PHYSMAP_START, + .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device physmap_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &physmap_flash_data, + }, + .num_resources = 1, + .resource = &physmap_flash_resource, +}; +#endif + +static int __init physmap_init(void) +{ + int err; + + err = platform_driver_register(&physmap_flash_driver); +#ifdef CONFIG_MTD_PHYSMAP_COMPAT + if (err == 0) { + err = platform_device_register(&physmap_flash); + if (err) + platform_driver_unregister(&physmap_flash_driver); + } +#endif + + return err; +} + +static void __exit physmap_exit(void) +{ +#ifdef CONFIG_MTD_PHYSMAP_COMPAT + platform_device_unregister(&physmap_flash); +#endif + platform_driver_unregister(&physmap_flash_driver); +} + +module_init(physmap_init); +module_exit(physmap_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>"); +MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); +MODULE_DESCRIPTION("Generic configurable MTD map driver"); + +/* legacy platform drivers can't hotplug or coldplg */ +#ifndef CONFIG_MTD_PHYSMAP_COMPAT +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:physmap-flash"); +#endif diff --git a/drivers/mtd/maps/physmap-gemini.c b/drivers/mtd/maps/physmap-gemini.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cortina Systems Gemini OF physmap add-on + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * + * This SoC has an elaborate flash control register, so we need to + * detect and set it up when booting on this platform. + */ +#include <linux/export.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/mtd/map.h> +#include <linux/mtd/xip.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/pinctrl/consumer.h> +#include "physmap-gemini.h" + +/* + * The Flash-relevant parts of the global status register + * These would also be relevant for a NAND driver. + */ +#define GLOBAL_STATUS 0x04 +#define FLASH_TYPE_MASK (0x3 << 24) +#define FLASH_TYPE_NAND_2K (0x3 << 24) +#define FLASH_TYPE_NAND_512 (0x2 << 24) +#define FLASH_TYPE_PARALLEL (0x1 << 24) +#define FLASH_TYPE_SERIAL (0x0 << 24) +/* if parallel */ +#define FLASH_WIDTH_16BIT (1 << 23) /* else 8 bit */ +/* if serial */ +#define FLASH_ATMEL (1 << 23) /* else STM */ + +#define FLASH_SIZE_MASK (0x3 << 21) +#define NAND_256M (0x3 << 21) /* and more */ +#define NAND_128M (0x2 << 21) +#define NAND_64M (0x1 << 21) +#define NAND_32M (0x0 << 21) +#define ATMEL_16M (0x3 << 21) /* and more */ +#define ATMEL_8M (0x2 << 21) +#define ATMEL_4M_2M (0x1 << 21) +#define ATMEL_1M (0x0 << 21) /* and less */ +#define STM_32M (1 << 22) /* and more */ +#define STM_16M (0 << 22) /* and less */ + +#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */ + +static const struct of_device_id syscon_match[] = { + { .compatible = "cortina,gemini-syscon" }, + { }, +}; + +struct gemini_flash { + struct device *dev; + struct pinctrl *p; + struct pinctrl_state *enabled_state; + struct pinctrl_state *disabled_state; +}; + +/* Static local state */ +static struct gemini_flash *gf; + +static void gemini_flash_enable_pins(void) +{ + int ret; + + if (IS_ERR(gf->enabled_state)) + return; + ret = pinctrl_select_state(gf->p, gf->enabled_state); + if (ret) + dev_err(gf->dev, "failed to enable pins\n"); +} + +static void gemini_flash_disable_pins(void) +{ + int ret; + + if (IS_ERR(gf->disabled_state)) + return; + ret = pinctrl_select_state(gf->p, gf->disabled_state); + if (ret) + dev_err(gf->dev, "failed to disable pins\n"); +} + +static map_word __xipram gemini_flash_map_read(struct map_info *map, + unsigned long ofs) +{ + map_word __xipram ret; + + gemini_flash_enable_pins(); + ret = inline_map_read(map, ofs); + gemini_flash_disable_pins(); + + return ret; +} + +static void __xipram gemini_flash_map_write(struct map_info *map, + const map_word datum, + unsigned long ofs) +{ + gemini_flash_enable_pins(); + inline_map_write(map, datum, ofs); + gemini_flash_disable_pins(); +} + +static void __xipram gemini_flash_map_copy_from(struct map_info *map, + void *to, unsigned long from, + ssize_t len) +{ + gemini_flash_enable_pins(); + inline_map_copy_from(map, to, from, len); + gemini_flash_disable_pins(); +} + +static void __xipram gemini_flash_map_copy_to(struct map_info *map, + unsigned long to, + const void *from, ssize_t len) +{ + gemini_flash_enable_pins(); + inline_map_copy_to(map, to, from, len); + gemini_flash_disable_pins(); +} + +int of_flash_probe_gemini(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + struct regmap *rmap; + struct device *dev = &pdev->dev; + u32 val; + int ret; + + /* Multiplatform guard */ + if (!of_device_is_compatible(np, "cortina,gemini-flash")) + return 0; + + gf = devm_kzalloc(dev, sizeof(*gf), GFP_KERNEL); + if (!gf) + return -ENOMEM; + gf->dev = dev; + + rmap = syscon_regmap_lookup_by_phandle(np, "syscon"); + if (IS_ERR(rmap)) { + dev_err(dev, "no syscon\n"); + return PTR_ERR(rmap); + } + + ret = regmap_read(rmap, GLOBAL_STATUS, &val); + if (ret) { + dev_err(dev, "failed to read global status register\n"); + return -ENODEV; + } + dev_dbg(dev, "global status reg: %08x\n", val); + + /* + * It would be contradictory if a physmap flash was NOT parallel. + */ + if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) { + dev_err(dev, "flash is not parallel\n"); + return -ENODEV; + } + + /* + * Complain if DT data and hardware definition is different. + */ + if (val & FLASH_WIDTH_16BIT) { + if (map->bankwidth != 2) + dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n", + map->bankwidth * 8); + } else { + if (map->bankwidth != 1) + dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n", + map->bankwidth * 8); + } + + gf->p = devm_pinctrl_get(dev); + if (IS_ERR(gf->p)) { + dev_err(dev, "no pinctrl handle\n"); + ret = PTR_ERR(gf->p); + return ret; + } + + gf->enabled_state = pinctrl_lookup_state(gf->p, "enabled"); + if (IS_ERR(gf->enabled_state)) + dev_err(dev, "no enabled pin control state\n"); + + gf->disabled_state = pinctrl_lookup_state(gf->p, "disabled"); + if (IS_ERR(gf->enabled_state)) { + dev_err(dev, "no disabled pin control state\n"); + } else { + ret = pinctrl_select_state(gf->p, gf->disabled_state); + if (ret) + dev_err(gf->dev, "failed to disable pins\n"); + } + + map->read = gemini_flash_map_read; + map->write = gemini_flash_map_write; + map->copy_from = gemini_flash_map_copy_from; + map->copy_to = gemini_flash_map_copy_to; + + dev_info(dev, "initialized Gemini-specific physmap control\n"); + + return 0; +} diff --git a/drivers/mtd/maps/physmap-gemini.h b/drivers/mtd/maps/physmap-gemini.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/of.h> +#include <linux/mtd/map.h> + +#ifdef CONFIG_MTD_PHYSMAP_GEMINI +int of_flash_probe_gemini(struct platform_device *pdev, + struct device_node *np, + struct map_info *map); +#else +static inline +int of_flash_probe_gemini(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + return 0; +} +#endif diff --git a/drivers/mtd/maps/physmap-versatile.c b/drivers/mtd/maps/physmap-versatile.c @@ -0,0 +1,254 @@ +/* + * Versatile OF physmap driver add-on + * + * Copyright (c) 2016, Linaro Limited + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <linux/export.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/mtd/map.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/bitops.h> +#include "physmap-versatile.h" + +static struct regmap *syscon_regmap; + +enum versatile_flashprot { + INTEGRATOR_AP_FLASHPROT, + INTEGRATOR_CP_FLASHPROT, + VERSATILE_FLASHPROT, + REALVIEW_FLASHPROT, +}; + +static const struct of_device_id syscon_match[] = { + { + .compatible = "arm,integrator-ap-syscon", + .data = (void *)INTEGRATOR_AP_FLASHPROT, + }, + { + .compatible = "arm,integrator-cp-syscon", + .data = (void *)INTEGRATOR_CP_FLASHPROT, + }, + { + .compatible = "arm,core-module-versatile", + .data = (void *)VERSATILE_FLASHPROT, + }, + { + .compatible = "arm,realview-eb-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pb1176-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pb11mp-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pba8-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + { + .compatible = "arm,realview-pbx-syscon", + .data = (void *)REALVIEW_FLASHPROT, + }, + {}, +}; + +/* + * Flash protection handling for the Integrator/AP + */ +#define INTEGRATOR_SC_CTRLS_OFFSET 0x08 +#define INTEGRATOR_SC_CTRLC_OFFSET 0x0C +#define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1) +#define INTEGRATOR_SC_CTRL_FLWP BIT(2) + +#define INTEGRATOR_EBI_CSR1_OFFSET 0x04 +/* The manual says bit 2, the code says bit 3, trust the code */ +#define INTEGRATOR_EBI_WRITE_ENABLE BIT(3) +#define INTEGRATOR_EBI_LOCK_OFFSET 0x20 +#define INTEGRATOR_EBI_LOCK_VAL 0xA05F + +static const struct of_device_id ebi_match[] = { + { .compatible = "arm,external-bus-interface"}, + { }, +}; + +static int ap_flash_init(struct platform_device *pdev) +{ + struct device_node *ebi; + void __iomem *ebi_base; + u32 val; + int ret; + + /* Look up the EBI */ + ebi = of_find_matching_node(NULL, ebi_match); + if (!ebi) { + return -ENODEV; + } + ebi_base = of_iomap(ebi, 0); + if (!ebi_base) + return -ENODEV; + + /* Clear VPP and write protection bits */ + ret = regmap_write(syscon_regmap, + INTEGRATOR_SC_CTRLC_OFFSET, + INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); + if (ret) + dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n"); + + /* Unlock the EBI */ + writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); + + /* Enable write cycles on the EBI, CSR1 (flash) */ + val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); + val |= INTEGRATOR_EBI_WRITE_ENABLE; + writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); + + /* Lock the EBI again */ + writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); + iounmap(ebi_base); + + return 0; +} + +static void ap_flash_set_vpp(struct map_info *map, int on) +{ + int ret; + + if (on) { + ret = regmap_write(syscon_regmap, + INTEGRATOR_SC_CTRLS_OFFSET, + INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); + if (ret) + pr_err("error enabling AP VPP\n"); + } else { + ret = regmap_write(syscon_regmap, + INTEGRATOR_SC_CTRLC_OFFSET, + INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); + if (ret) + pr_err("error disabling AP VPP\n"); + } +} + +/* + * Flash protection handling for the Integrator/CP + */ + +#define INTCP_FLASHPROG_OFFSET 0x04 +#define CINTEGRATOR_FLVPPEN BIT(0) +#define CINTEGRATOR_FLWREN BIT(1) +#define CINTEGRATOR_FLMASK BIT(0)|BIT(1) + +static void cp_flash_set_vpp(struct map_info *map, int on) +{ + int ret; + + if (on) { + ret = regmap_update_bits(syscon_regmap, + INTCP_FLASHPROG_OFFSET, + CINTEGRATOR_FLMASK, + CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN); + if (ret) + pr_err("error setting CP VPP\n"); + } else { + ret = regmap_update_bits(syscon_regmap, + INTCP_FLASHPROG_OFFSET, + CINTEGRATOR_FLMASK, + 0); + if (ret) + pr_err("error setting CP VPP\n"); + } +} + +/* + * Flash protection handling for the Versatiles and RealViews + */ + +#define VERSATILE_SYS_FLASH_OFFSET 0x4C + +static void versatile_flash_set_vpp(struct map_info *map, int on) +{ + int ret; + + ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET, + 0x01, !!on); + if (ret) + pr_err("error setting Versatile VPP\n"); +} + +int of_flash_probe_versatile(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + struct device_node *sysnp; + const struct of_device_id *devid; + struct regmap *rmap; + static enum versatile_flashprot versatile_flashprot; + int ret; + + /* Not all flash chips use this protection line */ + if (!of_device_is_compatible(np, "arm,versatile-flash")) + return 0; + + /* For first chip probed, look up the syscon regmap */ + if (!syscon_regmap) { + sysnp = of_find_matching_node_and_match(NULL, + syscon_match, + &devid); + if (!sysnp) + return -ENODEV; + + versatile_flashprot = (enum versatile_flashprot)devid->data; + rmap = syscon_node_to_regmap(sysnp); + if (IS_ERR(rmap)) + return PTR_ERR(rmap); + + syscon_regmap = rmap; + } + + switch (versatile_flashprot) { + case INTEGRATOR_AP_FLASHPROT: + ret = ap_flash_init(pdev); + if (ret) + return ret; + map->set_vpp = ap_flash_set_vpp; + dev_info(&pdev->dev, "Integrator/AP flash protection\n"); + break; + case INTEGRATOR_CP_FLASHPROT: + map->set_vpp = cp_flash_set_vpp; + dev_info(&pdev->dev, "Integrator/CP flash protection\n"); + break; + case VERSATILE_FLASHPROT: + case REALVIEW_FLASHPROT: + map->set_vpp = versatile_flash_set_vpp; + dev_info(&pdev->dev, "versatile/realview flash protection\n"); + break; + default: + dev_info(&pdev->dev, "device marked as Versatile flash " + "but no system controller was found\n"); + break; + } + + return 0; +} diff --git a/drivers/mtd/maps/physmap-versatile.h b/drivers/mtd/maps/physmap-versatile.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/of.h> +#include <linux/mtd/map.h> + +#ifdef CONFIG_MTD_PHYSMAP_VERSATILE +int of_flash_probe_versatile(struct platform_device *pdev, + struct device_node *np, + struct map_info *map); +#else +static inline +int of_flash_probe_versatile(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + return 0; +} +#endif diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c @@ -1,280 +0,0 @@ -/* - * Normal mappings of chips in physical memory - * - * Copyright (C) 2003 MontaVista Software Inc. - * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net - * - * 031022 - [jsun] add run-time configure and partition setup - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> -#include <linux/mtd/physmap.h> -#include <linux/mtd/concat.h> -#include <linux/io.h> - -#define MAX_RESOURCES 4 - -struct physmap_flash_info { - struct mtd_info *mtd[MAX_RESOURCES]; - struct mtd_info *cmtd; - struct map_info map[MAX_RESOURCES]; - spinlock_t vpp_lock; - int vpp_refcnt; -}; - -static int physmap_flash_remove(struct platform_device *dev) -{ - struct physmap_flash_info *info; - struct physmap_flash_data *physmap_data; - int i; - - info = platform_get_drvdata(dev); - if (info == NULL) - return 0; - - physmap_data = dev_get_platdata(&dev->dev); - - if (info->cmtd) { - mtd_device_unregister(info->cmtd); - if (info->cmtd != info->mtd[0]) - mtd_concat_destroy(info->cmtd); - } - - for (i = 0; i < MAX_RESOURCES; i++) { - if (info->mtd[i] != NULL) - map_destroy(info->mtd[i]); - } - - if (physmap_data->exit) - physmap_data->exit(dev); - - return 0; -} - -static void physmap_set_vpp(struct map_info *map, int state) -{ - struct platform_device *pdev; - struct physmap_flash_data *physmap_data; - struct physmap_flash_info *info; - unsigned long flags; - - pdev = (struct platform_device *)map->map_priv_1; - physmap_data = dev_get_platdata(&pdev->dev); - - if (!physmap_data->set_vpp) - return; - - info = platform_get_drvdata(pdev); - - spin_lock_irqsave(&info->vpp_lock, flags); - if (state) { - if (++info->vpp_refcnt == 1) /* first nested 'on' */ - physmap_data->set_vpp(pdev, 1); - } else { - if (--info->vpp_refcnt == 0) /* last nested 'off' */ - physmap_data->set_vpp(pdev, 0); - } - spin_unlock_irqrestore(&info->vpp_lock, flags); -} - -static const char * const rom_probe_types[] = { - "cfi_probe", "jedec_probe", "qinfo_probe", "map_rom", NULL }; - -static const char * const part_probe_types[] = { - "cmdlinepart", "RedBoot", "afs", NULL }; - -static int physmap_flash_probe(struct platform_device *dev) -{ - struct physmap_flash_data *physmap_data; - struct physmap_flash_info *info; - const char * const *probe_type; - const char * const *part_types; - int err = 0; - int i; - int devices_found = 0; - - physmap_data = dev_get_platdata(&dev->dev); - if (physmap_data == NULL) - return -ENODEV; - - info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info), - GFP_KERNEL); - if (info == NULL) { - err = -ENOMEM; - goto err_out; - } - - if (physmap_data->init) { - err = physmap_data->init(dev); - if (err) - goto err_out; - } - - platform_set_drvdata(dev, info); - - for (i = 0; i < dev->num_resources; i++) { - printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n", - (unsigned long long)resource_size(&dev->resource[i]), - (unsigned long long)dev->resource[i].start); - - if (!devm_request_mem_region(&dev->dev, - dev->resource[i].start, - resource_size(&dev->resource[i]), - dev_name(&dev->dev))) { - dev_err(&dev->dev, "Could not reserve memory region\n"); - err = -ENOMEM; - goto err_out; - } - - info->map[i].name = dev_name(&dev->dev); - info->map[i].phys = dev->resource[i].start; - info->map[i].size = resource_size(&dev->resource[i]); - info->map[i].bankwidth = physmap_data->width; - info->map[i].set_vpp = physmap_set_vpp; - info->map[i].pfow_base = physmap_data->pfow_base; - info->map[i].map_priv_1 = (unsigned long)dev; - - info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys, - info->map[i].size); - if (info->map[i].virt == NULL) { - dev_err(&dev->dev, "Failed to ioremap flash region\n"); - err = -EIO; - goto err_out; - } - - simple_map_init(&info->map[i]); - - probe_type = rom_probe_types; - if (physmap_data->probe_type == NULL) { - for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++) - info->mtd[i] = do_map_probe(*probe_type, &info->map[i]); - } else - info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]); - - if (info->mtd[i] == NULL) { - dev_err(&dev->dev, "map_probe failed\n"); - err = -ENXIO; - goto err_out; - } else { - devices_found++; - } - info->mtd[i]->dev.parent = &dev->dev; - } - - if (devices_found == 1) { - info->cmtd = info->mtd[0]; - } else if (devices_found > 1) { - /* - * We detected multiple devices. Concatenate them together. - */ - info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev)); - if (info->cmtd == NULL) - err = -ENXIO; - } - if (err) - goto err_out; - - spin_lock_init(&info->vpp_lock); - - part_types = physmap_data->part_probe_types ? : part_probe_types; - - mtd_device_parse_register(info->cmtd, part_types, NULL, - physmap_data->parts, physmap_data->nr_parts); - return 0; - -err_out: - physmap_flash_remove(dev); - return err; -} - -#ifdef CONFIG_PM -static void physmap_flash_shutdown(struct platform_device *dev) -{ - struct physmap_flash_info *info = platform_get_drvdata(dev); - int i; - - for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - if (mtd_suspend(info->mtd[i]) == 0) - mtd_resume(info->mtd[i]); -} -#else -#define physmap_flash_shutdown NULL -#endif - -static struct platform_driver physmap_flash_driver = { - .probe = physmap_flash_probe, - .remove = physmap_flash_remove, - .shutdown = physmap_flash_shutdown, - .driver = { - .name = "physmap-flash", - }, -}; - - -#ifdef CONFIG_MTD_PHYSMAP_COMPAT -static struct physmap_flash_data physmap_flash_data = { - .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, -}; - -static struct resource physmap_flash_resource = { - .start = CONFIG_MTD_PHYSMAP_START, - .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device physmap_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &physmap_flash_data, - }, - .num_resources = 1, - .resource = &physmap_flash_resource, -}; -#endif - -static int __init physmap_init(void) -{ - int err; - - err = platform_driver_register(&physmap_flash_driver); -#ifdef CONFIG_MTD_PHYSMAP_COMPAT - if (err == 0) { - err = platform_device_register(&physmap_flash); - if (err) - platform_driver_unregister(&physmap_flash_driver); - } -#endif - - return err; -} - -static void __exit physmap_exit(void) -{ -#ifdef CONFIG_MTD_PHYSMAP_COMPAT - platform_device_unregister(&physmap_flash); -#endif - platform_driver_unregister(&physmap_flash_driver); -} - -module_init(physmap_init); -module_exit(physmap_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("Generic configurable MTD map driver"); - -/* legacy platform drivers can't hotplug or coldplg */ -#ifndef CONFIG_MTD_PHYSMAP_COMPAT -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:physmap-flash"); -#endif diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c @@ -1,368 +0,0 @@ -/* - * Flash mappings described by the OF (or flattened) device tree - * - * Copyright (C) 2006 MontaVista Software Inc. - * Author: Vitaly Wool <vwool@ru.mvista.com> - * - * Revised to handle newer style flash binding by: - * Copyright (C) 2007 David Gibson, IBM Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/device.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> -#include <linux/mtd/concat.h> -#include <linux/mtd/cfi_endian.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> -#include <linux/slab.h> -#include "physmap_of_gemini.h" -#include "physmap_of_versatile.h" - -struct of_flash_list { - struct mtd_info *mtd; - struct map_info map; -}; - -struct of_flash { - struct mtd_info *cmtd; - int list_size; /* number of elements in of_flash_list */ - struct of_flash_list list[0]; -}; - -static int of_flash_remove(struct platform_device *dev) -{ - struct of_flash *info; - int i; - - info = dev_get_drvdata(&dev->dev); - if (!info) - return 0; - dev_set_drvdata(&dev->dev, NULL); - - if (info->cmtd) { - mtd_device_unregister(info->cmtd); - if (info->cmtd != info->list[0].mtd) - mtd_concat_destroy(info->cmtd); - } - - for (i = 0; i < info->list_size; i++) - if (info->list[i].mtd) - map_destroy(info->list[i].mtd); - - return 0; -} - -static const char * const rom_probe_types[] = { - "cfi_probe", "jedec_probe", "map_rom" }; - -/* Helper function to handle probing of the obsolete "direct-mapped" - * compatible binding, which has an extra "probe-type" property - * describing the type of flash probe necessary. */ -static struct mtd_info *obsolete_probe(struct platform_device *dev, - struct map_info *map) -{ - struct device_node *dp = dev->dev.of_node; - const char *of_probe; - struct mtd_info *mtd; - int i; - - dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" " - "flash binding\n"); - - of_probe = of_get_property(dp, "probe-type", NULL); - if (!of_probe) { - for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) { - mtd = do_map_probe(rom_probe_types[i], map); - if (mtd) - return mtd; - } - return NULL; - } else if (strcmp(of_probe, "CFI") == 0) { - return do_map_probe("cfi_probe", map); - } else if (strcmp(of_probe, "JEDEC") == 0) { - return do_map_probe("jedec_probe", map); - } else { - if (strcmp(of_probe, "ROM") != 0) - dev_warn(&dev->dev, "obsolete_probe: don't know probe " - "type '%s', mapping as rom\n", of_probe); - return do_map_probe("map_rom", map); - } -} - -/* When partitions are set we look for a linux,part-probe property which - specifies the list of partition probers to use. If none is given then the - default is use. These take precedence over other device tree - information. */ -static const char * const part_probe_types_def[] = { - "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL }; - -static const char * const *of_get_probes(struct device_node *dp) -{ - const char **res; - int count; - - count = of_property_count_strings(dp, "linux,part-probe"); - if (count < 0) - return part_probe_types_def; - - res = kcalloc(count + 1, sizeof(*res), GFP_KERNEL); - if (!res) - return NULL; - - count = of_property_read_string_array(dp, "linux,part-probe", res, - count); - if (count < 0) - return NULL; - - return res; -} - -static void of_free_probes(const char * const *probes) -{ - if (probes != part_probe_types_def) - kfree(probes); -} - -static const struct of_device_id of_flash_match[]; -static int of_flash_probe(struct platform_device *dev) -{ - const char * const *part_probe_types; - const struct of_device_id *match; - struct device_node *dp = dev->dev.of_node; - struct resource res; - struct of_flash *info; - const char *probe_type; - const __be32 *width; - int err; - int i; - int count; - const __be32 *p; - int reg_tuple_size; - struct mtd_info **mtd_list = NULL; - resource_size_t res_size; - bool map_indirect; - const char *mtd_name = NULL; - - match = of_match_device(of_flash_match, &dev->dev); - if (!match) - return -EINVAL; - probe_type = match->data; - - reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); - - of_property_read_string(dp, "linux,mtd-name", &mtd_name); - - /* - * Get number of "reg" tuples. Scan for MTD devices on area's - * described by each "reg" region. This makes it possible (including - * the concat support) to support the Intel P30 48F4400 chips which - * consists internally of 2 non-identical NOR chips on one die. - */ - p = of_get_property(dp, "reg", &count); - if (!p || count % reg_tuple_size != 0) { - dev_err(&dev->dev, "Malformed reg property on %pOF\n", - dev->dev.of_node); - err = -EINVAL; - goto err_flash_remove; - } - count /= reg_tuple_size; - - map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access"); - - err = -ENOMEM; - info = devm_kzalloc(&dev->dev, - sizeof(struct of_flash) + - sizeof(struct of_flash_list) * count, GFP_KERNEL); - if (!info) - goto err_flash_remove; - - dev_set_drvdata(&dev->dev, info); - - mtd_list = kcalloc(count, sizeof(*mtd_list), GFP_KERNEL); - if (!mtd_list) - goto err_flash_remove; - - for (i = 0; i < count; i++) { - err = -ENXIO; - if (of_address_to_resource(dp, i, &res)) { - /* - * Continue with next register tuple if this - * one is not mappable - */ - continue; - } - - dev_dbg(&dev->dev, "of_flash device: %pR\n", &res); - - err = -EBUSY; - res_size = resource_size(&res); - info->list[i].map.virt = devm_ioremap_resource(&dev->dev, &res); - if (IS_ERR(info->list[i].map.virt)) { - err = PTR_ERR(info->list[i].map.virt); - goto err_out; - } - - err = -ENXIO; - width = of_get_property(dp, "bank-width", NULL); - if (!width) { - dev_err(&dev->dev, "Can't get bank width from device" - " tree\n"); - goto err_out; - } - - info->list[i].map.name = mtd_name ?: dev_name(&dev->dev); - info->list[i].map.phys = res.start; - info->list[i].map.size = res_size; - info->list[i].map.bankwidth = be32_to_cpup(width); - info->list[i].map.device_node = dp; - - if (of_property_read_bool(dp, "big-endian")) - info->list[i].map.swap = CFI_BIG_ENDIAN; - else if (of_property_read_bool(dp, "little-endian")) - info->list[i].map.swap = CFI_LITTLE_ENDIAN; - - err = of_flash_probe_gemini(dev, dp, &info->list[i].map); - if (err) - goto err_out; - err = of_flash_probe_versatile(dev, dp, &info->list[i].map); - if (err) - goto err_out; - - simple_map_init(&info->list[i].map); - - /* - * On some platforms (e.g. MPC5200) a direct 1:1 mapping - * may cause problems with JFFS2 usage, as the local bus (LPB) - * doesn't support unaligned accesses as implemented in the - * JFFS2 code via memcpy(). By setting NO_XIP, the - * flash will not be exposed directly to the MTD users - * (e.g. JFFS2) any more. - */ - if (map_indirect) - info->list[i].map.phys = NO_XIP; - - if (probe_type) { - info->list[i].mtd = do_map_probe(probe_type, - &info->list[i].map); - } else { - info->list[i].mtd = obsolete_probe(dev, - &info->list[i].map); - } - - /* Fall back to mapping region as ROM */ - if (!info->list[i].mtd) { - dev_warn(&dev->dev, - "do_map_probe() failed for type %s\n", - probe_type); - - info->list[i].mtd = do_map_probe("map_rom", - &info->list[i].map); - } - mtd_list[i] = info->list[i].mtd; - - err = -ENXIO; - if (!info->list[i].mtd) { - dev_err(&dev->dev, "do_map_probe() failed\n"); - goto err_out; - } else { - info->list_size++; - } - info->list[i].mtd->dev.parent = &dev->dev; - } - - err = 0; - info->cmtd = NULL; - if (info->list_size == 1) { - info->cmtd = info->list[0].mtd; - } else if (info->list_size > 1) { - /* - * We detected multiple devices. Concatenate them together. - */ - info->cmtd = mtd_concat_create(mtd_list, info->list_size, - dev_name(&dev->dev)); - } - if (info->cmtd == NULL) - err = -ENXIO; - - if (err) - goto err_out; - - info->cmtd->dev.parent = &dev->dev; - mtd_set_of_node(info->cmtd, dp); - part_probe_types = of_get_probes(dp); - if (!part_probe_types) { - err = -ENOMEM; - goto err_out; - } - mtd_device_parse_register(info->cmtd, part_probe_types, NULL, - NULL, 0); - of_free_probes(part_probe_types); - - kfree(mtd_list); - - return 0; - -err_out: - kfree(mtd_list); -err_flash_remove: - of_flash_remove(dev); - - return err; -} - -static const struct of_device_id of_flash_match[] = { - { - .compatible = "cfi-flash", - .data = (void *)"cfi_probe", - }, - { - /* FIXME: JEDEC chips can't be safely and reliably - * probed, although the mtd code gets it right in - * practice most of the time. We should use the - * vendor and device ids specified by the binding to - * bypass the heuristic probe code, but the mtd layer - * provides, at present, no interface for doing so - * :(. */ - .compatible = "jedec-flash", - .data = (void *)"jedec_probe", - }, - { - .compatible = "mtd-ram", - .data = (void *)"map_ram", - }, - { - .compatible = "mtd-rom", - .data = (void *)"map_rom", - }, - { - .type = "rom", - .compatible = "direct-mapped" - }, - { }, -}; -MODULE_DEVICE_TABLE(of, of_flash_match); - -static struct platform_driver of_flash_driver = { - .driver = { - .name = "of-flash", - .of_match_table = of_flash_match, - }, - .probe = of_flash_probe, - .remove = of_flash_remove, -}; - -module_platform_driver(of_flash_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>"); -MODULE_DESCRIPTION("Device tree based MTD map driver"); diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Cortina Systems Gemini OF physmap add-on - * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> - * - * This SoC has an elaborate flash control register, so we need to - * detect and set it up when booting on this platform. - */ -#include <linux/export.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/mtd/map.h> -#include <linux/mfd/syscon.h> -#include <linux/regmap.h> -#include <linux/bitops.h> -#include "physmap_of_gemini.h" - -/* - * The Flash-relevant parts of the global status register - * These would also be relevant for a NAND driver. - */ -#define GLOBAL_STATUS 0x04 -#define FLASH_TYPE_MASK (0x3 << 24) -#define FLASH_TYPE_NAND_2K (0x3 << 24) -#define FLASH_TYPE_NAND_512 (0x2 << 24) -#define FLASH_TYPE_PARALLEL (0x1 << 24) -#define FLASH_TYPE_SERIAL (0x0 << 24) -/* if parallel */ -#define FLASH_WIDTH_16BIT (1 << 23) /* else 8 bit */ -/* if serial */ -#define FLASH_ATMEL (1 << 23) /* else STM */ - -#define FLASH_SIZE_MASK (0x3 << 21) -#define NAND_256M (0x3 << 21) /* and more */ -#define NAND_128M (0x2 << 21) -#define NAND_64M (0x1 << 21) -#define NAND_32M (0x0 << 21) -#define ATMEL_16M (0x3 << 21) /* and more */ -#define ATMEL_8M (0x2 << 21) -#define ATMEL_4M_2M (0x1 << 21) -#define ATMEL_1M (0x0 << 21) /* and less */ -#define STM_32M (1 << 22) /* and more */ -#define STM_16M (0 << 22) /* and less */ - -#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */ - -int of_flash_probe_gemini(struct platform_device *pdev, - struct device_node *np, - struct map_info *map) -{ - struct regmap *rmap; - struct device *dev = &pdev->dev; - u32 val; - int ret; - - /* Multiplatform guard */ - if (!of_device_is_compatible(np, "cortina,gemini-flash")) - return 0; - - rmap = syscon_regmap_lookup_by_phandle(np, "syscon"); - if (IS_ERR(rmap)) { - dev_err(dev, "no syscon\n"); - return PTR_ERR(rmap); - } - - ret = regmap_read(rmap, GLOBAL_STATUS, &val); - if (ret) { - dev_err(dev, "failed to read global status register\n"); - return -ENODEV; - } - dev_dbg(dev, "global status reg: %08x\n", val); - - /* - * It would be contradictory if a physmap flash was NOT parallel. - */ - if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) { - dev_err(dev, "flash is not parallel\n"); - return -ENODEV; - } - - /* - * Complain if DT data and hardware definition is different. - */ - if (val & FLASH_WIDTH_16BIT) { - if (map->bankwidth != 2) - dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n", - map->bankwidth * 8); - } else { - if (map->bankwidth != 1) - dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n", - map->bankwidth * 8); - } - - dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n"); - - return 0; -} diff --git a/drivers/mtd/maps/physmap_of_gemini.h b/drivers/mtd/maps/physmap_of_gemini.h @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include <linux/of.h> -#include <linux/mtd/map.h> - -#ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI -int of_flash_probe_gemini(struct platform_device *pdev, - struct device_node *np, - struct map_info *map); -#else -static inline -int of_flash_probe_gemini(struct platform_device *pdev, - struct device_node *np, - struct map_info *map) -{ - return 0; -} -#endif diff --git a/drivers/mtd/maps/physmap_of_versatile.c b/drivers/mtd/maps/physmap_of_versatile.c @@ -1,254 +0,0 @@ -/* - * Versatile OF physmap driver add-on - * - * Copyright (c) 2016, Linaro Limited - * Author: Linus Walleij <linus.walleij@linaro.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ -#include <linux/export.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/mtd/map.h> -#include <linux/mfd/syscon.h> -#include <linux/regmap.h> -#include <linux/bitops.h> -#include "physmap_of_versatile.h" - -static struct regmap *syscon_regmap; - -enum versatile_flashprot { - INTEGRATOR_AP_FLASHPROT, - INTEGRATOR_CP_FLASHPROT, - VERSATILE_FLASHPROT, - REALVIEW_FLASHPROT, -}; - -static const struct of_device_id syscon_match[] = { - { - .compatible = "arm,integrator-ap-syscon", - .data = (void *)INTEGRATOR_AP_FLASHPROT, - }, - { - .compatible = "arm,integrator-cp-syscon", - .data = (void *)INTEGRATOR_CP_FLASHPROT, - }, - { - .compatible = "arm,core-module-versatile", - .data = (void *)VERSATILE_FLASHPROT, - }, - { - .compatible = "arm,realview-eb-syscon", - .data = (void *)REALVIEW_FLASHPROT, - }, - { - .compatible = "arm,realview-pb1176-syscon", - .data = (void *)REALVIEW_FLASHPROT, - }, - { - .compatible = "arm,realview-pb11mp-syscon", - .data = (void *)REALVIEW_FLASHPROT, - }, - { - .compatible = "arm,realview-pba8-syscon", - .data = (void *)REALVIEW_FLASHPROT, - }, - { - .compatible = "arm,realview-pbx-syscon", - .data = (void *)REALVIEW_FLASHPROT, - }, - {}, -}; - -/* - * Flash protection handling for the Integrator/AP - */ -#define INTEGRATOR_SC_CTRLS_OFFSET 0x08 -#define INTEGRATOR_SC_CTRLC_OFFSET 0x0C -#define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1) -#define INTEGRATOR_SC_CTRL_FLWP BIT(2) - -#define INTEGRATOR_EBI_CSR1_OFFSET 0x04 -/* The manual says bit 2, the code says bit 3, trust the code */ -#define INTEGRATOR_EBI_WRITE_ENABLE BIT(3) -#define INTEGRATOR_EBI_LOCK_OFFSET 0x20 -#define INTEGRATOR_EBI_LOCK_VAL 0xA05F - -static const struct of_device_id ebi_match[] = { - { .compatible = "arm,external-bus-interface"}, - { }, -}; - -static int ap_flash_init(struct platform_device *pdev) -{ - struct device_node *ebi; - void __iomem *ebi_base; - u32 val; - int ret; - - /* Look up the EBI */ - ebi = of_find_matching_node(NULL, ebi_match); - if (!ebi) { - return -ENODEV; - } - ebi_base = of_iomap(ebi, 0); - if (!ebi_base) - return -ENODEV; - - /* Clear VPP and write protection bits */ - ret = regmap_write(syscon_regmap, - INTEGRATOR_SC_CTRLC_OFFSET, - INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); - if (ret) - dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n"); - - /* Unlock the EBI */ - writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); - - /* Enable write cycles on the EBI, CSR1 (flash) */ - val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); - val |= INTEGRATOR_EBI_WRITE_ENABLE; - writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); - - /* Lock the EBI again */ - writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); - iounmap(ebi_base); - - return 0; -} - -static void ap_flash_set_vpp(struct map_info *map, int on) -{ - int ret; - - if (on) { - ret = regmap_write(syscon_regmap, - INTEGRATOR_SC_CTRLS_OFFSET, - INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); - if (ret) - pr_err("error enabling AP VPP\n"); - } else { - ret = regmap_write(syscon_regmap, - INTEGRATOR_SC_CTRLC_OFFSET, - INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); - if (ret) - pr_err("error disabling AP VPP\n"); - } -} - -/* - * Flash protection handling for the Integrator/CP - */ - -#define INTCP_FLASHPROG_OFFSET 0x04 -#define CINTEGRATOR_FLVPPEN BIT(0) -#define CINTEGRATOR_FLWREN BIT(1) -#define CINTEGRATOR_FLMASK BIT(0)|BIT(1) - -static void cp_flash_set_vpp(struct map_info *map, int on) -{ - int ret; - - if (on) { - ret = regmap_update_bits(syscon_regmap, - INTCP_FLASHPROG_OFFSET, - CINTEGRATOR_FLMASK, - CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN); - if (ret) - pr_err("error setting CP VPP\n"); - } else { - ret = regmap_update_bits(syscon_regmap, - INTCP_FLASHPROG_OFFSET, - CINTEGRATOR_FLMASK, - 0); - if (ret) - pr_err("error setting CP VPP\n"); - } -} - -/* - * Flash protection handling for the Versatiles and RealViews - */ - -#define VERSATILE_SYS_FLASH_OFFSET 0x4C - -static void versatile_flash_set_vpp(struct map_info *map, int on) -{ - int ret; - - ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET, - 0x01, !!on); - if (ret) - pr_err("error setting Versatile VPP\n"); -} - -int of_flash_probe_versatile(struct platform_device *pdev, - struct device_node *np, - struct map_info *map) -{ - struct device_node *sysnp; - const struct of_device_id *devid; - struct regmap *rmap; - static enum versatile_flashprot versatile_flashprot; - int ret; - - /* Not all flash chips use this protection line */ - if (!of_device_is_compatible(np, "arm,versatile-flash")) - return 0; - - /* For first chip probed, look up the syscon regmap */ - if (!syscon_regmap) { - sysnp = of_find_matching_node_and_match(NULL, - syscon_match, - &devid); - if (!sysnp) - return -ENODEV; - - versatile_flashprot = (enum versatile_flashprot)devid->data; - rmap = syscon_node_to_regmap(sysnp); - if (IS_ERR(rmap)) - return PTR_ERR(rmap); - - syscon_regmap = rmap; - } - - switch (versatile_flashprot) { - case INTEGRATOR_AP_FLASHPROT: - ret = ap_flash_init(pdev); - if (ret) - return ret; - map->set_vpp = ap_flash_set_vpp; - dev_info(&pdev->dev, "Integrator/AP flash protection\n"); - break; - case INTEGRATOR_CP_FLASHPROT: - map->set_vpp = cp_flash_set_vpp; - dev_info(&pdev->dev, "Integrator/CP flash protection\n"); - break; - case VERSATILE_FLASHPROT: - case REALVIEW_FLASHPROT: - map->set_vpp = versatile_flash_set_vpp; - dev_info(&pdev->dev, "versatile/realview flash protection\n"); - break; - default: - dev_info(&pdev->dev, "device marked as Versatile flash " - "but no system controller was found\n"); - break; - } - - return 0; -} diff --git a/drivers/mtd/maps/physmap_of_versatile.h b/drivers/mtd/maps/physmap_of_versatile.h @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include <linux/of.h> -#include <linux/mtd/map.h> - -#ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE -int of_flash_probe_versatile(struct platform_device *pdev, - struct device_node *np, - struct map_info *map); -#else -static inline -int of_flash_probe_versatile(struct platform_device *pdev, - struct device_node *np, - struct map_info *map) -{ - return 0; -} -#endif diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c @@ -56,7 +56,7 @@ struct mtdblk_dev { */ static int erase_write (struct mtd_info *mtd, unsigned long pos, - int len, const char *buf) + unsigned int len, const char *buf) { struct erase_info erase; size_t retlen; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c @@ -665,6 +665,8 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd) } else { pr_debug("mtd device won't show a device symlink in sysfs\n"); } + + mtd->orig_flags = mtd->flags; } /** @@ -1136,13 +1138,13 @@ static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs, return -EINVAL; if (ops->ooblen) { - u64 maxooblen; + size_t maxooblen; if (ops->ooboffs >= mtd_oobavail(mtd, ops)) return -EINVAL; - maxooblen = ((mtd_div_by_ws(mtd->size, mtd) - - mtd_div_by_ws(offs, mtd)) * + maxooblen = ((size_t)(mtd_div_by_ws(mtd->size, mtd) - + mtd_div_by_ws(offs, mtd)) * mtd_oobavail(mtd, ops)) - ops->ooboffs; if (ops->ooblen > maxooblen) return -EINVAL; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c @@ -61,6 +61,15 @@ static inline struct mtd_part *mtd_to_part(const struct mtd_info *mtd) return container_of(mtd, struct mtd_part, mtd); } +static u64 part_absolute_offset(struct mtd_info *mtd) +{ + struct mtd_part *part = mtd_to_part(mtd); + + if (!mtd_is_partition(mtd)) + return 0; + + return part_absolute_offset(part->parent) + part->offset; +} /* * MTD methods which simply translate the effective address and pass through @@ -346,7 +355,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, /* set up the MTD object for this partition */ slave->mtd.type = parent->type; - slave->mtd.flags = parent->flags & ~part->mask_flags; + slave->mtd.flags = parent->orig_flags & ~part->mask_flags; + slave->mtd.orig_flags = slave->mtd.flags; slave->mtd.size = part->size; slave->mtd.writesize = parent->writesize; slave->mtd.writebufsize = parent->writebufsize; @@ -513,7 +523,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, if (!(slave->mtd.flags & MTD_NO_ERASE)) wr_alignment = slave->mtd.erasesize; - tmp = slave->offset; + tmp = part_absolute_offset(parent) + slave->offset; remainder = do_div(tmp, wr_alignment); if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) { /* Doesn't start on a boundary of major erase size */ @@ -524,7 +534,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, part->name); } - tmp = slave->mtd.size; + tmp = part_absolute_offset(parent) + slave->mtd.size; remainder = do_div(tmp, wr_alignment); if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) { slave->mtd.flags &= ~MTD_WRITEABLE; diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c @@ -1265,18 +1265,7 @@ static int mtdswap_show(struct seq_file *s, void *data) return 0; } - -static int mtdswap_open(struct inode *inode, struct file *file) -{ - return single_open(file, mtdswap_show, inode->i_private); -} - -static const struct file_operations mtdswap_fops = { - .open = mtdswap_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(mtdswap); static int mtdswap_add_debugfs(struct mtdswap_dev *d) { diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig @@ -70,7 +70,7 @@ config MTD_NAND_GPIO config MTD_NAND_AMS_DELTA tristate "NAND Flash device on Amstrad E3" - depends on MACH_AMS_DELTA + depends on MACH_AMS_DELTA || COMPILE_TEST default y help Support for NAND flash on Amstrad E3 (Delta). diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> * @@ -8,10 +9,6 @@ * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> * Partially stolen from plat_nand.c * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Overview: * This is a device driver for the NAND flash device found on the * Amstrad E3 (Delta). @@ -24,18 +21,14 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> -#include <linux/platform_data/gpio-omap.h> - -#include <asm/io.h> -#include <asm/sizes.h> - -#include <mach/hardware.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> /* * MTD structure for E3 (Delta) */ - struct ams_delta_nand { + struct nand_controller base; struct nand_chip nand_chip; struct gpio_desc *gpiod_rdy; struct gpio_desc *gpiod_nce; @@ -44,7 +37,7 @@ struct ams_delta_nand { struct gpio_desc *gpiod_nwe; struct gpio_desc *gpiod_ale; struct gpio_desc *gpiod_cle; - void __iomem *io_base; + struct gpio_descs *data_gpiods; bool data_in; }; @@ -73,99 +66,154 @@ static const struct mtd_partition partition_info[] = { .size = 3 * SZ_256K }, }; -static void ams_delta_io_write(struct ams_delta_nand *priv, u_char byte) +static void ams_delta_write_commit(struct ams_delta_nand *priv) { - writew(byte, priv->nand_chip.legacy.IO_ADDR_W); gpiod_set_value(priv->gpiod_nwe, 0); ndelay(40); gpiod_set_value(priv->gpiod_nwe, 1); } -static u_char ams_delta_io_read(struct ams_delta_nand *priv) +static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) +{ + struct gpio_descs *data_gpiods = priv->data_gpiods; + DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; + + gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, + data_gpiods->info, values); + + ams_delta_write_commit(priv); +} + +static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte) +{ + struct gpio_descs *data_gpiods = priv->data_gpiods; + DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; + int i; + + for (i = 0; i < data_gpiods->ndescs; i++) + gpiod_direction_output_raw(data_gpiods->desc[i], + test_bit(i, values)); + + ams_delta_write_commit(priv); + + priv->data_in = false; +} + +static u8 ams_delta_io_read(struct ams_delta_nand *priv) { - u_char res; + u8 res; + struct gpio_descs *data_gpiods = priv->data_gpiods; + DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, }; gpiod_set_value(priv->gpiod_nre, 0); ndelay(40); - res = readw(priv->nand_chip.legacy.IO_ADDR_R); + + gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, + data_gpiods->info, values); + gpiod_set_value(priv->gpiod_nre, 1); + res = values[0]; return res; } -static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in) +static void ams_delta_dir_input(struct ams_delta_nand *priv) { - writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL); - priv->data_in = in; + struct gpio_descs *data_gpiods = priv->data_gpiods; + int i; + + for (i = 0; i < data_gpiods->ndescs; i++) + gpiod_direction_input(data_gpiods->desc[i]); + + priv->data_in = true; } -static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf, +static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf, int len) { - struct ams_delta_nand *priv = nand_get_controller_data(this); - int i; + int i = 0; - if (priv->data_in) - ams_delta_dir_input(priv, false); + if (len > 0 && priv->data_in) + ams_delta_dir_output(priv, buf[i++]); - for (i = 0; i < len; i++) - ams_delta_io_write(priv, buf[i]); + while (i < len) + ams_delta_io_write(priv, buf[i++]); } -static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len) +static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) { - struct ams_delta_nand *priv = nand_get_controller_data(this); int i; if (!priv->data_in) - ams_delta_dir_input(priv, true); + ams_delta_dir_input(priv); for (i = 0; i < len; i++) buf[i] = ams_delta_io_read(priv); } -static u_char ams_delta_read_byte(struct nand_chip *this) +static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) { - u_char res; - - ams_delta_read_buf(this, &res, 1); - - return res; + gpiod_set_value(priv->gpiod_nce, assert ? 0 : 1); } -/* - * Command control function - * - * ctrl: - * NAND_NCE: bit 0 -> bit 2 - * NAND_CLE: bit 1 -> bit 7 - * NAND_ALE: bit 2 -> bit 6 - */ -static void ams_delta_hwcontrol(struct nand_chip *this, int cmd, - unsigned int ctrl) +static int ams_delta_exec_op(struct nand_chip *this, + const struct nand_operation *op, bool check_only) { struct ams_delta_nand *priv = nand_get_controller_data(this); - - if (ctrl & NAND_CTRL_CHANGE) { - gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE)); - gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE)); - gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE)); + const struct nand_op_instr *instr; + int ret = 0; + + if (check_only) + return 0; + + ams_delta_ctrl_cs(priv, 1); + + for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) { + switch (instr->type) { + case NAND_OP_CMD_INSTR: + gpiod_set_value(priv->gpiod_cle, 1); + ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1); + gpiod_set_value(priv->gpiod_cle, 0); + break; + + case NAND_OP_ADDR_INSTR: + gpiod_set_value(priv->gpiod_ale, 1); + ams_delta_write_buf(priv, instr->ctx.addr.addrs, + instr->ctx.addr.naddrs); + gpiod_set_value(priv->gpiod_ale, 0); + break; + + case NAND_OP_DATA_IN_INSTR: + ams_delta_read_buf(priv, instr->ctx.data.buf.in, + instr->ctx.data.len); + break; + + case NAND_OP_DATA_OUT_INSTR: + ams_delta_write_buf(priv, instr->ctx.data.buf.out, + instr->ctx.data.len); + break; + + case NAND_OP_WAITRDY_INSTR: + ret = priv->gpiod_rdy ? + nand_gpio_waitrdy(this, priv->gpiod_rdy, + instr->ctx.waitrdy.timeout_ms) : + nand_soft_waitrdy(this, + instr->ctx.waitrdy.timeout_ms); + break; + } + + if (ret) + break; } - if (cmd != NAND_CMD_NONE) { - u_char byte = cmd; + ams_delta_ctrl_cs(priv, 0); - ams_delta_write_buf(this, &byte, 1); - } -} - -static int ams_delta_nand_ready(struct nand_chip *this) -{ - struct ams_delta_nand *priv = nand_get_controller_data(this); - - return gpiod_get_value(priv->gpiod_rdy); + return ret; } +static const struct nand_controller_ops ams_delta_ops = { + .exec_op = ams_delta_exec_op, +}; /* * Main initialization routine @@ -175,61 +223,29 @@ static int ams_delta_init(struct platform_device *pdev) struct ams_delta_nand *priv; struct nand_chip *this; struct mtd_info *mtd; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - void __iomem *io_base; + struct gpio_descs *data_gpiods; int err = 0; - if (!res) - return -ENXIO; - /* Allocate memory for MTD device structure and private data */ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand), GFP_KERNEL); - if (!priv) { - pr_warn("Unable to allocate E3 NAND MTD device structure.\n"); + if (!priv) return -ENOMEM; - } + this = &priv->nand_chip; mtd = nand_to_mtd(this); mtd->dev.parent = &pdev->dev; - /* - * Don't try to request the memory region from here, - * it should have been already requested from the - * gpio-omap driver and requesting it again would fail. - */ - - io_base = ioremap(res->start, resource_size(res)); - if (io_base == NULL) { - dev_err(&pdev->dev, "ioremap failed\n"); - err = -EIO; - goto out_free; - } - - priv->io_base = io_base; nand_set_controller_data(this, priv); - /* Set address of NAND IO lines */ - this->legacy.IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH; - this->legacy.IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT; - this->legacy.read_byte = ams_delta_read_byte; - this->legacy.write_buf = ams_delta_write_buf; - this->legacy.read_buf = ams_delta_read_buf; - this->legacy.cmd_ctrl = ams_delta_hwcontrol; - priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN); if (IS_ERR(priv->gpiod_rdy)) { err = PTR_ERR(priv->gpiod_rdy); dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err); - goto out_mtd; + return err; } - if (priv->gpiod_rdy) - this->legacy.dev_ready = ams_delta_nand_ready; - - /* 25 us command delay time */ - this->legacy.chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; this->ecc.algo = NAND_ECC_HAMMING; @@ -240,61 +256,75 @@ static int ams_delta_init(struct platform_device *pdev) if (IS_ERR(priv->gpiod_nwp)) { err = PTR_ERR(priv->gpiod_nwp); dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); - goto out_mtd; + return err; } priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpiod_nce)) { err = PTR_ERR(priv->gpiod_nce); dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err); - goto out_mtd; + return err; } priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpiod_nre)) { err = PTR_ERR(priv->gpiod_nre); dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err); - goto out_mtd; + return err; } priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpiod_nwe)) { err = PTR_ERR(priv->gpiod_nwe); dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err); - goto out_mtd; + return err; } priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_ale)) { err = PTR_ERR(priv->gpiod_ale); dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err); - goto out_mtd; + return err; } priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_cle)) { err = PTR_ERR(priv->gpiod_cle); dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err); - goto out_mtd; + return err; } - /* Initialize data port direction to a known state */ - ams_delta_dir_input(priv, true); + /* Request array of data pins, initialize them as input */ + data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); + if (IS_ERR(data_gpiods)) { + err = PTR_ERR(data_gpiods); + dev_err(&pdev->dev, "data GPIO request failed: %d\n", err); + return err; + } + priv->data_gpiods = data_gpiods; + priv->data_in = true; + + /* Initialize the NAND controller object embedded in ams_delta_nand. */ + priv->base.ops = &ams_delta_ops; + nand_controller_init(&priv->base); + this->controller = &priv->base; /* Scan to find existence of the device */ err = nand_scan(this, 1); if (err) - goto out_mtd; + return err; /* Register the partitions */ - mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info)); + err = mtd_device_register(mtd, partition_info, + ARRAY_SIZE(partition_info)); + if (err) + goto err_nand_cleanup; - goto out; + return 0; + +err_nand_cleanup: + nand_cleanup(this); - out_mtd: - iounmap(io_base); -out_free: - out: return err; } @@ -305,13 +335,10 @@ static int ams_delta_cleanup(struct platform_device *pdev) { struct ams_delta_nand *priv = platform_get_drvdata(pdev); struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); - void __iomem *io_base = priv->io_base; - /* Release resources, unregister device */ + /* Unregister device */ nand_release(mtd_to_nand(mtd)); - iounmap(io_base); - return 0; } @@ -325,6 +352,6 @@ static struct platform_driver ams_delta_nand_driver = { module_platform_driver(ams_delta_nand_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>"); MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)"); diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1477,10 +1477,10 @@ static void atmel_nand_init(struct atmel_nand_controller *nc, chip->legacy.write_byte = atmel_nand_write_byte; chip->legacy.read_buf = atmel_nand_read_buf; chip->legacy.write_buf = atmel_nand_write_buf; - chip->select_chip = atmel_nand_select_chip; + chip->legacy.select_chip = atmel_nand_select_chip; - if (nc->mck && nc->caps->ops->setup_data_interface) - chip->setup_data_interface = atmel_nand_setup_data_interface; + if (!nc->mck || !nc->caps->ops->setup_data_interface) + chip->options |= NAND_KEEP_TIMINGS; /* Some NANDs require a longer delay than the default one (20us). */ chip->legacy.chip_delay = 40; @@ -1525,7 +1525,7 @@ static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc, /* Overload some methods for the HSMC controller. */ chip->legacy.cmd_ctrl = atmel_hsmc_nand_cmd_ctrl; - chip->select_chip = atmel_hsmc_nand_select_chip; + chip->legacy.select_chip = atmel_hsmc_nand_select_chip; } static int atmel_nand_controller_remove_nand(struct atmel_nand *nand) @@ -1908,6 +1908,7 @@ static int atmel_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops atmel_nand_controller_ops = { .attach_chip = atmel_nand_attach_chip, + .setup_data_interface = atmel_nand_setup_data_interface, }; static int atmel_nand_controller_init(struct atmel_nand_controller *nc, diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c @@ -430,7 +430,7 @@ static int au1550nd_probe(struct platform_device *pdev) ctx->cs = cs; this->legacy.dev_ready = au1550_device_ready; - this->select_chip = au1550_select_chip; + this->legacy.select_chip = au1550_select_chip; this->legacy.cmdfunc = au1550_command; /* 30 us command delay time */ diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c @@ -383,7 +383,7 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) u8 tbits, col_bits, col_size, row_bits, row_bsize; u32 val; - b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; + nand_chip->legacy.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; nand_chip->legacy.cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl; nand_chip->legacy.dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready; b47n->nand_chip.legacy.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c @@ -708,7 +708,7 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->nand.legacy.read_byte = cafe_read_byte; cafe->nand.legacy.read_buf = cafe_read_buf; cafe->nand.legacy.write_buf = cafe_write_buf; - cafe->nand.select_chip = cafe_select_chip; + cafe->nand.legacy.select_chip = cafe_select_chip; cafe->nand.legacy.set_features = nand_get_set_features_notsupp; cafe->nand.legacy.get_features = nand_get_set_features_notsupp; @@ -780,7 +780,7 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->usedma = 0; /* Scan to find existence of the device */ - cafe->nand.dummy_controller.ops = &cafe_nand_controller_ops; + cafe->nand.legacy.dummy_controller.ops = &cafe_nand_controller_ops; err = nand_scan(&cafe->nand, 2); if (err) goto out_irq; diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c @@ -762,7 +762,7 @@ static int nand_davinci_probe(struct platform_device *pdev) info->chip.legacy.IO_ADDR_R = vaddr; info->chip.legacy.IO_ADDR_W = vaddr; info->chip.legacy.chip_delay = 0; - info->chip.select_chip = nand_davinci_select_chip; + info->chip.legacy.select_chip = nand_davinci_select_chip; /* options such as NAND_BBT_USE_FLASH */ info->chip.bbt_options = pdata->bbt_options; @@ -801,7 +801,7 @@ static int nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - info->chip.dummy_controller.ops = &davinci_nand_controller_ops; + info->chip.legacy.dummy_controller.ops = &davinci_nand_controller_ops; ret = nand_scan(&info->chip, pdata->mask_chipsel ? 2 : 1); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c @@ -204,18 +204,6 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, return denali->irq_status; } -static uint32_t denali_check_irq(struct denali_nand_info *denali) -{ - unsigned long flags; - uint32_t irq_status; - - spin_lock_irqsave(&denali->irq_lock, flags); - irq_status = denali->irq_status; - spin_unlock_irqrestore(&denali->irq_lock, flags); - - return irq_status; -} - static void denali_read_buf(struct nand_chip *chip, uint8_t *buf, int len) { struct mtd_info *mtd = nand_to_mtd(chip); @@ -288,8 +276,7 @@ static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl) return; /* - * Some commands are followed by chip->legacy.dev_ready or - * chip->legacy.waitfunc. + * Some commands are followed by chip->legacy.waitfunc. * irq_status must be cleared here to catch the R/B# interrupt later. */ if (ctrl & NAND_CTRL_CHANGE) @@ -298,13 +285,6 @@ static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl) denali->host_write(denali, DENALI_BANK(denali) | type, dat); } -static int denali_dev_ready(struct nand_chip *chip) -{ - struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip)); - - return !!(denali_check_irq(denali) & INTR__INT_ACT); -} - static int denali_check_erased_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, unsigned long uncor_ecc_flags, @@ -1065,29 +1045,6 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr, return 0; } -static void denali_reset_banks(struct denali_nand_info *denali) -{ - u32 irq_status; - int i; - - for (i = 0; i < denali->max_banks; i++) { - denali->active_bank = i; - - denali_reset_irq(denali); - - iowrite32(DEVICE_RESET__BANK(i), - denali->reg + DEVICE_RESET); - - irq_status = denali_wait_for_irq(denali, - INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT); - if (!(irq_status & INTR__INT_ACT)) - break; - } - - dev_dbg(denali->dev, "%d chips connected\n", i); - denali->max_banks = i; -} - static void denali_hw_init(struct denali_nand_info *denali) { /* @@ -1316,6 +1273,7 @@ static void denali_detach_chip(struct nand_chip *chip) static const struct nand_controller_ops denali_controller_ops = { .attach_chip = denali_attach_chip, .detach_chip = denali_detach_chip, + .setup_data_interface = denali_setup_data_interface, }; int denali_init(struct denali_nand_info *denali) @@ -1341,12 +1299,6 @@ int denali_init(struct denali_nand_info *denali) } denali_enable_irq(denali); - denali_reset_banks(denali); - if (!denali->max_banks) { - /* Error out earlier if no chip is found for some reasons. */ - ret = -ENODEV; - goto disable_irq; - } denali->active_bank = DENALI_INVALID_BANK; @@ -1355,11 +1307,10 @@ int denali_init(struct denali_nand_info *denali) if (!mtd->name) mtd->name = "denali-nand"; - chip->select_chip = denali_select_chip; + chip->legacy.select_chip = denali_select_chip; chip->legacy.read_byte = denali_read_byte; chip->legacy.write_byte = denali_write_byte; chip->legacy.cmd_ctrl = denali_cmd_ctrl; - chip->legacy.dev_ready = denali_dev_ready; chip->legacy.waitfunc = denali_waitfunc; if (features & FEATURES__INDEX_ADDR) { @@ -1372,9 +1323,9 @@ int denali_init(struct denali_nand_info *denali) /* clk rate info is needed for setup_data_interface */ if (denali->clk_rate && denali->clk_x_rate) - chip->setup_data_interface = denali_setup_data_interface; + chip->options |= NAND_KEEP_TIMINGS; - chip->dummy_controller.ops = &denali_controller_ops; + chip->legacy.dummy_controller.ops = &denali_controller_ops; ret = nand_scan(chip, denali->max_banks); if (ret) goto disable_irq; diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h @@ -7,7 +7,7 @@ #ifndef __DENALI_H__ #define __DENALI_H__ -#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/completion.h> #include <linux/mtd/rawnand.h> #include <linux/spinlock_types.h> diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c @@ -1390,7 +1390,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) this->legacy.read_buf = doc2001plus_readbuf; doc->late_init = inftl_scan_bbt; this->legacy.cmd_ctrl = NULL; - this->select_chip = doc2001plus_select_chip; + this->legacy.select_chip = doc2001plus_select_chip; this->legacy.cmdfunc = doc2001plus_command; this->ecc.hwctl = doc2001plus_enable_hwecc; @@ -1568,7 +1568,7 @@ static int __init doc_probe(unsigned long physadr) mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops); nand_set_controller_data(nand, doc); - nand->select_chip = doc200x_select_chip; + nand->legacy.select_chip = doc200x_select_chip; nand->legacy.cmd_ctrl = doc200x_hwcontrol; nand->legacy.dev_ready = doc200x_dev_ready; nand->legacy.waitfunc = doc200x_wait; diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -779,7 +779,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->legacy.read_byte = fsl_elbc_read_byte; chip->legacy.write_buf = fsl_elbc_write_buf; chip->legacy.read_buf = fsl_elbc_read_buf; - chip->select_chip = fsl_elbc_select_chip; + chip->legacy.select_chip = fsl_elbc_select_chip; chip->legacy.cmdfunc = fsl_elbc_cmdfunc; chip->legacy.waitfunc = fsl_elbc_wait; chip->legacy.set_features = nand_get_set_features_notsupp; diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -864,7 +864,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->legacy.write_buf = fsl_ifc_write_buf; chip->legacy.read_buf = fsl_ifc_read_buf; - chip->select_chip = fsl_ifc_select_chip; + chip->legacy.select_chip = fsl_ifc_select_chip; chip->legacy.cmdfunc = fsl_ifc_cmdfunc; chip->legacy.waitfunc = fsl_ifc_wait; chip->legacy.set_features = nand_get_set_features_notsupp; diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c @@ -170,7 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun, fun->chip.ecc.mode = NAND_ECC_SOFT; fun->chip.ecc.algo = NAND_ECC_HAMMING; if (fun->mchip_count > 1) - fun->chip.select_chip = fun_select_chip; + fun->chip.legacy.select_chip = fun_select_chip; if (fun->rnb_gpio[0] >= 0) fun->chip.legacy.dev_ready = fun_chip_ready; diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ST Microelectronics * Flexible Static Memory Controller (FSMC) @@ -10,10 +11,6 @@ * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8) * Copyright © 2007 STMicroelectronics Pvt. Ltd. * Copyright © 2009 Alessandro Rubini - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> @@ -41,15 +38,14 @@ /* fsmc controller registers for NOR flash */ #define CTRL 0x0 /* ctrl register definitions */ - #define BANK_ENABLE (1 << 0) - #define MUXED (1 << 1) + #define BANK_ENABLE BIT(0) + #define MUXED BIT(1) #define NOR_DEV (2 << 2) - #define WIDTH_8 (0 << 4) - #define WIDTH_16 (1 << 4) - #define RSTPWRDWN (1 << 6) - #define WPROT (1 << 7) - #define WRT_ENABLE (1 << 12) - #define WAIT_ENB (1 << 13) + #define WIDTH_16 BIT(4) + #define RSTPWRDWN BIT(6) + #define WPROT BIT(7) + #define WRT_ENABLE BIT(12) + #define WAIT_ENB BIT(13) #define CTRL_TIM 0x4 /* ctrl_tim register definitions */ @@ -57,43 +53,35 @@ #define FSMC_NOR_BANK_SZ 0x8 #define FSMC_NOR_REG_SIZE 0x40 -#define FSMC_NOR_REG(base, bank, reg) (base + \ - FSMC_NOR_BANK_SZ * (bank) + \ - reg) +#define FSMC_NOR_REG(base, bank, reg) ((base) + \ + (FSMC_NOR_BANK_SZ * (bank)) + \ + (reg)) /* fsmc controller registers for NAND flash */ #define FSMC_PC 0x00 /* pc register definitions */ - #define FSMC_RESET (1 << 0) - #define FSMC_WAITON (1 << 1) - #define FSMC_ENABLE (1 << 2) - #define FSMC_DEVTYPE_NAND (1 << 3) - #define FSMC_DEVWID_8 (0 << 4) - #define FSMC_DEVWID_16 (1 << 4) - #define FSMC_ECCEN (1 << 6) - #define FSMC_ECCPLEN_512 (0 << 7) - #define FSMC_ECCPLEN_256 (1 << 7) - #define FSMC_TCLR_1 (1) + #define FSMC_RESET BIT(0) + #define FSMC_WAITON BIT(1) + #define FSMC_ENABLE BIT(2) + #define FSMC_DEVTYPE_NAND BIT(3) + #define FSMC_DEVWID_16 BIT(4) + #define FSMC_ECCEN BIT(6) + #define FSMC_ECCPLEN_256 BIT(7) #define FSMC_TCLR_SHIFT (9) #define FSMC_TCLR_MASK (0xF) - #define FSMC_TAR_1 (1) #define FSMC_TAR_SHIFT (13) #define FSMC_TAR_MASK (0xF) #define STS 0x04 /* sts register definitions */ - #define FSMC_CODE_RDY (1 << 15) + #define FSMC_CODE_RDY BIT(15) #define COMM 0x08 /* comm register definitions */ - #define FSMC_TSET_0 0 #define FSMC_TSET_SHIFT 0 #define FSMC_TSET_MASK 0xFF - #define FSMC_TWAIT_6 6 #define FSMC_TWAIT_SHIFT 8 #define FSMC_TWAIT_MASK 0xFF - #define FSMC_THOLD_4 4 #define FSMC_THOLD_SHIFT 16 #define FSMC_THOLD_MASK 0xFF - #define FSMC_THIZ_1 1 #define FSMC_THIZ_SHIFT 24 #define FSMC_THIZ_MASK 0xFF #define ATTRIB 0x0C @@ -106,12 +94,12 @@ #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) struct fsmc_nand_timings { - uint8_t tclr; - uint8_t tar; - uint8_t thiz; - uint8_t thold; - uint8_t twait; - uint8_t tset; + u8 tclr; + u8 tar; + u8 thiz; + u8 thold; + u8 twait; + u8 tset; }; enum access_mode { @@ -122,19 +110,21 @@ enum access_mode { /** * struct fsmc_nand_data - structure for FSMC NAND device state * + * @base: Inherit from the nand_controller struct * @pid: Part ID on the AMBA PrimeCell format - * @mtd: MTD info for a NAND flash. * @nand: Chip related info for a NAND flash. - * @partitions: Partition info for a NAND Flash. - * @nr_partitions: Total number of partition of a NAND flash. * * @bank: Bank number for probed device. + * @dev: Parent device + * @mode: Access mode * @clk: Clock structure for FSMC. * * @read_dma_chan: DMA channel for read access * @write_dma_chan: DMA channel for write access to NAND * @dma_access_complete: Completion structure * + * @dev_timings: NAND timings + * * @data_pa: NAND Physical port for Data. * @data_va: NAND port for Data. * @cmd_va: NAND port for Command. @@ -142,6 +132,7 @@ enum access_mode { * @regs_va: Registers base address for a given bank. */ struct fsmc_nand_data { + struct nand_controller base; u32 pid; struct nand_chip nand; @@ -248,9 +239,9 @@ static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = { .free = fsmc_ecc4_ooblayout_free, }; -static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd) +static inline struct fsmc_nand_data *nand_to_fsmc(struct nand_chip *chip) { - return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand); + return container_of(chip, struct fsmc_nand_data, nand); } /* @@ -262,8 +253,8 @@ static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd) static void fsmc_nand_setup(struct fsmc_nand_data *host, struct fsmc_nand_timings *tims) { - uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; - uint32_t tclr, tar, thiz, thold, twait, tset; + u32 value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; + u32 tclr, tar, thiz, thold, twait, tset; tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; @@ -273,13 +264,9 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host, tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (host->nand.options & NAND_BUSWIDTH_16) - writel_relaxed(value | FSMC_DEVWID_16, - host->regs_va + FSMC_PC); - else - writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + FSMC_PC); + value |= FSMC_DEVWID_16; - writel_relaxed(readl(host->regs_va + FSMC_PC) | tclr | tar, - host->regs_va + FSMC_PC); + writel_relaxed(value | tclr | tar, host->regs_va + FSMC_PC); writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM); writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB); } @@ -290,7 +277,7 @@ static int fsmc_calc_timings(struct fsmc_nand_data *host, { unsigned long hclk = clk_get_rate(host->clk); unsigned long hclkn = NSEC_PER_SEC / hclk; - uint32_t thiz, thold, twait, tset; + u32 thiz, thold, twait, tset; if (sdrt->tRC_min < 30000) return -EOPNOTSUPP; @@ -343,7 +330,7 @@ static int fsmc_calc_timings(struct fsmc_nand_data *host, static int fsmc_setup_data_interface(struct nand_chip *nand, int csline, const struct nand_data_interface *conf) { - struct fsmc_nand_data *host = nand_get_controller_data(nand); + struct fsmc_nand_data *host = nand_to_fsmc(nand); struct fsmc_nand_timings tims; const struct nand_sdr_timings *sdrt; int ret; @@ -369,7 +356,7 @@ static int fsmc_setup_data_interface(struct nand_chip *nand, int csline, */ static void fsmc_enable_hwecc(struct nand_chip *chip, int mode) { - struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip)); + struct fsmc_nand_data *host = nand_to_fsmc(chip); writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCPLEN_256, host->regs_va + FSMC_PC); @@ -384,18 +371,18 @@ static void fsmc_enable_hwecc(struct nand_chip *chip, int mode) * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to * max of 8-bits) */ -static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const uint8_t *data, - uint8_t *ecc) +static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const u8 *data, + u8 *ecc) { - struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip)); - uint32_t ecc_tmp; + struct fsmc_nand_data *host = nand_to_fsmc(chip); + u32 ecc_tmp; unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; do { if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY) break; - else - cond_resched(); + + cond_resched(); } while (!time_after_eq(jiffies, deadline)); if (time_after_eq(jiffies, deadline)) { @@ -404,25 +391,25 @@ static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const uint8_t *data, } ecc_tmp = readl_relaxed(host->regs_va + ECC1); - ecc[0] = (uint8_t) (ecc_tmp >> 0); - ecc[1] = (uint8_t) (ecc_tmp >> 8); - ecc[2] = (uint8_t) (ecc_tmp >> 16); - ecc[3] = (uint8_t) (ecc_tmp >> 24); + ecc[0] = ecc_tmp; + ecc[1] = ecc_tmp >> 8; + ecc[2] = ecc_tmp >> 16; + ecc[3] = ecc_tmp >> 24; ecc_tmp = readl_relaxed(host->regs_va + ECC2); - ecc[4] = (uint8_t) (ecc_tmp >> 0); - ecc[5] = (uint8_t) (ecc_tmp >> 8); - ecc[6] = (uint8_t) (ecc_tmp >> 16); - ecc[7] = (uint8_t) (ecc_tmp >> 24); + ecc[4] = ecc_tmp; + ecc[5] = ecc_tmp >> 8; + ecc[6] = ecc_tmp >> 16; + ecc[7] = ecc_tmp >> 24; ecc_tmp = readl_relaxed(host->regs_va + ECC3); - ecc[8] = (uint8_t) (ecc_tmp >> 0); - ecc[9] = (uint8_t) (ecc_tmp >> 8); - ecc[10] = (uint8_t) (ecc_tmp >> 16); - ecc[11] = (uint8_t) (ecc_tmp >> 24); + ecc[8] = ecc_tmp; + ecc[9] = ecc_tmp >> 8; + ecc[10] = ecc_tmp >> 16; + ecc[11] = ecc_tmp >> 24; ecc_tmp = readl_relaxed(host->regs_va + STS); - ecc[12] = (uint8_t) (ecc_tmp >> 16); + ecc[12] = ecc_tmp >> 16; return 0; } @@ -432,22 +419,22 @@ static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const uint8_t *data, * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to * max of 1-bit) */ -static int fsmc_read_hwecc_ecc1(struct nand_chip *chip, const uint8_t *data, - uint8_t *ecc) +static int fsmc_read_hwecc_ecc1(struct nand_chip *chip, const u8 *data, + u8 *ecc) { - struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip)); - uint32_t ecc_tmp; + struct fsmc_nand_data *host = nand_to_fsmc(chip); + u32 ecc_tmp; ecc_tmp = readl_relaxed(host->regs_va + ECC1); - ecc[0] = (uint8_t) (ecc_tmp >> 0); - ecc[1] = (uint8_t) (ecc_tmp >> 8); - ecc[2] = (uint8_t) (ecc_tmp >> 16); + ecc[0] = ecc_tmp; + ecc[1] = ecc_tmp >> 8; + ecc[2] = ecc_tmp >> 16; return 0; } /* Count the number of 0's in buff upto a max of max_bits */ -static int count_written_bits(uint8_t *buff, int size, int max_bits) +static int count_written_bits(u8 *buff, int size, int max_bits) { int k, written_bits = 0; @@ -468,7 +455,7 @@ static void dma_complete(void *param) } static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, - enum dma_data_direction direction) + enum dma_data_direction direction) { struct dma_chan *chan; struct dma_device *dma_dev; @@ -519,7 +506,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, time_left = wait_for_completion_timeout(&host->dma_access_complete, - msecs_to_jiffies(3000)); + msecs_to_jiffies(3000)); if (time_left == 0) { dmaengine_terminate_all(chan); dev_err(host->dev, "wait_for_completion_timeout\n"); @@ -537,18 +524,19 @@ unmap_dma: /* * fsmc_write_buf - write buffer to chip - * @mtd: MTD device structure + * @host: FSMC NAND controller * @buf: data buffer * @len: number of bytes to write */ -static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +static void fsmc_write_buf(struct fsmc_nand_data *host, const u8 *buf, + int len) { - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) && - IS_ALIGNED(len, sizeof(uint32_t))) { - uint32_t *p = (uint32_t *)buf; + if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) && + IS_ALIGNED(len, sizeof(u32))) { + u32 *p = (u32 *)buf; + len = len >> 2; for (i = 0; i < len; i++) writel_relaxed(p[i], host->data_va); @@ -560,18 +548,18 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) /* * fsmc_read_buf - read chip data into buffer - * @mtd: MTD device structure + * @host: FSMC NAND controller * @buf: buffer to store date * @len: number of bytes to read */ -static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +static void fsmc_read_buf(struct fsmc_nand_data *host, u8 *buf, int len) { - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) && - IS_ALIGNED(len, sizeof(uint32_t))) { - uint32_t *p = (uint32_t *)buf; + if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) && + IS_ALIGNED(len, sizeof(u32))) { + u32 *p = (u32 *)buf; + len = len >> 2; for (i = 0; i < len; i++) p[i] = readl_relaxed(host->data_va); @@ -583,48 +571,42 @@ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) /* * fsmc_read_buf_dma - read chip data into buffer - * @mtd: MTD device structure + * @host: FSMC NAND controller * @buf: buffer to store date * @len: number of bytes to read */ -static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len) +static void fsmc_read_buf_dma(struct fsmc_nand_data *host, u8 *buf, + int len) { - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - dma_xfer(host, buf, len, DMA_FROM_DEVICE); } /* * fsmc_write_buf_dma - write buffer to chip - * @mtd: MTD device structure + * @host: FSMC NAND controller * @buf: data buffer * @len: number of bytes to write */ -static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, - int len) +static void fsmc_write_buf_dma(struct fsmc_nand_data *host, const u8 *buf, + int len) { - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); } /* fsmc_select_chip - assert or deassert nCE */ -static void fsmc_select_chip(struct nand_chip *chip, int chipnr) +static void fsmc_ce_ctrl(struct fsmc_nand_data *host, bool assert) { - struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip)); - u32 pc; - - /* Support only one CS */ - if (chipnr > 0) - return; + u32 pc = readl(host->regs_va + FSMC_PC); - pc = readl(host->regs_va + FSMC_PC); - if (chipnr < 0) + if (!assert) writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC); else writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC); - /* nCE line must be asserted before starting any operation */ + /* + * nCE line changes must be applied before returning from this + * function. + */ mb(); } @@ -637,14 +619,16 @@ static void fsmc_select_chip(struct nand_chip *chip, int chipnr) static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, bool check_only) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + struct fsmc_nand_data *host = nand_to_fsmc(chip); const struct nand_op_instr *instr = NULL; int ret = 0; unsigned int op_id; int i; pr_debug("Executing operation [%d instructions]:\n", op->ninstrs); + + fsmc_ce_ctrl(host, true); + for (op_id = 0; op_id < op->ninstrs; op_id++) { instr = &op->instrs[op_id]; @@ -671,10 +655,10 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, ", force 8-bit" : ""); if (host->mode == USE_DMA_ACCESS) - fsmc_read_buf_dma(mtd, instr->ctx.data.buf.in, + fsmc_read_buf_dma(host, instr->ctx.data.buf.in, instr->ctx.data.len); else - fsmc_read_buf(mtd, instr->ctx.data.buf.in, + fsmc_read_buf(host, instr->ctx.data.buf.in, instr->ctx.data.len); break; @@ -684,10 +668,11 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, ", force 8-bit" : ""); if (host->mode == USE_DMA_ACCESS) - fsmc_write_buf_dma(mtd, instr->ctx.data.buf.out, + fsmc_write_buf_dma(host, + instr->ctx.data.buf.out, instr->ctx.data.len); else - fsmc_write_buf(mtd, instr->ctx.data.buf.out, + fsmc_write_buf(host, instr->ctx.data.buf.out, instr->ctx.data.len); break; @@ -701,6 +686,8 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, } } + fsmc_ce_ctrl(host, false); + return ret; } @@ -717,34 +704,35 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, * After this read, fsmc hardware generates and reports error data bits(up to a * max of 8 bits) */ -static int fsmc_read_page_hwecc(struct nand_chip *chip, uint8_t *buf, +static int fsmc_read_page_hwecc(struct nand_chip *chip, u8 *buf, int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); int i, j, s, stat, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->ecc.calc_buf; - uint8_t *ecc_code = chip->ecc.code_buf; - int off, len, group = 0; + u8 *p = buf; + u8 *ecc_calc = chip->ecc.calc_buf; + u8 *ecc_code = chip->ecc.code_buf; + int off, len, ret, group = 0; /* - * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we + * ecc_oob is intentionally taken as u16. In 16bit devices, we * end up reading 14 bytes (7 words) from oob. The local array is * to maintain word alignment */ - uint16_t ecc_oob[7]; - uint8_t *oob = (uint8_t *)&ecc_oob[0]; + u16 ecc_oob[7]; + u8 *oob = (u8 *)&ecc_oob[0]; unsigned int max_bitflips = 0; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { nand_read_page_op(chip, page, s * eccsize, NULL, 0); chip->ecc.hwctl(chip, NAND_ECC_READ); - nand_read_data_op(chip, p, eccsize, false); + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; for (j = 0; j < eccbytes;) { struct mtd_oob_region oobregion; - int ret; ret = mtd_ooblayout_ecc(mtd, group++, &oobregion); if (ret) @@ -788,15 +776,15 @@ static int fsmc_read_page_hwecc(struct nand_chip *chip, uint8_t *buf, * @calc_ecc: ecc calculated from read data * * calc_ecc is a 104 bit information containing maximum of 8 error - * offset informations of 13 bits each in 512 bytes of read data. + * offset information of 13 bits each in 512 bytes of read data. */ -static int fsmc_bch8_correct_data(struct nand_chip *chip, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) +static int fsmc_bch8_correct_data(struct nand_chip *chip, u8 *dat, + u8 *read_ecc, u8 *calc_ecc) { - struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip)); - uint32_t err_idx[8]; - uint32_t num_err, i; - uint32_t ecc1, ecc2, ecc3, ecc4; + struct fsmc_nand_data *host = nand_to_fsmc(chip); + u32 err_idx[8]; + u32 num_err, i; + u32 ecc1, ecc2, ecc3, ecc4; num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF; @@ -837,8 +825,8 @@ static int fsmc_bch8_correct_data(struct nand_chip *chip, uint8_t *dat, * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--| * * calc_ecc is a 104 bit information containing maximum of 8 error - * offset informations of 13 bits each. calc_ecc is copied into a - * uint64_t array and error offset indexes are populated in err_idx + * offset information of 13 bits each. calc_ecc is copied into a + * u64 array and error offset indexes are populated in err_idx * array */ ecc1 = readl_relaxed(host->regs_va + ECC1); @@ -897,11 +885,13 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev, nand->options |= NAND_SKIP_BBTSCAN; host->dev_timings = devm_kzalloc(&pdev->dev, - sizeof(*host->dev_timings), GFP_KERNEL); + sizeof(*host->dev_timings), + GFP_KERNEL); if (!host->dev_timings) return -ENOMEM; + ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings, - sizeof(*host->dev_timings)); + sizeof(*host->dev_timings)); if (ret) host->dev_timings = NULL; @@ -920,7 +910,7 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev, static int fsmc_nand_attach_chip(struct nand_chip *nand) { struct mtd_info *mtd = nand_to_mtd(nand); - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + struct fsmc_nand_data *host = nand_to_fsmc(nand); if (AMBA_REV_BITS(host->pid) >= 8) { switch (mtd->oobsize) { @@ -992,6 +982,8 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) static const struct nand_controller_ops fsmc_nand_controller_ops = { .attach_chip = fsmc_nand_attach_chip, + .exec_op = fsmc_exec_op, + .setup_data_interface = fsmc_setup_data_interface, }; /* @@ -1061,10 +1053,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * AMBA PrimeCell bus. However it is not a PrimeCell. */ for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); + pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & + 255) << (i * 8); + host->pid = pid; - dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, " - "revision %02x, config %02x\n", + + dev_info(&pdev->dev, + "FSMC device partno %03x, manufacturer %02x, revision %02x, config %02x\n", AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid), AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid)); @@ -1075,12 +1070,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) /* Link all private pointers */ mtd = nand_to_mtd(&host->nand); - nand_set_controller_data(nand, host); nand_set_flash_node(nand, pdev->dev.of_node); mtd->dev.parent = &pdev->dev; - nand->exec_op = fsmc_exec_op; - nand->select_chip = fsmc_select_chip; /* * Setup default ECC mode. nand_dt_init() called from nand_scan_ident() @@ -1106,10 +1098,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } } - if (host->dev_timings) + if (host->dev_timings) { fsmc_nand_setup(host, host->dev_timings); - else - nand->setup_data_interface = fsmc_setup_data_interface; + nand->options |= NAND_KEEP_TIMINGS; + } if (AMBA_REV_BITS(host->pid) >= 8) { nand->ecc.read_page = fsmc_read_page_hwecc; @@ -1119,10 +1111,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.strength = 8; } + nand_controller_init(&host->base); + host->base.ops = &fsmc_nand_controller_ops; + nand->controller = &host->base; + /* * Scan to find existence of the device */ - nand->dummy_controller.ops = &fsmc_nand_controller_ops; ret = nand_scan(nand, 1); if (ret) goto release_dma_write_chan; @@ -1175,19 +1170,23 @@ static int fsmc_nand_remove(struct platform_device *pdev) static int fsmc_nand_suspend(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); + if (host) clk_disable_unprepare(host->clk); + return 0; } static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); + if (host) { clk_prepare_enable(host->clk); if (host->dev_timings) fsmc_nand_setup(host, host->dev_timings); } + return 0; } #endif @@ -1212,6 +1211,6 @@ static struct platform_driver fsmc_nand_driver = { module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi"); MODULE_DESCRIPTION("NAND driver for SPEAr Platforms"); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1549,7 +1549,7 @@ static int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs) int column, page, chipnr; chipnr = (int)(ofs >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); column = !GPMI_IS_MX23(this) ? mtd->writesize : 0; @@ -1562,7 +1562,7 @@ static int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs) ret = nand_prog_page_op(chip, page, column, block_mark, 1); - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } @@ -1610,7 +1610,7 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; saved_chip_number = this->current_chip; - chip->select_chip(chip, 0); + nand_select_target(chip, 0); /* * Loop through the first search area, looking for the NCB fingerprint. @@ -1638,7 +1638,10 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) } - chip->select_chip(chip, saved_chip_number); + if (saved_chip_number >= 0) + nand_select_target(chip, saved_chip_number); + else + nand_deselect_target(chip); if (found_an_ncb_fingerprint) dev_dbg(dev, "\tFound a fingerprint\n"); @@ -1681,7 +1684,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Select chip 0. */ saved_chip_number = this->current_chip; - chip->select_chip(chip, 0); + nand_select_target(chip, 0); /* Loop over blocks in the first search area, erasing them. */ dev_dbg(dev, "Erasing the search area...\n"); @@ -1713,7 +1716,11 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) } /* Deselect chip 0. */ - chip->select_chip(chip, saved_chip_number); + if (saved_chip_number >= 0) + nand_select_target(chip, saved_chip_number); + else + nand_deselect_target(chip); + return 0; } @@ -1762,10 +1769,10 @@ static int mx23_boot_init(struct gpmi_nand_data *this) byte = block << chip->phys_erase_shift; /* Send the command to read the conventional block mark. */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); nand_read_page_op(chip, page, mtd->writesize, NULL, 0); block_mark = chip->legacy.read_byte(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); /* * Check if the block is marked bad. If so, we need to mark it @@ -1882,6 +1889,7 @@ static int gpmi_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops gpmi_nand_controller_ops = { .attach_chip = gpmi_nand_attach_chip, + .setup_data_interface = gpmi_setup_data_interface, }; static int gpmi_nand_init(struct gpmi_nand_data *this) @@ -1900,8 +1908,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ nand_set_controller_data(chip, this); nand_set_flash_node(chip, this->pdev->dev.of_node); - chip->select_chip = gpmi_select_chip; - chip->setup_data_interface = gpmi_setup_data_interface; + chip->legacy.select_chip = gpmi_select_chip; chip->legacy.cmd_ctrl = gpmi_cmd_ctrl; chip->legacy.dev_ready = gpmi_dev_ready; chip->legacy.read_byte = gpmi_read_byte; @@ -1924,7 +1931,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) if (ret) goto err_out; - chip->dummy_controller.ops = &gpmi_nand_controller_ops; + chip->legacy.dummy_controller.ops = &gpmi_nand_controller_ops; ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1); if (ret) goto err_out; diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c @@ -783,7 +783,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) nand_set_controller_data(chip, host); nand_set_flash_node(chip, np); chip->legacy.cmdfunc = hisi_nfc_cmdfunc; - chip->select_chip = hisi_nfc_select_chip; + chip->legacy.select_chip = hisi_nfc_select_chip; chip->legacy.read_byte = hisi_nfc_read_byte; chip->legacy.write_buf = hisi_nfc_write_buf; chip->legacy.read_buf = hisi_nfc_read_buf; @@ -799,7 +799,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) return ret; } - chip->dummy_controller.ops = &hisi_nfc_controller_ops; + chip->legacy.dummy_controller.ops = &hisi_nfc_controller_ops; ret = nand_scan(chip, max_chips); if (ret) return ret; diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h @@ -95,6 +95,39 @@ void nand_decode_ext_id(struct nand_chip *chip); void panic_nand_wait(struct nand_chip *chip, unsigned long timeo); void sanitize_string(uint8_t *s, size_t len); +static inline bool nand_has_exec_op(struct nand_chip *chip) +{ + if (!chip->controller || !chip->controller->ops || + !chip->controller->ops->exec_op) + return false; + + return true; +} + +static inline int nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op) +{ + if (!nand_has_exec_op(chip)) + return -ENOTSUPP; + + if (WARN_ON(op->cs >= chip->numchips)) + return -EINVAL; + + return chip->controller->ops->exec_op(chip, op, false); +} + +static inline bool nand_has_setup_data_iface(struct nand_chip *chip) +{ + if (!chip->controller || !chip->controller->ops || + !chip->controller->ops->setup_data_interface) + return false; + + if (chip->options & NAND_KEEP_TIMINGS) + return false; + + return true; +} + /* BBT functions */ int nand_markbad_bbt(struct nand_chip *chip, loff_t offs); int nand_isreserved_bbt(struct nand_chip *chip, loff_t offs); diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c @@ -335,14 +335,14 @@ static int jz_nand_detect_bank(struct platform_device *pdev, goto notfound_id; /* Retrieve the IDs from the first chip. */ - chip->select_chip(chip, 0); + nand_select_target(chip, 0); nand_reset_op(chip); nand_readid_op(chip, 0, id, sizeof(id)); *nand_maf_id = id[0]; *nand_dev_id = id[1]; } else { /* Detect additional chip. */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); nand_reset_op(chip); nand_readid_op(chip, 0, id, sizeof(id)); if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) { @@ -427,8 +427,8 @@ static int jz_nand_probe(struct platform_device *pdev) chip->legacy.chip_delay = 50; chip->legacy.cmd_ctrl = jz_nand_cmd_ctrl; - chip->select_chip = jz_nand_select_chip; - chip->dummy_controller.ops = &jz_nand_controller_ops; + chip->legacy.select_chip = jz_nand_select_chip; + chip->legacy.dummy_controller.ops = &jz_nand_controller_ops; if (nand->busy_gpio) chip->legacy.dev_ready = jz_nand_dev_ready; diff --git a/drivers/mtd/nand/raw/jz4780_bch.c b/drivers/mtd/nand/raw/jz4780_bch.c @@ -136,8 +136,10 @@ static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf, 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; diff --git a/drivers/mtd/nand/raw/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c @@ -279,7 +279,7 @@ static int jz4780_nand_init_chip(struct platform_device *pdev, chip->legacy.IO_ADDR_W = cs->base + OFFSET_DATA; chip->legacy.chip_delay = RB_DELAY_US; chip->options = NAND_NO_SUBPAGE_WRITE; - chip->select_chip = jz4780_nand_select_chip; + 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; diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -799,7 +799,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) * Scan to find existence of the device and get the type of NAND device: * SMALL block or LARGE block. */ - nand_chip->dummy_controller.ops = &lpc32xx_nand_controller_ops; + nand_chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops; res = nand_scan(nand_chip, 1); if (res) goto free_irq; diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -924,7 +924,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } /* Find NAND device */ - chip->dummy_controller.ops = &lpc32xx_nand_controller_ops; + chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops; res = nand_scan(chip, 1); if (res) goto release_dma; diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c @@ -378,7 +378,7 @@ struct marvell_nfc_caps { * @dev: Parent device (used to print error messages) * @regs: NAND controller registers * @core_clk: Core clock - * @reg_clk: Regiters clock + * @reg_clk: Registers clock * @complete: Completion object to wait for NAND controller events * @assigned_cs: Bitmask describing already assigned CS lines * @chips: List containing all the NAND chips attached to @@ -514,9 +514,14 @@ static void marvell_nfc_enable_int(struct marvell_nfc *nfc, u32 int_mask) writel_relaxed(reg & ~int_mask, nfc->regs + NDCR); } -static void marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask) +static u32 marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask) { + u32 reg; + + reg = readl_relaxed(nfc->regs + NDSR); writel_relaxed(int_mask, nfc->regs + NDSR); + + return reg & int_mask; } static void marvell_nfc_force_byte_access(struct nand_chip *chip, @@ -683,6 +688,7 @@ static int marvell_nfc_wait_cmdd(struct nand_chip *chip) static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) { struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 pending; int ret; /* Timeout is expressed in ms */ @@ -695,8 +701,13 @@ static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) ret = wait_for_completion_timeout(&nfc->complete, msecs_to_jiffies(timeout_ms)); marvell_nfc_disable_int(nfc, NDCR_RDYM); - marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1)); - if (!ret) { + pending = marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1)); + + /* + * In case the interrupt was not served in the required time frame, + * check if the ISR was not served or if something went actually wrong. + */ + if (ret && !pending) { dev_err(nfc->dev, "Timeout waiting for RB signal\n"); return -ETIMEDOUT; } @@ -704,7 +715,8 @@ static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) return 0; } -static void marvell_nfc_select_chip(struct nand_chip *chip, int die_nr) +static void marvell_nfc_select_target(struct nand_chip *chip, + unsigned int die_nr) { struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); @@ -713,12 +725,6 @@ static void marvell_nfc_select_chip(struct nand_chip *chip, int die_nr) if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die) return; - if (die_nr < 0 || die_nr >= marvell_nand->nsels) { - nfc->selected_chip = NULL; - marvell_nand->selected_die = -1; - return; - } - writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0); writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1); @@ -1024,13 +1030,13 @@ static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip, } ret = marvell_nfc_wait_cmdd(chip); - return ret; } static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_required, int page) { + marvell_nfc_select_target(chip, chip->cur_cs); return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, true, page); } @@ -1043,6 +1049,7 @@ static int marvell_nfc_hw_ecc_hmg_read_page(struct nand_chip *chip, u8 *buf, int max_bitflips = 0, ret; u8 *raw_buf; + marvell_nfc_select_target(chip, chip->cur_cs); marvell_nfc_enable_hw_ecc(chip); marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, false, page); @@ -1079,6 +1086,7 @@ static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct nand_chip *chip, int page) /* Invalidate page cache */ chip->pagebuf = -1; + 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); } @@ -1142,6 +1150,7 @@ static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct nand_chip *chip, const u8 *buf, int oob_required, int page) { + marvell_nfc_select_target(chip, chip->cur_cs); return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, true, page); } @@ -1152,6 +1161,7 @@ static int marvell_nfc_hw_ecc_hmg_write_page(struct nand_chip *chip, { int ret; + marvell_nfc_select_target(chip, chip->cur_cs); marvell_nfc_enable_hw_ecc(chip); ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, false, page); @@ -1175,6 +1185,7 @@ static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct nand_chip *chip, memset(chip->data_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); } @@ -1194,6 +1205,8 @@ static int marvell_nfc_hw_ecc_bch_read_page_raw(struct nand_chip *chip, u8 *buf, int ecc_len = lt->ecc_bytes; int chunk; + marvell_nfc_select_target(chip, chip->cur_cs); + if (oob_required) memset(chip->oob_poi, 0xFF, mtd->oobsize); @@ -1304,6 +1317,8 @@ static int marvell_nfc_hw_ecc_bch_read_page(struct nand_chip *chip, u32 failure_mask = 0; int chunk, ret; + marvell_nfc_select_target(chip, chip->cur_cs); + /* * With BCH, OOB is not fully used (and thus not read entirely), not * expected bytes could show up at the end of the OOB buffer if not @@ -1448,6 +1463,8 @@ static int marvell_nfc_hw_ecc_bch_write_page_raw(struct nand_chip *chip, lt->last_spare_bytes; int chunk; + marvell_nfc_select_target(chip, chip->cur_cs); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); for (chunk = 0; chunk < lt->nchunks; chunk++) { @@ -1559,6 +1576,8 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip, int spare_len = lt->spare_bytes; int chunk, ret; + marvell_nfc_select_target(chip, chip->cur_cs); + /* Spare data will be written anyway, so clear it to avoid garbage */ if (!oob_required) memset(chip->oob_poi, 0xFF, mtd->oobsize); @@ -2097,6 +2116,8 @@ static int marvell_nfc_exec_op(struct nand_chip *chip, { struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + marvell_nfc_select_target(chip, op->cs); + if (nfc->caps->is_nfcv2) return nand_op_parser_exec_op(chip, &marvell_nfcv2_op_parser, op, check_only); @@ -2495,6 +2516,8 @@ static int marvell_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops marvell_nand_controller_ops = { .attach_chip = marvell_nand_attach_chip, + .exec_op = marvell_nfc_exec_op, + .setup_data_interface = marvell_nfc_setup_data_interface, }; static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, @@ -2617,10 +2640,8 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, chip->controller = &nfc->controller; nand_set_flash_node(chip, np); - chip->exec_op = marvell_nfc_exec_op; - chip->select_chip = marvell_nfc_select_chip; if (!of_property_read_bool(np, "marvell,nand-keep-config")) - chip->setup_data_interface = marvell_nfc_setup_data_interface; + chip->options |= NAND_KEEP_TIMINGS; mtd = nand_to_mtd(chip); mtd->dev.parent = dev; diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -697,7 +697,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip->legacy.read_byte = mpc5121_nfc_read_byte; chip->legacy.read_buf = mpc5121_nfc_read_buf; chip->legacy.write_buf = mpc5121_nfc_write_buf; - chip->select_chip = mpc5121_nfc_select_chip; + chip->legacy.select_chip = mpc5121_nfc_select_chip; chip->legacy.set_features = nand_get_set_features_notsupp; chip->legacy.get_features = nand_get_set_features_notsupp; chip->bbt_options = NAND_BBT_USE_FLASH; @@ -712,7 +712,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) return retval; } - chip->select_chip = ads5121_select_chip; + chip->legacy.select_chip = ads5121_select_chip; } /* Enable NFC clock */ diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c @@ -1288,6 +1288,7 @@ static int mtk_nfc_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops mtk_nfc_controller_ops = { .attach_chip = mtk_nfc_attach_chip, + .setup_data_interface = mtk_nfc_setup_data_interface, }; static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, @@ -1333,13 +1334,12 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ; nand->legacy.dev_ready = mtk_nfc_dev_ready; - nand->select_chip = mtk_nfc_select_chip; + nand->legacy.select_chip = mtk_nfc_select_chip; nand->legacy.write_byte = mtk_nfc_write_byte; nand->legacy.write_buf = mtk_nfc_write_buf; nand->legacy.read_byte = mtk_nfc_read_byte; nand->legacy.read_buf = mtk_nfc_read_buf; nand->legacy.cmd_ctrl = mtk_nfc_cmd_ctrl; - nand->setup_data_interface = mtk_nfc_setup_data_interface; /* set default mode in case dt entry is missing */ nand->ecc.mode = NAND_ECC_HW; diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c @@ -1738,8 +1738,17 @@ static int mxcnd_attach_chip(struct nand_chip *chip) return 0; } +static int mxcnd_setup_data_interface(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + return host->devtype_data->setup_data_interface(chip, chipnr, conf); +} + static const struct nand_controller_ops mxcnd_controller_ops = { .attach_chip = mxcnd_attach_chip, + .setup_data_interface = mxcnd_setup_data_interface, }; static int mxcnd_probe(struct platform_device *pdev) @@ -1800,7 +1809,8 @@ static int mxcnd_probe(struct platform_device *pdev) if (err < 0) return err; - this->setup_data_interface = host->devtype_data->setup_data_interface; + if (!host->devtype_data->setup_data_interface) + this->options |= NAND_KEEP_TIMINGS; if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1828,7 +1838,7 @@ static int mxcnd_probe(struct platform_device *pdev) this->ecc.bytes = host->devtype_data->eccbytes; host->eccsize = host->devtype_data->eccsize; - this->select_chip = host->devtype_data->select_chip; + this->legacy.select_chip = host->devtype_data->select_chip; this->ecc.size = 512; mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); @@ -1881,7 +1891,7 @@ static int mxcnd_probe(struct platform_device *pdev) } /* Scan the NAND device */ - this->dummy_controller.ops = &mxcnd_controller_ops; + this->legacy.dummy_controller.ops = &mxcnd_controller_ops; err = nand_scan(this, is_imx25_nfc(host) ? 4 : 1); if (err) goto escan; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c @@ -45,14 +45,10 @@ #include <linux/io.h> #include <linux/mtd/partitions.h> #include <linux/of.h> +#include <linux/gpio/consumer.h> #include "internals.h" -static int nand_get_device(struct mtd_info *mtd, int new_state); - -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops); - /* Define default oob placement schemes for large and small page devices */ static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) @@ -213,10 +209,8 @@ static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = { .free = nand_ooblayout_free_lp_hamming, }; -static int check_offs_len(struct mtd_info *mtd, - loff_t ofs, uint64_t len) +static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len) { - struct nand_chip *chip = mtd_to_nand(mtd); int ret = 0; /* Start address must align on block boundary */ @@ -235,15 +229,54 @@ static int check_offs_len(struct mtd_info *mtd, } /** + * nand_select_target() - Select a NAND target (A.K.A. die) + * @chip: NAND chip object + * @cs: the CS line to select. Note that this CS id is always from the chip + * PoV, not the controller one + * + * Select a NAND target so that further operations executed on @chip go to the + * selected NAND target. + */ +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. + */ + if (WARN_ON(cs > chip->numchips)) + return; + + chip->cur_cs = cs; + + if (chip->legacy.select_chip) + chip->legacy.select_chip(chip, cs); +} +EXPORT_SYMBOL_GPL(nand_select_target); + +/** + * nand_deselect_target() - Deselect the currently selected target + * @chip: NAND chip object + * + * Deselect the currently selected NAND target. The result of operations + * executed on @chip after the target has been deselected is undefined. + */ +void nand_deselect_target(struct nand_chip *chip) +{ + if (chip->legacy.select_chip) + chip->legacy.select_chip(chip, -1); + + chip->cur_cs = -1; +} +EXPORT_SYMBOL_GPL(nand_deselect_target); + +/** * nand_release_device - [GENERIC] release chip - * @mtd: MTD device structure + * @chip: NAND chip object * * Release chip lock and wake up anyone waiting on the device. */ -static void nand_release_device(struct mtd_info *mtd) +static void nand_release_device(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); - /* Release the controller and the chip */ spin_lock(&chip->controller->lock); chip->controller->active = NULL; @@ -289,6 +322,197 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs) return 0; } +static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs) +{ + if (chip->legacy.block_bad) + return chip->legacy.block_bad(chip, ofs); + + return nand_block_bad(chip, ofs); +} + +/** + * panic_nand_get_device - [GENERIC] Get chip for selected access + * @chip: the nand chip descriptor + * @new_state: the state which is requested + * + * Used when in panic, no locks are taken. + */ +static void panic_nand_get_device(struct nand_chip *chip, int new_state) +{ + /* Hardware controller shared among independent devices */ + chip->controller->active = chip; + chip->state = new_state; +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @chip: NAND chip structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +static int +nand_get_device(struct nand_chip *chip, int new_state) +{ + spinlock_t *lock = &chip->controller->lock; + wait_queue_head_t *wq = &chip->controller->wq; + DECLARE_WAITQUEUE(wait, current); +retry: + spin_lock(lock); + + /* Hardware controller shared among independent devices */ + if (!chip->controller->active) + chip->controller->active = chip; + + if (chip->controller->active == chip && chip->state == FL_READY) { + chip->state = new_state; + spin_unlock(lock); + return 0; + } + if (new_state == FL_PM_SUSPENDED) { + if (chip->controller->active->state == FL_PM_SUSPENDED) { + chip->state = FL_PM_SUSPENDED; + spin_unlock(lock); + return 0; + } + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); + goto retry; +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @chip: NAND chip object + * + * Check, if the device is write protected. The function expects, that the + * device is already selected. + */ +static int nand_check_wp(struct nand_chip *chip) +{ + u8 status; + int ret; + + /* Broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + + /* Check the WP bit */ + ret = nand_status_op(chip, &status); + if (ret) + return ret; + + return status & NAND_STATUS_WP ? 0 : 1; +} + +/** + * nand_fill_oob - [INTERN] Transfer client buffer to oob + * @oob: oob data buffer + * @len: oob data write length + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + /* + * Initialise to all 0xFF, to avoid the possibility of left over OOB + * data from a previous OOB read. + */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: + ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi, + ops->ooboffs, len); + BUG_ON(ret); + return oob + len; + + default: + BUG(); + } + return NULL; +} + +/** + * nand_do_write_oob - [MTD Interface] NAND write out-of-band + * @chip: NAND chip object + * @to: offset to write to + * @ops: oob operation description structure + * + * NAND write out-of-band. + */ +static int nand_do_write_oob(struct nand_chip *chip, loff_t to, + struct mtd_oob_ops *ops) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int chipnr, page, status, len; + + pr_debug("%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int)to, (int)ops->ooblen); + + len = mtd_oobavail(mtd, ops); + + /* Do not allow write past end of page */ + if ((ops->ooboffs + ops->ooblen) > len) { + pr_debug("%s: attempt to write past end of page\n", + __func__); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + + /* + * Reset the chip. Some chips (like the Toshiba TC5832DC found in one + * of my DiskOnChip 2000 test units) will clear the whole data page too + * if we don't do this. I have no clue why, but I seem to have 'fixed' + * it in the doc2000 driver in August 1999. dwmw2. + */ + nand_reset(chip, chipnr); + + nand_select_target(chip, chipnr); + + /* Shift to get page */ + page = (int)(to >> chip->page_shift); + + /* Check, if it is write protected */ + if (nand_check_wp(chip)) { + nand_deselect_target(chip); + return -EROFS; + } + + /* Invalidate the page cache, if we write to the cached page */ + if (page == chip->pagebuf) + chip->pagebuf = -1; + + nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); + + if (ops->mode == MTD_OPS_RAW) + status = chip->ecc.write_oob_raw(chip, page & chip->pagemask); + else + status = chip->ecc.write_oob(chip, page & chip->pagemask); + + nand_deselect_target(chip); + + if (status) + return status; + + ops->oobretlen = ops->ooblen; + + return 0; +} + /** * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker * @chip: NAND chip object @@ -320,7 +544,7 @@ static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs) if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) ofs += mtd->erasesize - mtd->writesize; do { - res = nand_do_write_oob(mtd, ofs, &ops); + res = nand_do_write_oob(chip, ofs, &ops); if (!ret) ret = res; @@ -344,17 +568,9 @@ int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs) return nand_default_block_markbad(chip, ofs); } -static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs) -{ - if (chip->legacy.block_bad) - return chip->legacy.block_bad(chip, ofs); - - return nand_block_bad(chip, ofs); -} - /** * nand_block_markbad_lowlevel - mark a block bad - * @mtd: MTD device structure + * @chip: NAND chip object * @ofs: offset from device start * * This function performs the generic NAND bad block marking steps (i.e., bad @@ -371,9 +587,9 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs) * Note that we retain the first error encountered in (2) or (3), finish the * procedures, and dump the error in the end. */ -static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) +static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs) { - struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); int res, ret = 0; if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { @@ -386,9 +602,9 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) nand_erase_nand(chip, &einfo, 0); /* Write bad block marker to OOB */ - nand_get_device(mtd, FL_WRITING); + nand_get_device(chip, FL_WRITING); ret = nand_markbad_bbm(chip, ofs); - nand_release_device(mtd); + nand_release_device(chip); } /* Mark block bad in BBT */ @@ -405,31 +621,6 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) } /** - * nand_check_wp - [GENERIC] check if the chip is write protected - * @mtd: MTD device structure - * - * Check, if the device is write protected. The function expects, that the - * device is already selected. - */ -static int nand_check_wp(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u8 status; - int ret; - - /* Broken xD cards report WP despite being writable */ - if (chip->options & NAND_BROKEN_XD) - return 0; - - /* Check the WP bit */ - ret = nand_status_op(chip, &status); - if (ret) - return ret; - - return status & NAND_STATUS_WP ? 0 : 1; -} - -/** * nand_block_isreserved - [GENERIC] Check if a block is marked reserved. * @mtd: MTD device structure * @ofs: offset from device start @@ -448,17 +639,15 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) /** * nand_block_checkbad - [GENERIC] Check if a block is marked bad - * @mtd: MTD device structure + * @chip: NAND chip object * @ofs: offset from device start * @allowbbt: 1, if its allowed to access the bbt area * * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */ -static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt) +static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt) { - struct nand_chip *chip = mtd_to_nand(mtd); - /* Return info from the table */ if (chip->bbt) return nand_isbad_bbt(chip, ofs, allowbbt); @@ -489,7 +678,7 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) u8 status = 0; int ret; - if (!chip->exec_op) + if (!nand_has_exec_op(chip)) return -ENOTSUPP; /* Wait tWB before polling the STATUS reg. */ @@ -532,61 +721,34 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) EXPORT_SYMBOL_GPL(nand_soft_waitrdy); /** - * panic_nand_get_device - [GENERIC] Get chip for selected access - * @chip: the nand chip descriptor - * @mtd: MTD device structure - * @new_state: the state which is requested + * nand_gpio_waitrdy - Poll R/B GPIO pin until ready + * @chip: NAND chip structure + * @gpiod: GPIO descriptor of R/B pin + * @timeout_ms: Timeout in ms * - * Used when in panic, no locks are taken. - */ -static void panic_nand_get_device(struct nand_chip *chip, - struct mtd_info *mtd, int new_state) -{ - /* Hardware controller shared among independent devices */ - chip->controller->active = chip; - chip->state = new_state; -} - -/** - * nand_get_device - [GENERIC] Get chip for selected access - * @mtd: MTD device structure - * @new_state: the state which is requested + * Poll the R/B GPIO pin until it becomes ready. If that does not happen + * whitin the specified timeout, -ETIMEDOUT is returned. * - * Get the device and lock it for exclusive access + * This helper is intended to be used when the controller has access to the + * NAND R/B pin over GPIO. + * + * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise. */ -static int -nand_get_device(struct mtd_info *mtd, int new_state) +int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod, + unsigned long timeout_ms) { - struct nand_chip *chip = mtd_to_nand(mtd); - spinlock_t *lock = &chip->controller->lock; - wait_queue_head_t *wq = &chip->controller->wq; - DECLARE_WAITQUEUE(wait, current); -retry: - spin_lock(lock); + /* Wait until R/B pin indicates chip is ready or timeout occurs */ + timeout_ms = jiffies + msecs_to_jiffies(timeout_ms); + do { + if (gpiod_get_value_cansleep(gpiod)) + return 0; - /* Hardware controller shared among independent devices */ - if (!chip->controller->active) - chip->controller->active = chip; + cond_resched(); + } while (time_before(jiffies, timeout_ms)); - if (chip->controller->active == chip && chip->state == FL_READY) { - chip->state = new_state; - spin_unlock(lock); - return 0; - } - if (new_state == FL_PM_SUSPENDED) { - if (chip->controller->active->state == FL_PM_SUSPENDED) { - chip->state = FL_PM_SUSPENDED; - spin_unlock(lock); - return 0; - } - } - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(wq, &wait); - spin_unlock(lock); - schedule(); - remove_wait_queue(wq, &wait); - goto retry; -} + return gpiod_get_value_cansleep(gpiod) ? 0 : -ETIMEDOUT; +}; +EXPORT_SYMBOL_GPL(nand_gpio_waitrdy); /** * panic_nand_wait - [GENERIC] wait until the command is done @@ -645,7 +807,7 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) { int ret; - if (!chip->setup_data_interface) + if (!nand_has_setup_data_iface(chip)) return 0; /* @@ -663,7 +825,8 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) */ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); - ret = chip->setup_data_interface(chip, chipnr, &chip->data_interface); + ret = chip->controller->ops->setup_data_interface(chip, chipnr, + &chip->data_interface); if (ret) pr_err("Failed to configure data interface to SDR timing mode 0\n"); @@ -690,21 +853,22 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) }; int ret; - if (!chip->setup_data_interface) + if (!nand_has_setup_data_iface(chip)) return 0; /* Change the mode on the chip side (if supported by the NAND chip) */ if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) { - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, tmode_param); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) return ret; } /* Change the mode on the controller side */ - ret = chip->setup_data_interface(chip, chipnr, &chip->data_interface); + ret = chip->controller->ops->setup_data_interface(chip, chipnr, + &chip->data_interface); if (ret) return ret; @@ -713,10 +877,10 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) return 0; memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, tmode_param); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) goto err_reset_chip; @@ -734,9 +898,9 @@ err_reset_chip: * timing mode. */ nand_reset_data_interface(chip, chipnr); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); nand_reset_op(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } @@ -759,7 +923,7 @@ static int nand_init_data_interface(struct nand_chip *chip) { int modes, mode, ret; - if (!chip->setup_data_interface) + if (!nand_has_setup_data_iface(chip)) return 0; /* @@ -785,7 +949,7 @@ static int nand_init_data_interface(struct nand_chip *chip) * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the * controller supports the requested timings. */ - ret = chip->setup_data_interface(chip, + ret = chip->controller->ops->setup_data_interface(chip, NAND_DATA_IFACE_CHECK_ONLY, &chip->data_interface); if (!ret) { @@ -866,7 +1030,7 @@ static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page, PSEC_TO_NSEC(sdr->tRR_min)), NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; /* Drop the DATA_IN instruction if len is set to 0. */ @@ -909,7 +1073,7 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, PSEC_TO_NSEC(sdr->tRR_min)), NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; /* Drop the DATA_IN instruction if len is set to 0. */ @@ -955,7 +1119,7 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { if (mtd->writesize > 512) return nand_lp_exec_read_page_op(chip, page, offset_in_page, buf, @@ -994,7 +1158,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, if (len && !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1004,7 +1168,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, PSEC_TO_NSEC(sdr->tRR_min)), NAND_OP_8BIT_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); /* Drop the DATA_IN instruction if len is set to 0. */ if (!len) @@ -1049,7 +1213,7 @@ int nand_change_read_column_op(struct nand_chip *chip, if (mtd->writesize <= 512) return -ENOTSUPP; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); u8 addrs[2] = {}; @@ -1060,7 +1224,7 @@ int nand_change_read_column_op(struct nand_chip *chip, PSEC_TO_NSEC(sdr->tCCS_min)), NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; ret = nand_fill_column_cycles(chip, addrs, offset_in_page); @@ -1108,7 +1272,7 @@ int nand_read_oob_op(struct nand_chip *chip, unsigned int page, if (offset_in_oob + len > mtd->oobsize) return -EINVAL; - if (chip->exec_op) + if (nand_has_exec_op(chip)) return nand_read_page_op(chip, page, mtd->writesize + offset_in_oob, buf, len); @@ -1142,7 +1306,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); int ret; u8 status; @@ -1221,7 +1385,7 @@ int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; - if (chip->exec_op) + if (nand_has_exec_op(chip)) return nand_exec_prog_page_op(chip, page, offset_in_page, buf, len, false); @@ -1248,7 +1412,7 @@ int nand_prog_page_end_op(struct nand_chip *chip) int ret; u8 status; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1256,7 +1420,7 @@ int nand_prog_page_end_op(struct nand_chip *chip) PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); ret = nand_exec_op(chip, &op); if (ret) @@ -1307,7 +1471,7 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, len, true); } else { @@ -1355,7 +1519,7 @@ int nand_change_write_column_op(struct nand_chip *chip, if (mtd->writesize <= 512) return -ENOTSUPP; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); u8 addrs[2]; @@ -1364,7 +1528,7 @@ int nand_change_write_column_op(struct nand_chip *chip, NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)), NAND_OP_DATA_OUT(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; ret = nand_fill_column_cycles(chip, addrs, offset_in_page); @@ -1410,7 +1574,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, if (len && !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1418,7 +1582,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), NAND_OP_8BIT_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); /* Drop the DATA_IN instruction if len is set to 0. */ if (!len) @@ -1449,7 +1613,7 @@ EXPORT_SYMBOL_GPL(nand_readid_op); */ int nand_status_op(struct nand_chip *chip, u8 *status) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1457,7 +1621,7 @@ int nand_status_op(struct nand_chip *chip, u8 *status) PSEC_TO_NSEC(sdr->tADL_min)), NAND_OP_8BIT_DATA_IN(1, status, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); if (!status) op.ninstrs--; @@ -1486,11 +1650,11 @@ EXPORT_SYMBOL_GPL(nand_status_op); */ int nand_exit_status_op(struct nand_chip *chip) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_READ0, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1518,7 +1682,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) int ret; u8 status; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); u8 addrs[3] = { page, page >> 8, page >> 16 }; @@ -1529,7 +1693,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) PSEC_TO_MSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); if (chip->options & NAND_ROW_ADDR_3) instrs[1].ctx.addr.naddrs++; @@ -1577,7 +1741,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, const u8 *params = data; int i, ret; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1587,7 +1751,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1624,7 +1788,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature, u8 *params = data; int i; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1635,7 +1799,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature, NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN, data, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1650,12 +1814,12 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature, static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms, unsigned int delay_ns) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_WAIT_RDY(PSEC_TO_MSEC(timeout_ms), PSEC_TO_NSEC(delay_ns)), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1681,14 +1845,14 @@ static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms, */ int nand_reset_op(struct nand_chip *chip) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1718,11 +1882,11 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, if (!len || !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); instrs[0].ctx.data.force_8bit = force_8bit; @@ -1762,11 +1926,11 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf, if (!len || !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_DATA_OUT(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); instrs[0].ctx.data.force_8bit = force_8bit; @@ -2224,11 +2388,12 @@ int nand_reset(struct nand_chip *chip, int chipnr) /* * The CS line has to be released before we can apply the new NAND - * interface settings, hence this weird ->select_chip() dance. + * interface settings, hence this weird nand_select_target() + * nand_deselect_target() dance. */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); ret = nand_reset_op(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) return ret; @@ -2924,15 +3089,15 @@ static int nand_read_page_syndrome(struct nand_chip *chip, uint8_t *buf, /** * nand_transfer_oob - [INTERN] Transfer oob to client buffer - * @mtd: mtd info structure + * @chip: NAND chip object * @oob: oob destination address * @ops: oob ops structure * @len: size of oob to transfer */ -static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { - struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); int ret; switch (ops->mode) { @@ -2989,17 +3154,17 @@ static void nand_wait_readrdy(struct nand_chip *chip) /** * nand_do_read_ops - [INTERN] Read data with ECC - * @mtd: MTD device structure + * @chip: NAND chip object * @from: offset to read from * @ops: oob ops structure * * Internal function. Called with chip held. */ -static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, +static int nand_do_read_ops(struct nand_chip *chip, loff_t from, struct mtd_oob_ops *ops) { int chipnr, page, realpage, col, bytes, aligned, oob_required; - struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; @@ -3012,7 +3177,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, bool ecc_fail = false; chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; @@ -3087,8 +3252,8 @@ read_retry: int toread = min(oobreadlen, max_oobsize); if (toread) { - oob = nand_transfer_oob(mtd, - oob, ops, toread); + oob = nand_transfer_oob(chip, oob, ops, + toread); oobreadlen -= toread; } } @@ -3143,11 +3308,11 @@ read_retry: /* Check, if we cross a chip boundary */ if (!page) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } - chip->select_chip(chip, -1); + nand_deselect_target(chip); ops->retlen = ops->len - (size_t) readlen; if (oob) @@ -3318,18 +3483,18 @@ static int nand_write_oob_syndrome(struct nand_chip *chip, int page) /** * nand_do_read_oob - [INTERN] NAND read out-of-band - * @mtd: MTD device structure + * @chip: NAND chip object * @from: offset to read from * @ops: oob operations description structure * * NAND read out-of-band data from the spare area. */ -static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, +static int nand_do_read_oob(struct nand_chip *chip, loff_t from, struct mtd_oob_ops *ops) { + struct mtd_info *mtd = nand_to_mtd(chip); unsigned int max_bitflips = 0; int page, realpage, chipnr; - struct nand_chip *chip = mtd_to_nand(mtd); struct mtd_ecc_stats stats; int readlen = ops->ooblen; int len; @@ -3344,7 +3509,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, len = mtd_oobavail(mtd, ops); chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Shift to get page */ realpage = (int)(from >> chip->page_shift); @@ -3360,7 +3525,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, break; len = min(len, readlen); - buf = nand_transfer_oob(mtd, buf, ops, len); + buf = nand_transfer_oob(chip, buf, ops, len); nand_wait_readrdy(chip); @@ -3377,11 +3542,11 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, /* Check, if we cross a chip boundary */ if (!page) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } - chip->select_chip(chip, -1); + nand_deselect_target(chip); ops->oobretlen = ops->ooblen - readlen; @@ -3405,6 +3570,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + struct nand_chip *chip = mtd_to_nand(mtd); int ret; ops->retlen = 0; @@ -3414,14 +3580,14 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, ops->mode != MTD_OPS_RAW) return -ENOTSUPP; - nand_get_device(mtd, FL_READING); + nand_get_device(chip, FL_READING); if (!ops->datbuf) - ret = nand_do_read_oob(mtd, from, ops); + ret = nand_do_read_oob(chip, from, ops); else - ret = nand_do_read_ops(mtd, from, ops); + ret = nand_do_read_ops(chip, from, ops); - nand_release_device(mtd); + nand_release_device(chip); return ret; } @@ -3749,7 +3915,6 @@ static int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf, /** * nand_write_page - write one page - * @mtd: MTD device structure * @chip: NAND chip descriptor * @offset: address offset within the page * @data_len: length of actual data to be written @@ -3758,10 +3923,11 @@ static int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf, * @page: page number to write * @raw: use _raw version of write_page */ -static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offset, int data_len, const uint8_t *buf, - int oob_required, int page, int raw) +static int nand_write_page(struct nand_chip *chip, uint32_t offset, + int data_len, const uint8_t *buf, int oob_required, + int page, int raw) { + struct mtd_info *mtd = nand_to_mtd(chip); int status, subpage; if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && @@ -3785,59 +3951,21 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, return 0; } -/** - * nand_fill_oob - [INTERN] Transfer client buffer to oob - * @mtd: MTD device structure - * @oob: oob data buffer - * @len: oob data write length - * @ops: oob ops structure - */ -static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, - struct mtd_oob_ops *ops) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - /* - * Initialise to all 0xFF, to avoid the possibility of left over OOB - * data from a previous OOB read. - */ - memset(chip->oob_poi, 0xff, mtd->oobsize); - - switch (ops->mode) { - - case MTD_OPS_PLACE_OOB: - case MTD_OPS_RAW: - memcpy(chip->oob_poi + ops->ooboffs, oob, len); - return oob + len; - - case MTD_OPS_AUTO_OOB: - ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi, - ops->ooboffs, len); - BUG_ON(ret); - return oob + len; - - default: - BUG(); - } - return NULL; -} - #define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) /** * nand_do_write_ops - [INTERN] NAND write with ECC - * @mtd: MTD device structure + * @chip: NAND chip object * @to: offset to write to * @ops: oob operations description structure * * NAND write with ECC. */ -static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, +static int nand_do_write_ops(struct nand_chip *chip, loff_t to, struct mtd_oob_ops *ops) { + struct mtd_info *mtd = nand_to_mtd(chip); int chipnr, realpage, page, column; - struct nand_chip *chip = mtd_to_nand(mtd); uint32_t writelen = ops->len; uint32_t oobwritelen = ops->ooblen; @@ -3862,10 +3990,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, column = to & (mtd->writesize - 1); chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { + if (nand_check_wp(chip)) { ret = -EIO; goto err_out; } @@ -3913,14 +4041,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (unlikely(oob)) { size_t len = min(oobwritelen, oobmaxlen); - oob = nand_fill_oob(mtd, oob, len, ops); + oob = nand_fill_oob(chip, oob, len, ops); oobwritelen -= len; } else { /* We still need to erase leftover OOB data */ memset(chip->oob_poi, 0xff, mtd->oobsize); } - ret = nand_write_page(mtd, chip, column, bytes, wbuf, + ret = nand_write_page(chip, column, bytes, wbuf, oob_required, page, (ops->mode == MTD_OPS_RAW)); if (ret) @@ -3938,8 +4066,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, /* Check, if we cross a chip boundary */ if (!page) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } @@ -3948,7 +4076,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ops->oobretlen = ops->ooblen; err_out: - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } @@ -3972,9 +4100,9 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, int ret; /* Grab the device */ - panic_nand_get_device(chip, mtd, FL_WRITING); + panic_nand_get_device(chip, FL_WRITING); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Wait for the device to get ready */ panic_nand_wait(chip, 400); @@ -3984,81 +4112,13 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, ops.datbuf = (uint8_t *)buf; ops.mode = MTD_OPS_PLACE_OOB; - ret = nand_do_write_ops(mtd, to, &ops); + ret = nand_do_write_ops(chip, to, &ops); *retlen = ops.retlen; return ret; } /** - * nand_do_write_oob - [MTD Interface] NAND write out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure - * - * NAND write out-of-band. - */ -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int chipnr, page, status, len; - struct nand_chip *chip = mtd_to_nand(mtd); - - pr_debug("%s: to = 0x%08x, len = %i\n", - __func__, (unsigned int)to, (int)ops->ooblen); - - len = mtd_oobavail(mtd, ops); - - /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->ooblen) > len) { - pr_debug("%s: attempt to write past end of page\n", - __func__); - return -EINVAL; - } - - chipnr = (int)(to >> chip->chip_shift); - - /* - * Reset the chip. Some chips (like the Toshiba TC5832DC found in one - * of my DiskOnChip 2000 test units) will clear the whole data page too - * if we don't do this. I have no clue why, but I seem to have 'fixed' - * it in the doc2000 driver in August 1999. dwmw2. - */ - nand_reset(chip, chipnr); - - chip->select_chip(chip, chipnr); - - /* Shift to get page */ - page = (int)(to >> chip->page_shift); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - chip->select_chip(chip, -1); - return -EROFS; - } - - /* Invalidate the page cache, if we write to the cached page */ - if (page == chip->pagebuf) - chip->pagebuf = -1; - - nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); - - if (ops->mode == MTD_OPS_RAW) - status = chip->ecc.write_oob_raw(chip, page & chip->pagemask); - else - status = chip->ecc.write_oob(chip, page & chip->pagemask); - - chip->select_chip(chip, -1); - - if (status) - return status; - - ops->oobretlen = ops->ooblen; - - return 0; -} - -/** * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band * @mtd: MTD device structure * @to: offset to write to @@ -4067,11 +4127,12 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { + struct nand_chip *chip = mtd_to_nand(mtd); int ret = -ENOTSUPP; ops->retlen = 0; - nand_get_device(mtd, FL_WRITING); + nand_get_device(chip, FL_WRITING); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -4084,12 +4145,12 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, } if (!ops->datbuf) - ret = nand_do_write_oob(mtd, to, ops); + ret = nand_do_write_oob(chip, to, ops); else - ret = nand_do_write_ops(mtd, to, ops); + ret = nand_do_write_ops(chip, to, ops); out: - nand_release_device(mtd); + nand_release_device(chip); return ret; } @@ -4133,7 +4194,6 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, int allowbbt) { - struct mtd_info *mtd = nand_to_mtd(chip); int page, status, pages_per_block, ret, chipnr; loff_t len; @@ -4141,11 +4201,11 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, __func__, (unsigned long long)instr->addr, (unsigned long long)instr->len); - if (check_offs_len(mtd, instr->addr, instr->len)) + if (check_offs_len(chip, instr->addr, instr->len)) return -EINVAL; /* Grab the lock and see if the device is available */ - nand_get_device(mtd, FL_ERASING); + nand_get_device(chip, FL_ERASING); /* Shift to get first page */ page = (int)(instr->addr >> chip->page_shift); @@ -4155,10 +4215,10 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); /* Select the NAND device */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { + if (nand_check_wp(chip)) { pr_debug("%s: device is write protected!\n", __func__); ret = -EIO; @@ -4170,7 +4230,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, while (len) { /* Check if we have a bad block, we do not erase bad blocks! */ - if (nand_block_checkbad(mtd, ((loff_t) page) << + if (nand_block_checkbad(chip, ((loff_t) page) << chip->page_shift, allowbbt)) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", __func__, page); @@ -4209,8 +4269,8 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, /* Check, if we cross a chip boundary */ if (len && !(page & chip->pagemask)) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } @@ -4218,8 +4278,8 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, erase_exit: /* Deselect and wake up anyone waiting on the device */ - chip->select_chip(chip, -1); - nand_release_device(mtd); + nand_deselect_target(chip); + nand_release_device(chip); /* Return more or less happy */ return ret; @@ -4233,12 +4293,14 @@ erase_exit: */ static void nand_sync(struct mtd_info *mtd) { + struct nand_chip *chip = mtd_to_nand(mtd); + pr_debug("%s: called\n", __func__); /* Grab the lock and see if the device is available */ - nand_get_device(mtd, FL_SYNCING); + nand_get_device(chip, FL_SYNCING); /* Release it and go back */ - nand_release_device(mtd); + nand_release_device(chip); } /** @@ -4253,13 +4315,13 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) int ret; /* Select the NAND device */ - nand_get_device(mtd, FL_READING); - chip->select_chip(chip, chipnr); + nand_get_device(chip, FL_READING); + nand_select_target(chip, chipnr); - ret = nand_block_checkbad(mtd, offs, 0); + ret = nand_block_checkbad(chip, offs, 0); - chip->select_chip(chip, -1); - nand_release_device(mtd); + nand_deselect_target(chip); + nand_release_device(chip); return ret; } @@ -4281,7 +4343,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } - return nand_block_markbad_lowlevel(mtd, ofs); + return nand_block_markbad_lowlevel(mtd_to_nand(mtd), ofs); } /** @@ -4326,7 +4388,7 @@ static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) */ static int nand_suspend(struct mtd_info *mtd) { - return nand_get_device(mtd, FL_PM_SUSPENDED); + return nand_get_device(mtd_to_nand(mtd), FL_PM_SUSPENDED); } /** @@ -4338,7 +4400,7 @@ static void nand_resume(struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); if (chip->state == FL_PM_SUSPENDED) - nand_release_device(mtd); + nand_release_device(chip); else pr_err("%s called for a chip which is not in suspended state\n", __func__); @@ -4351,19 +4413,20 @@ static void nand_resume(struct mtd_info *mtd) */ static void nand_shutdown(struct mtd_info *mtd) { - nand_get_device(mtd, FL_PM_SUSPENDED); + nand_get_device(mtd_to_nand(mtd), FL_PM_SUSPENDED); } /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { - nand_legacy_set_defaults(chip); - + /* If no controller is provided, use the dummy, legacy one. */ if (!chip->controller) { - chip->controller = &chip->dummy_controller; + chip->controller = &chip->legacy.dummy_controller; nand_controller_init(chip->controller); } + nand_legacy_set_defaults(chip); + if (!chip->buf_align) chip->buf_align = 1; } @@ -4627,7 +4690,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) return ret; /* Select the device */ - chip->select_chip(chip, 0); + nand_select_target(chip, 0); /* Send the command for reading device ID */ ret = nand_readid_op(chip, 0, id_data, 2); @@ -4952,6 +5015,9 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, unsigned int i; int ret; + /* Assume all dies are deselected when we enter nand_scan_ident(). */ + chip->cur_cs = -1; + /* Enforce the right timings for reset/detection */ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); @@ -4962,31 +5028,32 @@ 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); - if (chip->exec_op && !chip->select_chip) { - pr_err("->select_chip() is mandatory when implementing ->exec_op()\n"); - return -EINVAL; - } + /* + * 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); ret = nand_legacy_check_hooks(chip); if (ret) return ret; - /* Set the default functions */ - nand_set_defaults(chip); - /* Read the flash type */ ret = nand_detect(chip, table); if (ret) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) pr_warn("No NAND device found\n"); - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } nand_maf_id = chip->id.data[0]; nand_dev_id = chip->id.data[1]; - chip->select_chip(chip, -1); + nand_deselect_target(chip); /* Check for a chip array */ for (i = 1; i < maxchips; i++) { @@ -4995,15 +5062,15 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, /* See comment in nand_get_flash_type for reset */ nand_reset(chip, i); - chip->select_chip(chip, i); + nand_select_target(chip, i); /* Send the command for reading device ID */ nand_readid_op(chip, 0, id, sizeof(id)); /* Read manufacturer and device IDs */ if (nand_maf_id != id[0] || nand_dev_id != id[1]) { - chip->select_chip(chip, -1); + nand_deselect_target(chip); break; } - chip->select_chip(chip, -1); + nand_deselect_target(chip); } if (i > 1) pr_info("%d chips detected\n", i); @@ -5021,9 +5088,9 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) kfree(chip->parameters.onfi); } -static int nand_set_ecc_soft_ops(struct mtd_info *mtd) +static int nand_set_ecc_soft_ops(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; if (WARN_ON(ecc->mode != NAND_ECC_SOFT)) @@ -5379,9 +5446,9 @@ EXPORT_SYMBOL_GPL(nand_ecc_choose_conf); * Requirement (2) ensures we can correct even when all bitflips are clumped * in the same sector. */ -static bool nand_ecc_strength_good(struct mtd_info *mtd) +static bool nand_ecc_strength_good(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; int corr, ds_corr; @@ -5429,9 +5496,9 @@ static int nand_scan_tail(struct nand_chip *chip) * to explictly select the relevant die when interacting with the NAND * chip. */ - chip->select_chip(chip, 0); + nand_select_target(chip, 0); ret = nand_manufacturer_init(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) goto err_free_buf; @@ -5546,7 +5613,7 @@ static int nand_scan_tail(struct nand_chip *chip) ecc->algo = NAND_ECC_HAMMING; case NAND_ECC_SOFT: - ret = nand_set_ecc_soft_ops(mtd); + ret = nand_set_ecc_soft_ops(chip); if (ret) { ret = -EINVAL; goto err_nand_manuf_cleanup; @@ -5631,7 +5698,7 @@ static int nand_scan_tail(struct nand_chip *chip) mtd->oobavail = ret; /* ECC sanity check: warn if it's too weak */ - if (!nand_ecc_strength_good(mtd)) + if (!nand_ecc_strength_good(chip)) pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", mtd->name); diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c @@ -77,8 +77,6 @@ #define BBT_ENTRY_MASK 0x03 #define BBT_ENTRY_SHIFT 2 -static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); - static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) { uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; @@ -160,7 +158,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td) /** * read_bbt - [GENERIC] Read the bad block table starting from page - * @mtd: MTD device structure + * @chip: NAND chip object * @buf: temporary buffer * @page: the starting page * @num: the number of bbt descriptors to read @@ -169,11 +167,11 @@ static u32 add_marker_len(struct nand_bbt_descr *td) * * Read the bad block table starting from page. */ -static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, - struct nand_bbt_descr *td, int offs) +static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num, + struct nand_bbt_descr *td, int offs) { + struct mtd_info *mtd = nand_to_mtd(this); int res, ret = 0, i, j, act = 0; - struct nand_chip *this = mtd_to_nand(mtd); size_t retlen, len, totlen; loff_t from; int bits = td->options & NAND_BBT_NRBITS_MSK; @@ -253,7 +251,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, /** * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page - * @mtd: MTD device structure + * @this: NAND chip object * @buf: temporary buffer * @td: descriptor for the bad block table * @chip: read the table for a specific chip, -1 read all chips; applies only if @@ -262,16 +260,17 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, * Read the bad block table for all chips starting at a given page. We assume * that the bbt bits are in consecutive order. */ -static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +static int read_abs_bbt(struct nand_chip *this, uint8_t *buf, + struct nand_bbt_descr *td, int chip) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); int res = 0, i; if (td->options & NAND_BBT_PERCHIP) { int offs = 0; for (i = 0; i < this->numchips; i++) { if (chip == -1 || chip == i) - res = read_bbt(mtd, buf, td->pages[i], + res = read_bbt(this, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, td, offs); if (res) @@ -279,7 +278,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc offs += this->chipsize >> this->bbt_erase_shift; } } else { - res = read_bbt(mtd, buf, td->pages[0], + res = read_bbt(this, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, td, 0); if (res) return res; @@ -288,9 +287,10 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc } /* BBT marker is in the first page, no OOB */ -static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - struct nand_bbt_descr *td) +static int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs, + struct nand_bbt_descr *td) { + struct mtd_info *mtd = nand_to_mtd(this); size_t retlen; size_t len; @@ -303,7 +303,7 @@ static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, /** * scan_read_oob - [GENERIC] Scan data+OOB region to buffer - * @mtd: MTD device structure + * @this: NAND chip object * @buf: temporary buffer * @offs: offset at which to scan * @len: length of data region to read @@ -312,9 +312,10 @@ static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" * ECC condition (error or bitflip). May quit on the first (non-ECC) error. */ -static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +static int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs, size_t len) { + struct mtd_info *mtd = nand_to_mtd(this); struct mtd_oob_ops ops; int res, ret = 0; @@ -342,19 +343,20 @@ static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, return ret; } -static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - size_t len, struct nand_bbt_descr *td) +static int scan_read(struct nand_chip *this, uint8_t *buf, loff_t offs, + size_t len, struct nand_bbt_descr *td) { if (td->options & NAND_BBT_NO_OOB) - return scan_read_data(mtd, buf, offs, td); + return scan_read_data(this, buf, offs, td); else - return scan_read_oob(mtd, buf, offs, len); + return scan_read_oob(this, buf, offs, len); } /* Scan write data with oob to flash */ -static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, +static int scan_write_bbt(struct nand_chip *this, loff_t offs, size_t len, uint8_t *buf, uint8_t *oob) { + struct mtd_info *mtd = nand_to_mtd(this); struct mtd_oob_ops ops; ops.mode = MTD_OPS_PLACE_OOB; @@ -367,8 +369,9 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, return mtd_write_oob(mtd, offs, &ops); } -static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) +static u32 bbt_get_ver_offs(struct nand_chip *this, struct nand_bbt_descr *td) { + struct mtd_info *mtd = nand_to_mtd(this); u32 ver_offs = td->veroffs; if (!(td->options & NAND_BBT_NO_OOB)) @@ -378,7 +381,7 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) /** * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page - * @mtd: MTD device structure + * @this: NAND chip object * @buf: temporary buffer * @td: descriptor for the bad block table * @md: descriptor for the bad block table mirror @@ -386,34 +389,35 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) * Read the bad block table(s) for all chips starting at a given page. We * assume that the bbt bits are in consecutive order. */ -static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, +static void read_abs_bbts(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, - mtd->writesize, td); - td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; + scan_read(this, buf, (loff_t)td->pages[0] << this->page_shift, + mtd->writesize, td); + td->version[0] = buf[bbt_get_ver_offs(this, td)]; pr_info("Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); } /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, - mtd->writesize, md); - md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; + scan_read(this, buf, (loff_t)md->pages[0] << this->page_shift, + mtd->writesize, md); + md->version[0] = buf[bbt_get_ver_offs(this, md)]; pr_info("Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); } } /* Scan a given block partially */ -static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, +static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd, loff_t offs, uint8_t *buf, int numpages) { + struct mtd_info *mtd = nand_to_mtd(this); struct mtd_oob_ops ops; int j, ret; @@ -443,7 +447,7 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, /** * create_bbt - [GENERIC] Create a bad block table by scanning the device - * @mtd: MTD device structure + * @this: NAND chip object * @buf: temporary buffer * @bd: descriptor for the good/bad block search pattern * @chip: create the table for a specific chip, -1 read all chips; applies only @@ -452,10 +456,10 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, * Create a bad block table by scanning the device for the given good/bad block * identify pattern. */ -static int create_bbt(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *bd, int chip) +static int create_bbt(struct nand_chip *this, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); int i, numblocks, numpages; int startblock; loff_t from; @@ -491,7 +495,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, BUG_ON(bd->options & NAND_BBT_NO_OOB); - ret = scan_block_fast(mtd, bd, from, buf, numpages); + ret = scan_block_fast(this, bd, from, buf, numpages); if (ret < 0) return ret; @@ -509,7 +513,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, /** * search_bbt - [GENERIC] scan the device for a specific bad block table - * @mtd: MTD device structure + * @this: NAND chip object * @buf: temporary buffer * @td: descriptor for the bad block table * @@ -522,9 +526,10 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, * * The bbt ident pattern resides in the oob area of the first page in a block. */ -static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +static int search_bbt(struct nand_chip *this, uint8_t *buf, + struct nand_bbt_descr *td) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); int i, chips; int startblock, block, dir; int scanlen = mtd->writesize + mtd->oobsize; @@ -561,11 +566,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr loff_t offs = (loff_t)actblock << this->bbt_erase_shift; /* Read first page */ - scan_read(mtd, buf, offs, mtd->writesize, td); + scan_read(this, buf, offs, mtd->writesize, td); if (!check_pattern(buf, scanlen, mtd->writesize, td)) { td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { - offs = bbt_get_ver_offs(mtd, td); + offs = bbt_get_ver_offs(this, td); td->version[i] = buf[offs]; } break; @@ -586,23 +591,23 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /** * search_read_bbts - [GENERIC] scan the device for bad block table(s) - * @mtd: MTD device structure + * @this: NAND chip object * @buf: temporary buffer * @td: descriptor for the bad block table * @md: descriptor for the bad block table mirror * * Search and read the bad block table(s). */ -static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, +static void search_read_bbts(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) { /* Search the primary table */ - search_bbt(mtd, buf, td); + search_bbt(this, buf, td); /* Search the mirror table */ if (md) - search_bbt(mtd, buf, md); + search_bbt(this, buf, md); } /** @@ -700,7 +705,7 @@ static void mark_bbt_block_bad(struct nand_chip *this, /** * write_bbt - [GENERIC] (Re)write the bad block table - * @mtd: MTD device structure + * @this: NAND chip object * @buf: temporary buffer * @td: descriptor for the bad block table * @md: descriptor for the bad block table mirror @@ -708,11 +713,11 @@ static void mark_bbt_block_bad(struct nand_chip *this, * * (Re)write the bad block table. */ -static int write_bbt(struct mtd_info *mtd, uint8_t *buf, +static int write_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); struct erase_info einfo; int i, res, chip = 0; int bits, page, offs, numblocks, sft, sftmsk; @@ -862,9 +867,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, continue; } - res = scan_write_bbt(mtd, to, len, buf, - td->options & NAND_BBT_NO_OOB ? NULL : - &buf[len]); + res = scan_write_bbt(this, to, len, buf, + td->options & NAND_BBT_NO_OOB ? + NULL : &buf[len]); if (res < 0) { pr_warn("nand_bbt: error while writing BBT block %d\n", res); @@ -887,22 +892,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, /** * nand_memory_bbt - [GENERIC] create a memory based bad block table - * @mtd: MTD device structure + * @this: NAND chip object * @bd: descriptor for the good/bad block search pattern * * The function creates a memory based bbt by scanning the device for * manufacturer / software marked good / bad blocks. */ -static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +static inline int nand_memory_bbt(struct nand_chip *this, + struct nand_bbt_descr *bd) { - struct nand_chip *this = mtd_to_nand(mtd); - - return create_bbt(mtd, this->data_buf, bd, -1); + return create_bbt(this, this->data_buf, bd, -1); } /** * check_create - [GENERIC] create and write bbt(s) if necessary - * @mtd: MTD device structure + * @this: the NAND device * @buf: temporary buffer * @bd: descriptor for the good/bad block search pattern * @@ -911,10 +915,10 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b * for the chip/device. Update is necessary if one of the tables is missing or * the version nr. of one table is less than the other. */ -static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +static int check_create(struct nand_chip *this, uint8_t *buf, + struct nand_bbt_descr *bd) { int i, chips, writeops, create, chipsel, res, res2; - struct nand_chip *this = mtd_to_nand(mtd); struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; struct nand_bbt_descr *rd, *rd2; @@ -971,7 +975,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc /* Create the table in memory by scanning the chip(s) */ if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) - create_bbt(mtd, buf, bd, chipsel); + create_bbt(this, buf, bd, chipsel); td->version[i] = 1; if (md) @@ -980,7 +984,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc /* Read back first? */ if (rd) { - res = read_abs_bbt(mtd, buf, rd, chipsel); + res = read_abs_bbt(this, buf, rd, chipsel); if (mtd_is_eccerr(res)) { /* Mark table as invalid */ rd->pages[i] = -1; @@ -991,7 +995,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc } /* If they weren't versioned, read both */ if (rd2) { - res2 = read_abs_bbt(mtd, buf, rd2, chipsel); + res2 = read_abs_bbt(this, buf, rd2, chipsel); if (mtd_is_eccerr(res2)) { /* Mark table as invalid */ rd2->pages[i] = -1; @@ -1013,14 +1017,14 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc /* Write the bad block table to the device? */ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, td, md, chipsel); + res = write_bbt(this, buf, td, md, chipsel); if (res < 0) return res; } /* Write the mirror bad block table to the device? */ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, md, td, chipsel); + res = write_bbt(this, buf, md, td, chipsel); if (res < 0) return res; } @@ -1029,16 +1033,71 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc } /** + * nand_update_bbt - update bad block table(s) + * @this: the NAND device + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s). + */ +static int nand_update_bbt(struct nand_chip *this, loff_t offs) +{ + struct mtd_info *mtd = nand_to_mtd(this); + int len, res = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int)(offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device? */ + if (td->options & NAND_BBT_WRITE) { + res = write_bbt(this, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device? */ + if (md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(this, buf, md, td, chipsel); + } + + out: + kfree(buf); + return res; +} + +/** * mark_bbt_regions - [GENERIC] mark the bad block table regions - * @mtd: MTD device structure + * @this: the NAND device * @td: bad block table descriptor * * The bad block table regions are marked as "bad" to prevent accidental * erasures / writes. The regions are identified by the mark 0x02. */ -static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) +static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); int i, j, chips, block, nrblocks, update; uint8_t oldval; @@ -1061,7 +1120,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); if ((oldval != BBT_BLOCK_RESERVED) && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << + nand_update_bbt(this, (loff_t)block << this->bbt_erase_shift); continue; } @@ -1083,22 +1142,22 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) * bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 1) << + nand_update_bbt(this, (loff_t)(block - 1) << this->bbt_erase_shift); } } /** * verify_bbt_descr - verify the bad block description - * @mtd: MTD device structure + * @this: the NAND device * @bd: the table to verify * * This functions performs a few sanity checks on the bad block description * table. */ -static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) +static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); u32 pattern_len; u32 bits; u32 table_size; @@ -1138,7 +1197,7 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) /** * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) - * @mtd: MTD device structure + * @this: the NAND device * @bd: descriptor for the good/bad block search pattern * * The function checks, if a bad block table(s) is/are already available. If @@ -1148,9 +1207,9 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) * The bad block table memory is allocated here. It must be freed by calling * the nand_free_bbt function. */ -static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) { - struct nand_chip *this = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(this); int len, res; uint8_t *buf; struct nand_bbt_descr *td = this->bbt_td; @@ -1170,14 +1229,14 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) * memory based bad block table. */ if (!td) { - if ((res = nand_memory_bbt(mtd, bd))) { + if ((res = nand_memory_bbt(this, bd))) { pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); goto err; } return 0; } - verify_bbt_descr(mtd, td); - verify_bbt_descr(mtd, md); + verify_bbt_descr(this, td); + verify_bbt_descr(this, md); /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); @@ -1190,20 +1249,20 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) /* Is the bbt at a given page? */ if (td->options & NAND_BBT_ABSPAGE) { - read_abs_bbts(mtd, buf, td, md); + read_abs_bbts(this, buf, td, md); } else { /* Search the bad block table using a pattern in oob */ - search_read_bbts(mtd, buf, td, md); + search_read_bbts(this, buf, td, md); } - res = check_create(mtd, buf, bd); + res = check_create(this, buf, bd); if (res) goto err; /* Prevent the bbt regions from erasing / writing */ - mark_bbt_region(mtd, td); + mark_bbt_region(this, td); if (md) - mark_bbt_region(mtd, md); + mark_bbt_region(this, md); vfree(buf); return 0; @@ -1214,61 +1273,6 @@ err: return res; } -/** - * nand_update_bbt - update bad block table(s) - * @mtd: MTD device structure - * @offs: the offset of the newly marked block - * - * The function updates the bad block table(s). - */ -static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int len, res = 0; - int chip, chipsel; - uint8_t *buf; - struct nand_bbt_descr *td = this->bbt_td; - struct nand_bbt_descr *md = this->bbt_md; - - if (!this->bbt || !td) - return -EINVAL; - - /* Allocate a temporary buffer for one eraseblock incl. oob */ - len = (1 << this->bbt_erase_shift); - len += (len >> this->page_shift) * mtd->oobsize; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) { - chip = (int)(offs >> this->chip_shift); - chipsel = chip; - } else { - chip = 0; - chipsel = -1; - } - - td->version[chip]++; - if (md) - md->version[chip]++; - - /* Write the bad block table to the device? */ - if (td->options & NAND_BBT_WRITE) { - res = write_bbt(mtd, buf, td, md, chipsel); - if (res < 0) - goto out; - } - /* Write the mirror bad block table to the device? */ - if (md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, md, td, chipsel); - } - - out: - kfree(buf); - return res; -} - /* * Define some generic bad / good block scan pattern which are used * while scanning a device for factory marked good / bad blocks. @@ -1382,7 +1386,7 @@ int nand_create_bbt(struct nand_chip *this) return ret; } - return nand_scan_bbt(nand_to_mtd(this), this->badblock_pattern); + return nand_scan_bbt(this, this->badblock_pattern); } EXPORT_SYMBOL(nand_create_bbt); @@ -1433,7 +1437,6 @@ int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt) */ int nand_markbad_bbt(struct nand_chip *this, loff_t offs) { - struct mtd_info *mtd = nand_to_mtd(this); int block, ret = 0; block = (int)(offs >> this->bbt_erase_shift); @@ -1443,7 +1446,7 @@ int nand_markbad_bbt(struct nand_chip *this, loff_t offs) /* Update flash-based bad block table */ if (this->bbt_options & NAND_BBT_USE_FLASH) - ret = nand_update_bbt(mtd, offs); + ret = nand_update_bbt(this, offs); return ret; } diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c @@ -80,11 +80,11 @@ static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_CMD(cmd, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -98,12 +98,12 @@ static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) { u16 column = ((u16)addr << 8) | addr; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_ADDR(1, &addr, 0), NAND_OP_8BIT_DATA_OUT(1, &val, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } diff --git a/drivers/mtd/nand/raw/nand_jedec.c b/drivers/mtd/nand/raw/nand_jedec.c @@ -107,6 +107,8 @@ int nand_jedec_detect(struct nand_chip *chip) pr_warn("Invalid codeword size\n"); } + ret = 1; + free_jedec_param_page: kfree(p); return ret; diff --git a/drivers/mtd/nand/raw/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c @@ -165,15 +165,14 @@ static void nand_read_buf16(struct nand_chip *chip, uint8_t *buf, int len) /** * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. - * @mtd: MTD device structure + * @chip: NAND chip object * @timeo: Timeout * * Helper function for nand_wait_ready used when needing to wait in interrupt * context. */ -static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) +static void panic_nand_wait_ready(struct nand_chip *chip, unsigned long timeo) { - struct nand_chip *chip = mtd_to_nand(mtd); int i; /* Wait for the device to get ready */ @@ -193,11 +192,10 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) */ void nand_wait_ready(struct nand_chip *chip) { - struct mtd_info *mtd = nand_to_mtd(chip); unsigned long timeo = 400; if (in_interrupt() || oops_in_progress) - return panic_nand_wait_ready(mtd, timeo); + return panic_nand_wait_ready(chip, timeo); /* Wait until command is processed or timeout occurs */ timeo = jiffies + msecs_to_jiffies(timeo); @@ -214,14 +212,13 @@ EXPORT_SYMBOL_GPL(nand_wait_ready); /** * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands. - * @mtd: MTD device structure + * @chip: NAND chip object * @timeo: Timeout in ms * * Wait for status ready (i.e. command done) or timeout. */ -static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) +static void nand_wait_status_ready(struct nand_chip *chip, unsigned long timeo) { - register struct nand_chip *chip = mtd_to_nand(mtd); int ret; timeo = jiffies + msecs_to_jiffies(timeo); @@ -321,7 +318,7 @@ static void nand_command(struct nand_chip *chip, unsigned int command, chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ - nand_wait_status_ready(mtd, 250); + nand_wait_status_ready(chip, 250); return; /* This applies to read commands */ @@ -367,7 +364,7 @@ static void nand_ccs_delay(struct nand_chip *chip) * Wait tCCS_min if it is correctly defined, otherwise wait 500ns * (which should be safe for all NANDs). */ - if (chip->setup_data_interface) + if (nand_has_setup_data_iface(chip)) ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000); else ndelay(500); @@ -458,7 +455,7 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command, chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ - nand_wait_status_ready(mtd, 250); + nand_wait_status_ready(chip, 250); return; case NAND_CMD_RNDOUT: @@ -525,7 +522,6 @@ EXPORT_SYMBOL(nand_get_set_features_notsupp); /** * nand_wait - [DEFAULT] wait until the command is done - * @mtd: MTD device structure * @chip: NAND chip structure * * Wait for command done. This applies to erase and program only. @@ -581,7 +577,7 @@ void nand_legacy_set_defaults(struct nand_chip *chip) { unsigned int busw = chip->options & NAND_BUSWIDTH_16; - if (chip->exec_op) + if (nand_has_exec_op(chip)) return; /* check for proper chip_delay setup, set 20us if not */ @@ -589,15 +585,15 @@ void nand_legacy_set_defaults(struct nand_chip *chip) chip->legacy.chip_delay = 20; /* check, if a user supplied command function given */ - if (!chip->legacy.cmdfunc && !chip->exec_op) + if (!chip->legacy.cmdfunc) chip->legacy.cmdfunc = nand_command; /* check, if a user supplied wait function given */ if (chip->legacy.waitfunc == NULL) chip->legacy.waitfunc = nand_wait; - if (!chip->select_chip) - chip->select_chip = nand_select_chip; + if (!chip->legacy.select_chip) + chip->legacy.select_chip = nand_select_chip; /* If called twice, pointers that depend on busw may need to be reset */ if (!chip->legacy.read_byte || chip->legacy.read_byte == nand_read_byte) @@ -625,14 +621,15 @@ int nand_legacy_check_hooks(struct nand_chip *chip) * ->legacy.cmdfunc() is legacy and will only be used if ->exec_op() is * not populated. */ - if (chip->exec_op) + if (nand_has_exec_op(chip)) return 0; /* * Default functions assigned for ->legacy.cmdfunc() and - * ->select_chip() both expect ->legacy.cmd_ctrl() to be populated. + * ->legacy.select_chip() both expect ->legacy.cmd_ctrl() to be + * populated. */ - if ((!chip->legacy.cmdfunc || !chip->select_chip) && + if ((!chip->legacy.cmdfunc || !chip->legacy.select_chip) && !chip->legacy.cmd_ctrl) { pr_err("->legacy.cmd_ctrl() should be provided\n"); return -EINVAL; diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c @@ -33,6 +33,13 @@ static void macronix_nand_fix_broken_get_timings(struct nand_chip *chip) "MX30LF4G18AC", "MX30LF4G28AC", "MX60LF8G18AC", + "MX30UF1G18AC", + "MX30UF1G16AC", + "MX30UF2G18AC", + "MX30UF2G16AC", + "MX30UF4G18AC", + "MX30UF4G16AC", + "MX30UF4G28AC", }; if (!chip->parameters.supports_set_get_features) diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c @@ -443,7 +443,7 @@ static unsigned long total_wear = 0; /* MTD structure for NAND controller */ static struct mtd_info *nsmtd; -static int nandsim_debugfs_show(struct seq_file *m, void *private) +static int nandsim_show(struct seq_file *m, void *private) { unsigned long wmin = -1, wmax = 0, avg; unsigned long deciles[10], decile_max[10], tot = 0; @@ -494,18 +494,7 @@ static int nandsim_debugfs_show(struct seq_file *m, void *private) return 0; } - -static int nandsim_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, nandsim_debugfs_show, inode->i_private); -} - -static const struct file_operations dfs_fops = { - .open = nandsim_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(nandsim); /** * nandsim_debugfs_create - initialize debugfs @@ -531,7 +520,7 @@ static int nandsim_debugfs_create(struct nandsim *dev) } dent = debugfs_create_file("nandsim_wear_report", S_IRUSR, - root, dev, &dfs_fops); + root, dev, &nandsim_fops); if (IS_ERR_OR_NULL(dent)) { NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n"); return -1; @@ -2304,7 +2293,7 @@ static int __init ns_init_module(void) if ((retval = parse_gravepages()) != 0) goto error; - chip->dummy_controller.ops = &ns_controller_ops; + chip->legacy.dummy_controller.ops = &ns_controller_ops; retval = nand_scan(chip, 1); if (retval) { NS_ERR("Could not scan NAND Simulator device\n"); diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c @@ -146,7 +146,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->legacy.IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; chip->legacy.cmd_ctrl = ndfc_hwcontrol; chip->legacy.dev_ready = ndfc_ready; - chip->select_chip = ndfc_select_chip; + chip->legacy.select_chip = ndfc_select_chip; chip->legacy.chip_delay = 50; chip->controller = &ndfc->ndfc_control; chip->legacy.read_buf = ndfc_read_buf; diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c @@ -1944,7 +1944,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case NAND_OMAP_PREFETCH_DMA: dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - info->dma = dma_request_chan(dev, "rxtx"); + info->dma = dma_request_chan(dev->parent, "rxtx"); if (IS_ERR(info->dma)) { dev_err(dev, "DMA engine request failed\n"); diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c @@ -63,7 +63,7 @@ static int plat_nand_probe(struct platform_device *pdev) data->chip.legacy.IO_ADDR_W = data->io_base; data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; - data->chip.select_chip = pdata->ctrl.select_chip; + data->chip.legacy.select_chip = pdata->ctrl.select_chip; data->chip.legacy.write_buf = pdata->ctrl.write_buf; data->chip.legacy.read_buf = pdata->ctrl.read_buf; data->chip.legacy.chip_delay = pdata->chip.chip_delay; diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2804,7 +2804,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, mtd->dev.parent = dev; chip->legacy.cmdfunc = qcom_nandc_command; - chip->select_chip = qcom_nandc_select_chip; + chip->legacy.select_chip = qcom_nandc_select_chip; chip->legacy.read_byte = qcom_nandc_read_byte; chip->legacy.read_buf = qcom_nandc_read_buf; chip->legacy.write_buf = qcom_nandc_write_buf; diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c @@ -151,8 +151,9 @@ static void r852_dma_done(struct r852_device *dev, int error) dev->dma_stage = 0; if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer) - pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN, - dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + dma_unmap_single(&dev->pci_dev->dev, dev->phys_dma_addr, + R852_DMA_LEN, + dev->dma_dir ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } /* @@ -197,11 +198,10 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) bounce = 1; if (!bounce) { - dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf, + dev->phys_dma_addr = dma_map_single(&dev->pci_dev->dev, buf, R852_DMA_LEN, - (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); - - if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr)) + do_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(&dev->pci_dev->dev, dev->phys_dma_addr)) bounce = 1; } @@ -835,7 +835,7 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) pci_set_master(pci_dev); - error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + error = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32)); if (error) goto error2; @@ -885,8 +885,8 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) dev->pci_dev = pci_dev; pci_set_drvdata(pci_dev, dev); - dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN, - &dev->phys_bounce_buffer); + dev->bounce_buffer = dma_alloc_coherent(&pci_dev->dev, R852_DMA_LEN, + &dev->phys_bounce_buffer, GFP_KERNEL); if (!dev->bounce_buffer) goto error6; @@ -946,8 +946,8 @@ error9: error8: pci_iounmap(pci_dev, dev->mmio); error7: - pci_free_consistent(pci_dev, R852_DMA_LEN, - dev->bounce_buffer, dev->phys_bounce_buffer); + dma_free_coherent(&pci_dev->dev, R852_DMA_LEN, dev->bounce_buffer, + dev->phys_bounce_buffer); error6: kfree(dev); error5: @@ -980,8 +980,8 @@ static void r852_remove(struct pci_dev *pci_dev) /* Cleanup */ kfree(dev->tmp_buffer); pci_iounmap(pci_dev, dev->mmio); - pci_free_consistent(pci_dev, R852_DMA_LEN, - dev->bounce_buffer, dev->phys_bounce_buffer); + dma_free_coherent(&pci_dev->dev, R852_DMA_LEN, dev->bounce_buffer, + dev->phys_bounce_buffer); kfree(dev->chip); kfree(dev); @@ -1045,9 +1045,9 @@ static int r852_resume(struct device *device) /* Otherwise, initialize the card */ if (dev->card_registered) { r852_engine_enable(dev); - dev->chip->select_chip(dev->chip, 0); + nand_select_target(dev->chip, 0); nand_reset_op(dev->chip); - dev->chip->select_chip(dev->chip, -1); + nand_deselect_target(dev->chip); } /* Program card detection IRQ */ diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c @@ -866,7 +866,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->legacy.write_buf = s3c2410_nand_write_buf; chip->legacy.read_buf = s3c2410_nand_read_buf; - chip->select_chip = s3c2410_nand_select_chip; + chip->legacy.select_chip = s3c2410_nand_select_chip; chip->legacy.chip_delay = 50; nand_set_controller_data(chip, nmtd); chip->options = set->options; @@ -876,8 +876,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, * let's keep behavior unchanged for legacy boards booting via pdata and * auto-detect timings only when booting with a device tree. */ - if (np) - chip->setup_data_interface = s3c2410_nand_setup_data_interface; + if (!np) + chip->options |= NAND_KEEP_TIMINGS; switch (info->cpu_type) { case TYPE_S3C2410: @@ -1011,6 +1011,7 @@ static int s3c2410_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops s3c24xx_nand_controller_ops = { .attach_chip = s3c2410_nand_attach_chip, + .setup_data_interface = s3c2410_nand_setup_data_interface, }; static const struct of_device_id s3c24xx_nand_dt_ids[] = { diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SuperH FLCTL nand controller * @@ -5,20 +6,6 @@ * Copyright (c) 2008 Atom Create Engineering Co., Ltd. * * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> @@ -1183,7 +1170,7 @@ static int flctl_probe(struct platform_device *pdev) nand->legacy.read_byte = flctl_read_byte; nand->legacy.write_buf = flctl_write_buf; nand->legacy.read_buf = flctl_read_buf; - nand->select_chip = flctl_select_chip; + nand->legacy.select_chip = flctl_select_chip; nand->legacy.cmdfunc = flctl_cmdfunc; nand->legacy.set_features = nand_get_set_features_notsupp; nand->legacy.get_features = nand_get_set_features_notsupp; @@ -1196,7 +1183,7 @@ static int flctl_probe(struct platform_device *pdev) flctl_setup_dma(flctl); - nand->dummy_controller.ops = &flctl_nand_controller_ops; + nand->legacy.dummy_controller.ops = &flctl_nand_controller_ops; ret = nand_scan(nand, 1); if (ret) goto err_chip; @@ -1236,7 +1223,7 @@ static struct platform_driver flctl_driver = { module_platform_driver_probe(flctl_driver, flctl_probe); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Yoshihiro Shimoda"); MODULE_DESCRIPTION("SuperH FLCTL driver"); MODULE_ALIAS("platform:sh_flctl"); diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c @@ -194,7 +194,7 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) chip->options |= NAND_SKIP_BBTSCAN; /* Scan for card properties */ - chip->dummy_controller.ops = &sm_controller_ops; + chip->legacy.dummy_controller.ops = &sm_controller_ops; flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids; ret = nand_scan_with_ids(chip, 1, flash_ids); if (ret) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1393,7 +1393,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *chip, sunxi_nfc_randomizer_enable(mtd); writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, - nfc->regs + NFC_REG_RCMD_SET); + nfc->regs + NFC_REG_WCMD_SET); dma_async_issue_pending(nfc->dmac); @@ -1847,6 +1847,7 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand) static const struct nand_controller_ops sunxi_nand_controller_ops = { .attach_chip = sunxi_nand_attach_chip, + .setup_data_interface = sunxi_nfc_setup_data_interface, }; static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, @@ -1922,12 +1923,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, */ nand->ecc.mode = NAND_ECC_HW; nand_set_flash_node(nand, np); - nand->select_chip = sunxi_nfc_select_chip; + nand->legacy.select_chip = sunxi_nfc_select_chip; nand->legacy.cmd_ctrl = sunxi_nfc_cmd_ctrl; nand->legacy.read_buf = sunxi_nfc_read_buf; nand->legacy.write_buf = sunxi_nfc_write_buf; nand->legacy.read_byte = sunxi_nfc_read_byte; - nand->setup_data_interface = sunxi_nfc_setup_data_interface; mtd = nand_to_mtd(nand); mtd->dev.parent = dev; diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c @@ -530,6 +530,7 @@ static int tango_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops tango_controller_ops = { .attach_chip = tango_attach_chip, + .setup_data_interface = tango_set_timings, }; static int chip_init(struct device *dev, struct device_node *np) @@ -567,10 +568,9 @@ static int chip_init(struct device *dev, struct device_node *np) chip->legacy.read_byte = tango_read_byte; chip->legacy.write_buf = tango_write_buf; chip->legacy.read_buf = tango_read_buf; - chip->select_chip = tango_select_chip; + chip->legacy.select_chip = tango_select_chip; chip->legacy.cmd_ctrl = tango_cmd_ctrl; chip->legacy.dev_ready = tango_dev_ready; - chip->setup_data_interface = tango_set_timings; chip->options = NAND_USE_BOUNCE_BUFFER | NAND_NO_SUBPAGE_WRITE | NAND_WAIT_TCCS; diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c @@ -454,29 +454,24 @@ static const struct nand_op_parser tegra_nand_op_parser = NAND_OP_PARSER( NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 4)), ); +static void tegra_nand_select_target(struct nand_chip *chip, + unsigned int die_nr) +{ + struct tegra_nand_chip *nand = to_tegra_chip(chip); + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + + ctrl->cur_cs = nand->cs[die_nr]; +} + static int tegra_nand_exec_op(struct nand_chip *chip, const struct nand_operation *op, bool check_only) { + tegra_nand_select_target(chip, op->cs); return nand_op_parser_exec_op(chip, &tegra_nand_op_parser, op, check_only); } -static void tegra_nand_select_chip(struct nand_chip *chip, int die_nr) -{ - struct tegra_nand_chip *nand = to_tegra_chip(chip); - struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); - - WARN_ON(die_nr >= (int)ARRAY_SIZE(nand->cs)); - - if (die_nr < 0 || die_nr > 0) { - ctrl->cur_cs = -1; - return; - } - - ctrl->cur_cs = nand->cs[die_nr]; -} - static void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl, struct nand_chip *chip, bool enable) { @@ -503,6 +498,8 @@ static int tegra_nand_page_xfer(struct mtd_info *mtd, struct nand_chip *chip, u32 addr1, cmd, dma_ctrl; int ret; + tegra_nand_select_target(chip, chip->cur_cs); + if (read) { writel_relaxed(NAND_CMD_READ0, ctrl->regs + CMD_REG1); writel_relaxed(NAND_CMD_READSTART, ctrl->regs + CMD_REG2); @@ -1053,6 +1050,8 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops tegra_nand_controller_ops = { .attach_chip = &tegra_nand_attach_chip, + .exec_op = tegra_nand_exec_op, + .setup_data_interface = tegra_nand_setup_data_interface, }; static int tegra_nand_chips_init(struct device *dev, @@ -1115,9 +1114,6 @@ static int tegra_nand_chips_init(struct device *dev, mtd->name = "tegra_nand"; chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; - chip->exec_op = tegra_nand_exec_op; - chip->select_chip = tegra_nand_select_chip; - chip->setup_data_interface = tegra_nand_setup_data_interface; ret = nand_scan(chip, 1); if (ret) diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2009-2015 Freescale Semiconductor, Inc. and others * @@ -10,11 +11,6 @@ * * Based on original driver mpc5121_nfc.c. * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * * Limitations: * - Untested on MPC5125 and M54418. * - DMA and pipelining not used. @@ -152,6 +148,7 @@ enum vf610_nfc_variant { }; struct vf610_nfc { + struct nand_controller base; struct nand_chip chip; struct device *dev; void __iomem *regs; @@ -168,11 +165,6 @@ struct vf610_nfc { u32 ecc_mode; }; -static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip); -} - static inline struct vf610_nfc *chip_to_nfc(struct nand_chip *chip) { return container_of(chip, struct vf610_nfc, chip); @@ -316,8 +308,7 @@ static void vf610_nfc_done(struct vf610_nfc *nfc) static irqreturn_t vf610_nfc_irq(int irq, void *data) { - struct mtd_info *mtd = data; - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + struct vf610_nfc *nfc = data; vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); complete(&nfc->cmd_done); @@ -487,40 +478,40 @@ static const struct nand_op_parser vf610_nfc_op_parser = NAND_OP_PARSER( NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, PAGE_2K + OOB_MAX)), ); -static int vf610_nfc_exec_op(struct nand_chip *chip, - const struct nand_operation *op, - bool check_only) -{ - return nand_op_parser_exec_op(chip, &vf610_nfc_op_parser, op, - check_only); -} - /* * This function supports Vybrid only (MPC5125 would have full RB and four CS) */ -static void vf610_nfc_select_chip(struct nand_chip *chip, int cs) +static void vf610_nfc_select_target(struct nand_chip *chip, unsigned int cs) { - struct vf610_nfc *nfc = mtd_to_nfc(nand_to_mtd(chip)); - u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR); + struct vf610_nfc *nfc = chip_to_nfc(chip); + u32 tmp; /* Vybrid only (MPC5125 would have full RB and four CS) */ if (nfc->variant != NFC_VFC610) return; + tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR); tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); - - if (cs >= 0) { - tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; - tmp |= BIT(cs) << ROW_ADDR_CHIP_SEL_SHIFT; - } + tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; + tmp |= BIT(cs) << ROW_ADDR_CHIP_SEL_SHIFT; vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); } -static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, +static int vf610_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + vf610_nfc_select_target(chip, op->cs); + return nand_op_parser_exec_op(chip, &vf610_nfc_op_parser, op, + check_only); +} + +static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat, uint8_t *oob, int page) { - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + struct vf610_nfc *nfc = chip_to_nfc(chip); + struct mtd_info *mtd = nand_to_mtd(chip); u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; u8 ecc_status; u8 ecc_count; @@ -560,12 +551,14 @@ static void vf610_nfc_fill_row(struct nand_chip *chip, int page, u32 *code, static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { + struct vf610_nfc *nfc = chip_to_nfc(chip); struct mtd_info *mtd = nand_to_mtd(chip); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); int trfr_sz = mtd->writesize + mtd->oobsize; u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; int stat; + vf610_nfc_select_target(chip, chip->cur_cs); + cmd2 |= NAND_CMD_READ0 << CMD_BYTE1_SHIFT; code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2; @@ -592,7 +585,7 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf, mtd->writesize, mtd->oobsize, false); - stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); + stat = vf610_nfc_correct_data(chip, buf, chip->oob_poi, page); if (stat < 0) { mtd->ecc_stats.failed++; @@ -606,13 +599,15 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf, static int vf610_nfc_write_page(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { + struct vf610_nfc *nfc = chip_to_nfc(chip); struct mtd_info *mtd = nand_to_mtd(chip); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); int trfr_sz = mtd->writesize + mtd->oobsize; u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; u8 status; int ret; + vf610_nfc_select_target(chip, chip->cur_cs); + cmd2 |= NAND_CMD_SEQIN << CMD_BYTE1_SHIFT; code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2; @@ -648,8 +643,7 @@ static int vf610_nfc_write_page(struct nand_chip *chip, const uint8_t *buf, static int vf610_nfc_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_required, int page) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + struct vf610_nfc *nfc = chip_to_nfc(chip); int ret; nfc->data_access = true; @@ -662,8 +656,8 @@ static int vf610_nfc_read_page_raw(struct nand_chip *chip, u8 *buf, static int vf610_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf, int oob_required, int page) { + struct vf610_nfc *nfc = chip_to_nfc(chip); struct mtd_info *mtd = nand_to_mtd(chip); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); int ret; nfc->data_access = true; @@ -681,7 +675,7 @@ static int vf610_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf, static int vf610_nfc_read_oob(struct nand_chip *chip, int page) { - struct vf610_nfc *nfc = mtd_to_nfc(nand_to_mtd(chip)); + struct vf610_nfc *nfc = chip_to_nfc(chip); int ret; nfc->data_access = true; @@ -694,7 +688,7 @@ static int vf610_nfc_read_oob(struct nand_chip *chip, int page) static int vf610_nfc_write_oob(struct nand_chip *chip, int page) { struct mtd_info *mtd = nand_to_mtd(chip); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + struct vf610_nfc *nfc = chip_to_nfc(chip); int ret; nfc->data_access = true; @@ -751,7 +745,7 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc) static int vf610_nfc_attach_chip(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + struct vf610_nfc *nfc = chip_to_nfc(chip); vf610_nfc_init_controller(nfc); @@ -809,6 +803,8 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops vf610_nfc_controller_ops = { .attach_chip = vf610_nfc_attach_chip, + .exec_op = vf610_nfc_exec_op, + }; static int vf610_nfc_probe(struct platform_device *pdev) @@ -876,14 +872,11 @@ static int vf610_nfc_probe(struct platform_device *pdev) goto err_disable_clk; } - chip->exec_op = vf610_nfc_exec_op; - chip->select_chip = vf610_nfc_select_chip; - chip->options |= NAND_NO_SUBPAGE_WRITE; init_completion(&nfc->cmd_done); - err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); + err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, nfc); if (err) { dev_err(nfc->dev, "Error requesting IRQ!\n"); goto err_disable_clk; @@ -891,13 +884,16 @@ static int vf610_nfc_probe(struct platform_device *pdev) vf610_nfc_preinit_controller(nfc); + nand_controller_init(&nfc->base); + nfc->base.ops = &vf610_nfc_controller_ops; + chip->controller = &nfc->base; + /* Scan the NAND chip */ - chip->dummy_controller.ops = &vf610_nfc_controller_ops; err = nand_scan(chip, 1); if (err) goto err_disable_clk; - platform_set_drvdata(pdev, mtd); + platform_set_drvdata(pdev, nfc); /* Register device in MTD */ err = mtd_device_register(mtd, NULL, 0); @@ -914,10 +910,9 @@ err_disable_clk: static int vf610_nfc_remove(struct platform_device *pdev) { - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + struct vf610_nfc *nfc = platform_get_drvdata(pdev); - nand_release(mtd_to_nand(mtd)); + nand_release(&nfc->chip); clk_disable_unprepare(nfc->clk); return 0; } @@ -925,8 +920,7 @@ static int vf610_nfc_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int vf610_nfc_suspend(struct device *dev) { - struct mtd_info *mtd = dev_get_drvdata(dev); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); + struct vf610_nfc *nfc = dev_get_drvdata(dev); clk_disable_unprepare(nfc->clk); return 0; @@ -934,11 +928,9 @@ static int vf610_nfc_suspend(struct device *dev) static int vf610_nfc_resume(struct device *dev) { + struct vf610_nfc *nfc = dev_get_drvdata(dev); int err; - struct mtd_info *mtd = dev_get_drvdata(dev); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - err = clk_prepare_enable(nfc->clk); if (err) return err; diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c @@ -176,7 +176,7 @@ static int xway_nand_probe(struct platform_device *pdev) data->chip.legacy.cmd_ctrl = xway_cmd_ctrl; data->chip.legacy.dev_ready = xway_dev_ready; - data->chip.select_chip = xway_select_chip; + data->chip.legacy.select_chip = xway_select_chip; data->chip.legacy.write_buf = xway_write_buf; data->chip.legacy.read_buf = xway_read_buf; data->chip.legacy.read_byte = xway_read_byte; diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o macronix.o micron.o winbond.o +spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c @@ -764,8 +764,10 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + &gigadevice_spinand_manufacturer, &macronix_spinand_manufacturer, &micron_spinand_manufacturer, + &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, }; diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: + * Chuanhong Guo <gch981213@gmail.com> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/spinand.h> + +#define SPINAND_MFR_GIGADEVICE 0xC8 +#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) +#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + + return 0; +} + +static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + if (section) { + region->offset = 16 * section; + region->length = 8; + } else { + /* section 0 has one byte reserved for bad block mark */ + region->offset = 1; + region->length = 7; + } + return 0; +} + +static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: + /* 1-7 bits are flipped. return the maximum. */ + return 7; + + case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: + return 8; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { + .ecc = gd5fxgq4xa_ooblayout_ecc, + .free = gd5fxgq4xa_ooblayout_free, +}; + +static const struct spinand_info gigadevice_spinand_table[] = { + SPINAND_INFO("GD5F1GQ4xA", 0xF1, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F2GQ4xA", 0xF2, + NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4xA", 0xF4, + NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), +}; + +static int gigadevice_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * For GD NANDs, There is an address byte needed to shift in before IDs + * are read out, so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_GIGADEVICE) + return 0; + + ret = spinand_match_and_init(spinand, gigadevice_spinand_table, + ARRAY_SIZE(gigadevice_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { + .detect = gigadevice_spinand_detect, +}; + +const struct spinand_manufacturer gigadevice_spinand_manufacturer = { + .id = SPINAND_MFR_GIGADEVICE, + .name = "GigaDevice", + .ops = &gigadevice_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 exceet electronics GmbH + * Copyright (c) 2018 Kontron Electronics GmbH + * + * Author: Frieder Schrempf <frieder.schrempf@kontron.de> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/spinand.h> + +#define SPINAND_MFR_TOSHIBA 0x98 +#define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int tc58cvg2s0h_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 7) + return -ERANGE; + + region->offset = 128 + 16 * section; + region->length = 16; + + return 0; +} + +static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 0) + return -ERANGE; + + /* 2 bytes reserved for BBM */ + region->offset = 2; + region->length = 126; + + return 0; +} + +static const struct mtd_ooblayout_ops tc58cvg2s0h_ooblayout = { + .ecc = tc58cvg2s0h_ooblayout_ecc, + .free = tc58cvg2s0h_ooblayout_free, +}; + +static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + u8 mbf = 0; + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case STATUS_ECC_HAS_BITFLIPS: + case TOSH_STATUS_ECC_HAS_BITFLIPS_T: + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (spi_mem_exec_op(spinand->spimem, &op)) + return nand->eccreq.strength; + + mbf >>= 4; + + if (WARN_ON(mbf > nand->eccreq.strength || !mbf)) + return nand->eccreq.strength; + + return mbf; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info toshiba_spinand_table[] = { + SPINAND_INFO("TC58CVG2S0H", 0xCD, + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tc58cvg2s0h_ooblayout, + tc58cvg2s0h_ecc_get_status)), +}; + +static int toshiba_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Toshiba SPI NAND read ID needs a dummy byte, + * so the first byte in id is garbage. + */ + if (id[1] != SPINAND_MFR_TOSHIBA) + return 0; + + ret = spinand_match_and_init(spinand, toshiba_spinand_table, + ARRAY_SIZE(toshiba_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { + .detect = toshiba_spinand_detect, +}; + +const struct spinand_manufacturer toshiba_spinand_manufacturer = { + .id = SPINAND_MFR_TOSHIBA, + .name = "Toshiba", + .ops = &toshiba_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c @@ -84,6 +84,14 @@ static const struct spinand_info winbond_spinand_table[] = { 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), + SPINAND_INFO("W25N01GV", 0xAA, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), }; /** diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c @@ -346,25 +346,26 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) goto fail; } - /* increase and write Wear-Leveling info */ - nb_erases = le32_to_cpu(uci.WearInfo); - nb_erases++; - - /* wrap (almost impossible with current flash) or free block */ - if (nb_erases == 0) - nb_erases = 1; - - /* check the "freeness" of Erase Unit before updating metadata - * FixMe: is this check really necessary ? since we have check the - * return code after the erase operation. */ - if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) - goto fail; - - uci.WearInfo = le32_to_cpu(nb_erases); - if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + - 8, 8, &retlen, (char *)&uci) < 0) - goto fail; - return 0; + /* increase and write Wear-Leveling info */ + nb_erases = le32_to_cpu(uci.WearInfo); + nb_erases++; + + /* wrap (almost impossible with current flash) or free block */ + if (nb_erases == 0) + nb_erases = 1; + + /* check the "freeness" of Erase Unit before updating metadata + * FixMe: is this check really necessary ? since we have check the + * return code after the erase operation. + */ + if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) + goto fail; + + uci.WearInfo = le32_to_cpu(nb_erases); + if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + + 8, 8, &retlen, (char *)&uci) < 0) + goto fail; + return 0; fail: /* could not format, update the bad block table (caller is responsible for setting the ReplUnitTable to BLOCK_RESERVED on failure) */ diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig @@ -14,3 +14,53 @@ config MTD_SHARPSL_PARTS This provides the read-only FTL logic necessary to read the partition table from the NAND flash of Sharp SL Series (Zaurus) and the MTD partition parser using this code. + +config MTD_REDBOOT_PARTS + tristate "RedBoot partition table parsing" + help + RedBoot is a ROM monitor and bootloader which deals with multiple + 'images' in flash devices by putting a table one of the erase + blocks on the device, similar to a partition table, which gives + the offsets, lengths and names of all the images stored in the + flash. + + If you need code which can detect and parse this table, and register + MTD 'partitions' corresponding to each image in the table, enable + this option. + + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. The + SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for + example. + +if MTD_REDBOOT_PARTS + +config MTD_REDBOOT_DIRECTORY_BLOCK + int "Location of RedBoot partition table" + default "-1" + help + This option is the Linux counterpart to the + CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time + option. + + The option specifies which Flash sectors holds the RedBoot + partition table. A zero or positive value gives an absolute + erase block number. A negative value specifies a number of + sectors before the end of the device. + + For example "2" means block number 2, "-1" means the last + block and "-2" means the penultimate block. + +config MTD_REDBOOT_PARTS_UNALLOCATED + bool "Include unallocated flash regions" + help + If you need to register each unallocated flash region as a MTD + 'partition', enable this option. + +config MTD_REDBOOT_PARTS_READONLY + bool "Force read-only for RedBoot system images" + help + If you need to force read-only for 'RedBoot', 'RedBoot Config' and + 'FIS directory' images, enable this option. + +endif # MTD_REDBOOT_PARTS diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o +obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o diff --git a/drivers/mtd/parsers/redboot.c b/drivers/mtd/parsers/redboot.c @@ -0,0 +1,332 @@ +/* + * Parse RedBoot-style Flash Image System (FIS) tables and + * produce a Linux partition array to match. + * + * Copyright © 2001 Red Hat UK Limited + * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/of.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/module.h> + +struct fis_image_desc { + unsigned char name[16]; // Null terminated name + uint32_t flash_base; // Address within FLASH of image + uint32_t mem_base; // Address in memory where it executes + uint32_t size; // Length of image + uint32_t entry_point; // Execution entry point + uint32_t data_length; // Length of actual data + unsigned char _pad[256-(16+7*sizeof(uint32_t))]; + uint32_t desc_cksum; // Checksum over image descriptor + uint32_t file_cksum; // Checksum over image data +}; + +struct fis_list { + struct fis_image_desc *img; + struct fis_list *next; +}; + +static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; +module_param(directory, int, 0); + +static inline int redboot_checksum(struct fis_image_desc *img) +{ + /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ + return 1; +} + +static void parse_redboot_of(struct mtd_info *master) +{ + struct device_node *np; + u32 dirblock; + int ret; + + np = mtd_get_of_node(master); + if (!np) + return; + + ret = of_property_read_u32(np, "fis-index-block", &dirblock); + if (ret) + return; + + /* + * Assign the block found in the device tree to the local + * directory block pointer. + */ + directory = dirblock; +} + +static int parse_redboot_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + int nrparts = 0; + struct fis_image_desc *buf; + struct mtd_partition *parts; + struct fis_list *fl = NULL, *tmp_fl; + int ret, i; + size_t retlen; + char *names; + char *nullname; + int namelen = 0; + int nulllen = 0; + int numslots; + unsigned long offset; +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + static char nullstring[] = "unallocated"; +#endif + + parse_redboot_of(master); + + if ( directory < 0 ) { + offset = master->size + directory * master->erasesize; + while (mtd_block_isbad(master, offset)) { + if (!offset) { + nogood: + printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n"); + return -EIO; + } + offset -= master->erasesize; + } + } else { + offset = directory * master->erasesize; + while (mtd_block_isbad(master, offset)) { + offset += master->erasesize; + if (offset == master->size) + goto nogood; + } + } + buf = vmalloc(master->erasesize); + + if (!buf) + return -ENOMEM; + + printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", + master->name, offset); + + ret = mtd_read(master, offset, master->erasesize, &retlen, + (void *)buf); + + if (ret) + goto out; + + if (retlen != master->erasesize) { + ret = -EIO; + goto out; + } + + numslots = (master->erasesize / sizeof(struct fis_image_desc)); + for (i = 0; i < numslots; i++) { + if (!memcmp(buf[i].name, "FIS directory", 14)) { + /* This is apparently the FIS directory entry for the + * FIS directory itself. The FIS directory size is + * one erase block; if the buf[i].size field is + * swab32(erasesize) then we know we are looking at + * a byte swapped FIS directory - swap all the entries! + * (NOTE: this is 'size' not 'data_length'; size is + * the full size of the entry.) + */ + + /* RedBoot can combine the FIS directory and + config partitions into a single eraseblock; + we assume wrong-endian if either the swapped + 'size' matches the eraseblock size precisely, + or if the swapped size actually fits in an + eraseblock while the unswapped size doesn't. */ + if (swab32(buf[i].size) == master->erasesize || + (buf[i].size > master->erasesize + && swab32(buf[i].size) < master->erasesize)) { + int j; + /* Update numslots based on actual FIS directory size */ + numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); + for (j = 0; j < numslots; ++j) { + + /* A single 0xff denotes a deleted entry. + * Two of them in a row is the end of the table. + */ + if (buf[j].name[0] == 0xff) { + if (buf[j].name[1] == 0xff) { + break; + } else { + continue; + } + } + + /* The unsigned long fields were written with the + * wrong byte sex, name and pad have no byte sex. + */ + swab32s(&buf[j].flash_base); + swab32s(&buf[j].mem_base); + swab32s(&buf[j].size); + swab32s(&buf[j].entry_point); + swab32s(&buf[j].data_length); + swab32s(&buf[j].desc_cksum); + swab32s(&buf[j].file_cksum); + } + } else if (buf[i].size < master->erasesize) { + /* Update numslots based on actual FIS directory size */ + numslots = buf[i].size / sizeof(struct fis_image_desc); + } + break; + } + } + if (i == numslots) { + /* Didn't find it */ + printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", + master->name); + ret = 0; + goto out; + } + + for (i = 0; i < numslots; i++) { + struct fis_list *new_fl, **prev; + + if (buf[i].name[0] == 0xff) { + if (buf[i].name[1] == 0xff) { + break; + } else { + continue; + } + } + if (!redboot_checksum(&buf[i])) + break; + + new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL); + namelen += strlen(buf[i].name)+1; + if (!new_fl) { + ret = -ENOMEM; + goto out; + } + new_fl->img = &buf[i]; + if (data && data->origin) + buf[i].flash_base -= data->origin; + else + buf[i].flash_base &= master->size-1; + + /* I'm sure the JFFS2 code has done me permanent damage. + * I now think the following is _normal_ + */ + prev = &fl; + while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base) + prev = &(*prev)->next; + new_fl->next = *prev; + *prev = new_fl; + + nrparts++; + } +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->img->flash_base) { + nrparts++; + nulllen = sizeof(nullstring); + } + + for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) { + if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) { + nrparts++; + nulllen = sizeof(nullstring); + } + } +#endif + parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); + + if (!parts) { + ret = -ENOMEM; + goto out; + } + + nullname = (char *)&parts[nrparts]; +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (nulllen > 0) { + strcpy(nullname, nullstring); + } +#endif + names = nullname + nulllen; + + i=0; + +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->img->flash_base) { + parts[0].name = nullname; + parts[0].size = fl->img->flash_base; + parts[0].offset = 0; + i++; + } +#endif + for ( ; i<nrparts; i++) { + parts[i].size = fl->img->size; + parts[i].offset = fl->img->flash_base; + parts[i].name = names; + + strcpy(names, fl->img->name); +#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY + if (!memcmp(names, "RedBoot", 8) || + !memcmp(names, "RedBoot config", 15) || + !memcmp(names, "FIS directory", 14)) { + parts[i].mask_flags = MTD_WRITEABLE; + } +#endif + names += strlen(names)+1; + +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { + i++; + parts[i].offset = parts[i-1].size + parts[i-1].offset; + parts[i].size = fl->next->img->flash_base - parts[i].offset; + parts[i].name = nullname; + } +#endif + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); + } + ret = nrparts; + *pparts = parts; + out: + while (fl) { + struct fis_list *old = fl; + fl = fl->next; + kfree(old); + } + vfree(buf); + return ret; +} + +static const struct of_device_id mtd_parser_redboot_of_match_table[] = { + { .compatible = "redboot-fis" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtd_parser_redboot_of_match_table); + +static struct mtd_part_parser redboot_parser = { + .parse_fn = parse_redboot_partitions, + .name = "RedBoot", + .of_match_table = mtd_parser_redboot_of_match_table, +}; +module_mtd_part_parser(redboot_parser); + +/* mtd parsers will request the module by parser name */ +MODULE_ALIAS("RedBoot"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables"); diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c @@ -1,302 +0,0 @@ -/* - * Parse RedBoot-style Flash Image System (FIS) tables and - * produce a Linux partition array to match. - * - * Copyright © 2001 Red Hat UK Limited - * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/vmalloc.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> -#include <linux/module.h> - -struct fis_image_desc { - unsigned char name[16]; // Null terminated name - uint32_t flash_base; // Address within FLASH of image - uint32_t mem_base; // Address in memory where it executes - uint32_t size; // Length of image - uint32_t entry_point; // Execution entry point - uint32_t data_length; // Length of actual data - unsigned char _pad[256-(16+7*sizeof(uint32_t))]; - uint32_t desc_cksum; // Checksum over image descriptor - uint32_t file_cksum; // Checksum over image data -}; - -struct fis_list { - struct fis_image_desc *img; - struct fis_list *next; -}; - -static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; -module_param(directory, int, 0); - -static inline int redboot_checksum(struct fis_image_desc *img) -{ - /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ - return 1; -} - -static int parse_redboot_partitions(struct mtd_info *master, - const struct mtd_partition **pparts, - struct mtd_part_parser_data *data) -{ - int nrparts = 0; - struct fis_image_desc *buf; - struct mtd_partition *parts; - struct fis_list *fl = NULL, *tmp_fl; - int ret, i; - size_t retlen; - char *names; - char *nullname; - int namelen = 0; - int nulllen = 0; - int numslots; - unsigned long offset; -#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED - static char nullstring[] = "unallocated"; -#endif - - if ( directory < 0 ) { - offset = master->size + directory * master->erasesize; - while (mtd_block_isbad(master, offset)) { - if (!offset) { - nogood: - printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n"); - return -EIO; - } - offset -= master->erasesize; - } - } else { - offset = directory * master->erasesize; - while (mtd_block_isbad(master, offset)) { - offset += master->erasesize; - if (offset == master->size) - goto nogood; - } - } - buf = vmalloc(master->erasesize); - - if (!buf) - return -ENOMEM; - - printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", - master->name, offset); - - ret = mtd_read(master, offset, master->erasesize, &retlen, - (void *)buf); - - if (ret) - goto out; - - if (retlen != master->erasesize) { - ret = -EIO; - goto out; - } - - numslots = (master->erasesize / sizeof(struct fis_image_desc)); - for (i = 0; i < numslots; i++) { - if (!memcmp(buf[i].name, "FIS directory", 14)) { - /* This is apparently the FIS directory entry for the - * FIS directory itself. The FIS directory size is - * one erase block; if the buf[i].size field is - * swab32(erasesize) then we know we are looking at - * a byte swapped FIS directory - swap all the entries! - * (NOTE: this is 'size' not 'data_length'; size is - * the full size of the entry.) - */ - - /* RedBoot can combine the FIS directory and - config partitions into a single eraseblock; - we assume wrong-endian if either the swapped - 'size' matches the eraseblock size precisely, - or if the swapped size actually fits in an - eraseblock while the unswapped size doesn't. */ - if (swab32(buf[i].size) == master->erasesize || - (buf[i].size > master->erasesize - && swab32(buf[i].size) < master->erasesize)) { - int j; - /* Update numslots based on actual FIS directory size */ - numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); - for (j = 0; j < numslots; ++j) { - - /* A single 0xff denotes a deleted entry. - * Two of them in a row is the end of the table. - */ - if (buf[j].name[0] == 0xff) { - if (buf[j].name[1] == 0xff) { - break; - } else { - continue; - } - } - - /* The unsigned long fields were written with the - * wrong byte sex, name and pad have no byte sex. - */ - swab32s(&buf[j].flash_base); - swab32s(&buf[j].mem_base); - swab32s(&buf[j].size); - swab32s(&buf[j].entry_point); - swab32s(&buf[j].data_length); - swab32s(&buf[j].desc_cksum); - swab32s(&buf[j].file_cksum); - } - } else if (buf[i].size < master->erasesize) { - /* Update numslots based on actual FIS directory size */ - numslots = buf[i].size / sizeof(struct fis_image_desc); - } - break; - } - } - if (i == numslots) { - /* Didn't find it */ - printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", - master->name); - ret = 0; - goto out; - } - - for (i = 0; i < numslots; i++) { - struct fis_list *new_fl, **prev; - - if (buf[i].name[0] == 0xff) { - if (buf[i].name[1] == 0xff) { - break; - } else { - continue; - } - } - if (!redboot_checksum(&buf[i])) - break; - - new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL); - namelen += strlen(buf[i].name)+1; - if (!new_fl) { - ret = -ENOMEM; - goto out; - } - new_fl->img = &buf[i]; - if (data && data->origin) - buf[i].flash_base -= data->origin; - else - buf[i].flash_base &= master->size-1; - - /* I'm sure the JFFS2 code has done me permanent damage. - * I now think the following is _normal_ - */ - prev = &fl; - while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base) - prev = &(*prev)->next; - new_fl->next = *prev; - *prev = new_fl; - - nrparts++; - } -#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED - if (fl->img->flash_base) { - nrparts++; - nulllen = sizeof(nullstring); - } - - for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) { - if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) { - nrparts++; - nulllen = sizeof(nullstring); - } - } -#endif - parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); - - if (!parts) { - ret = -ENOMEM; - goto out; - } - - nullname = (char *)&parts[nrparts]; -#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED - if (nulllen > 0) { - strcpy(nullname, nullstring); - } -#endif - names = nullname + nulllen; - - i=0; - -#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED - if (fl->img->flash_base) { - parts[0].name = nullname; - parts[0].size = fl->img->flash_base; - parts[0].offset = 0; - i++; - } -#endif - for ( ; i<nrparts; i++) { - parts[i].size = fl->img->size; - parts[i].offset = fl->img->flash_base; - parts[i].name = names; - - strcpy(names, fl->img->name); -#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY - if (!memcmp(names, "RedBoot", 8) || - !memcmp(names, "RedBoot config", 15) || - !memcmp(names, "FIS directory", 14)) { - parts[i].mask_flags = MTD_WRITEABLE; - } -#endif - names += strlen(names)+1; - -#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED - if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { - i++; - parts[i].offset = parts[i-1].size + parts[i-1].offset; - parts[i].size = fl->next->img->flash_base - parts[i].offset; - parts[i].name = nullname; - } -#endif - tmp_fl = fl; - fl = fl->next; - kfree(tmp_fl); - } - ret = nrparts; - *pparts = parts; - out: - while (fl) { - struct fis_list *old = fl; - fl = fl->next; - kfree(old); - } - vfree(buf); - return ret; -} - -static struct mtd_part_parser redboot_parser = { - .parse_fn = parse_redboot_partitions, - .name = "RedBoot", -}; -module_mtd_part_parser(redboot_parser); - -/* mtd parsers will request the module by parser name */ -MODULE_ALIAS("RedBoot"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables"); diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c * * Copyright (C) 2005, Intec Automation Inc. * Copyright (C) 2014, Freescale Semiconductor, Inc. - * - * This code 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/err.h> @@ -42,6 +39,197 @@ #define SPI_NOR_MAX_ID_LEN 6 #define SPI_NOR_MAX_ADDR_WIDTH 4 +struct spi_nor_read_command { + u8 num_mode_clocks; + u8 num_wait_states; + u8 opcode; + enum spi_nor_protocol proto; +}; + +struct spi_nor_pp_command { + u8 opcode; + enum spi_nor_protocol proto; +}; + +enum spi_nor_read_command_index { + SNOR_CMD_READ, + SNOR_CMD_READ_FAST, + SNOR_CMD_READ_1_1_1_DTR, + + /* Dual SPI */ + SNOR_CMD_READ_1_1_2, + SNOR_CMD_READ_1_2_2, + SNOR_CMD_READ_2_2_2, + SNOR_CMD_READ_1_2_2_DTR, + + /* Quad SPI */ + SNOR_CMD_READ_1_1_4, + SNOR_CMD_READ_1_4_4, + SNOR_CMD_READ_4_4_4, + SNOR_CMD_READ_1_4_4_DTR, + + /* Octo SPI */ + SNOR_CMD_READ_1_1_8, + SNOR_CMD_READ_1_8_8, + SNOR_CMD_READ_8_8_8, + SNOR_CMD_READ_1_8_8_DTR, + + SNOR_CMD_READ_MAX +}; + +enum spi_nor_pp_command_index { + SNOR_CMD_PP, + + /* Quad SPI */ + SNOR_CMD_PP_1_1_4, + SNOR_CMD_PP_1_4_4, + SNOR_CMD_PP_4_4_4, + + /* Octo SPI */ + SNOR_CMD_PP_1_1_8, + SNOR_CMD_PP_1_8_8, + SNOR_CMD_PP_8_8_8, + + SNOR_CMD_PP_MAX +}; + +struct spi_nor_flash_parameter { + u64 size; + u32 page_size; + + struct spi_nor_hwcaps hwcaps; + struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; + struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; + + int (*quad_enable)(struct spi_nor *nor); +}; + +struct sfdp_parameter_header { + u8 id_lsb; + u8 minor; + u8 major; + u8 length; /* in double words */ + u8 parameter_table_pointer[3]; /* byte address */ + u8 id_msb; +}; + +#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb) +#define SFDP_PARAM_HEADER_PTP(p) \ + (((p)->parameter_table_pointer[2] << 16) | \ + ((p)->parameter_table_pointer[1] << 8) | \ + ((p)->parameter_table_pointer[0] << 0)) + +#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ +#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ +#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */ + +#define SFDP_SIGNATURE 0x50444653U +#define SFDP_JESD216_MAJOR 1 +#define SFDP_JESD216_MINOR 0 +#define SFDP_JESD216A_MINOR 5 +#define SFDP_JESD216B_MINOR 6 + +struct sfdp_header { + u32 signature; /* Ox50444653U <=> "SFDP" */ + u8 minor; + u8 major; + u8 nph; /* 0-base number of parameter headers */ + u8 unused; + + /* Basic Flash Parameter Table. */ + struct sfdp_parameter_header bfpt_header; +}; + +/* Basic Flash Parameter Table */ + +/* + * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs. + * They are indexed from 1 but C arrays are indexed from 0. + */ +#define BFPT_DWORD(i) ((i) - 1) +#define BFPT_DWORD_MAX 16 + +/* The first version of JESB216 defined only 9 DWORDs. */ +#define BFPT_DWORD_MAX_JESD216 9 + +/* 1st DWORD. */ +#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16) +#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17) +#define BFPT_DWORD1_DTR BIT(19) +#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20) +#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21) +#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22) + +/* 5th DWORD. */ +#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0) +#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4) + +/* 11th DWORD. */ +#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4 +#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4) + +/* 15th DWORD. */ + +/* + * (from JESD216 rev B) + * Quad Enable Requirements (QER): + * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4 + * reads based on instruction. DQ3/HOLD# functions are hold during + * instruction phase. + * - 001b: QE is bit 1 of status register 2. It is set via Write Status with + * two data bytes where bit 1 of the second byte is one. + * [...] + * Writing only one byte to the status register has the side-effect of + * clearing status register 2, including the QE bit. The 100b code is + * used if writing one byte to the status register does not modify + * status register 2. + * - 010b: QE is bit 6 of status register 1. It is set via Write Status with + * one data byte where bit 6 is one. + * [...] + * - 011b: QE is bit 7 of status register 2. It is set via Write status + * register 2 instruction 3Eh with one data byte where bit 7 is one. + * [...] + * The status register 2 is read using instruction 3Fh. + * - 100b: QE is bit 1 of status register 2. It is set via Write Status with + * two data bytes where bit 1 of the second byte is one. + * [...] + * In contrast to the 001b code, writing one byte to the status + * register does not modify status register 2. + * - 101b: QE is bit 1 of status register 2. Status register 1 is read using + * Read Status instruction 05h. Status register2 is read using + * instruction 35h. QE is set via Writ Status instruction 01h with + * two data bytes where bit 1 of the second byte is one. + * [...] + */ +#define BFPT_DWORD15_QER_MASK GENMASK(22, 20) +#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ +#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) +#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ +#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ + +struct sfdp_bfpt { + u32 dwords[BFPT_DWORD_MAX]; +}; + +/** + * struct spi_nor_fixups - SPI NOR fixup hooks + * @post_bfpt: called after the BFPT table has been parsed + * + * Those hooks can be used to tweak the SPI NOR configuration when the SFDP + * table is broken or not available. + */ +struct spi_nor_fixups { + int (*post_bfpt)(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params); +}; + struct flash_info { char *name; @@ -91,13 +279,14 @@ struct flash_info { #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ #define USE_CLSR BIT(14) /* use CLSR command */ + /* Part specific fixup hooks. */ + const struct spi_nor_fixups *fixups; + int (*quad_enable)(struct spi_nor *nor); }; #define JEDEC_MFR(info) ((info)->id[0]) -static const struct flash_info *spi_nor_match_id(const char *name); - /* * Read the status register, returning its value in the location * Return the status register value. @@ -159,7 +348,7 @@ static int read_cr(struct spi_nor *nor) * Write status register 1 byte * Returns negative if error occurred. */ -static inline int write_sr(struct spi_nor *nor, u8 val) +static int write_sr(struct spi_nor *nor, u8 val) { nor->cmd_buf[0] = val; return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1); @@ -169,7 +358,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) * Set write enable latch with Write Enable command. * Returns negative if error occurred. */ -static inline int write_enable(struct spi_nor *nor) +static int write_enable(struct spi_nor *nor) { return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); } @@ -177,12 +366,12 @@ static inline int write_enable(struct spi_nor *nor) /* * Send write disable instruction to the chip. */ -static inline int write_disable(struct spi_nor *nor) +static int write_disable(struct spi_nor *nor) { return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0); } -static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) { return mtd->priv; } @@ -200,7 +389,7 @@ static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) return opcode; } -static inline u8 spi_nor_convert_3to4_read(u8 opcode) +static u8 spi_nor_convert_3to4_read(u8 opcode) { static const u8 spi_nor_3to4_read[][2] = { { SPINOR_OP_READ, SPINOR_OP_READ_4B }, @@ -219,7 +408,7 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode) ARRAY_SIZE(spi_nor_3to4_read)); } -static inline u8 spi_nor_convert_3to4_program(u8 opcode) +static u8 spi_nor_convert_3to4_program(u8 opcode) { static const u8 spi_nor_3to4_program[][2] = { { SPINOR_OP_PP, SPINOR_OP_PP_4B }, @@ -231,7 +420,7 @@ static inline u8 spi_nor_convert_3to4_program(u8 opcode) ARRAY_SIZE(spi_nor_3to4_program)); } -static inline u8 spi_nor_convert_3to4_erase(u8 opcode) +static u8 spi_nor_convert_3to4_erase(u8 opcode) { static const u8 spi_nor_3to4_erase[][2] = { { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B }, @@ -243,15 +432,14 @@ static inline u8 spi_nor_convert_3to4_erase(u8 opcode) ARRAY_SIZE(spi_nor_3to4_erase)); } -static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, - const struct flash_info *info) +static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) { /* Do some manufacturer fixups first */ - switch (JEDEC_MFR(info)) { + switch (JEDEC_MFR(nor->info)) { case SNOR_MFR_SPANSION: /* No small sector erase for 4-byte command set */ nor->erase_opcode = SPINOR_OP_SE; - nor->mtd.erasesize = info->sector_size; + nor->mtd.erasesize = nor->info->sector_size; break; default: @@ -276,17 +464,18 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, } /* Enable/disable 4-byte addressing mode. */ -static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, - int enable) +static int set_4byte(struct spi_nor *nor, bool enable) { int status; bool need_wren = false; u8 cmd; - switch (JEDEC_MFR(info)) { + switch (JEDEC_MFR(nor->info)) { + case SNOR_MFR_ST: case SNOR_MFR_MICRON: /* Some Micron need WREN command; all will accept it */ need_wren = true; + /* fall through */ case SNOR_MFR_MACRONIX: case SNOR_MFR_WINBOND: if (need_wren) @@ -298,7 +487,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, write_disable(nor); if (!status && !enable && - JEDEC_MFR(info) == SNOR_MFR_WINBOND) { + JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) { /* * On Winbond W25Q256FV, leaving 4byte mode causes * the Extended Address Register to be set to 1, so all @@ -333,7 +522,7 @@ static int s3an_sr_ready(struct spi_nor *nor) return !!(val & XSR_RDY); } -static inline int spi_nor_sr_ready(struct spi_nor *nor) +static int spi_nor_sr_ready(struct spi_nor *nor) { int sr = read_sr(nor); if (sr < 0) @@ -352,7 +541,7 @@ static inline int spi_nor_sr_ready(struct spi_nor *nor) return !(sr & SR_WIP); } -static inline int spi_nor_fsr_ready(struct spi_nor *nor) +static int spi_nor_fsr_ready(struct spi_nor *nor) { int fsr = read_fsr(nor); if (fsr < 0) @@ -1200,59 +1389,324 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) return ret; } -static int macronix_quad_enable(struct spi_nor *nor); +/* + * Write status Register and configuration register with 2 bytes + * The first byte will be written to the status register, while the + * second byte will be written to the configuration register. + * Return negative if error occurred. + */ +static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) +{ + int ret; -/* Used when the "_ext_id" is two bytes at most */ -#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff, \ - ((_ext_id) >> 8) & 0xff, \ - (_ext_id) & 0xff, \ - }, \ - .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ - .flags = (_flags), + write_enable(nor); -#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff, \ - ((_ext_id) >> 16) & 0xff, \ - ((_ext_id) >> 8) & 0xff, \ - (_ext_id) & 0xff, \ - }, \ - .id_len = 6, \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ - .flags = (_flags), + ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); + if (ret < 0) { + dev_err(nor->dev, + "error while writing configuration register\n"); + return -EINVAL; + } -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = (_page_size), \ - .addr_width = (_addr_width), \ - .flags = (_flags), + ret = spi_nor_wait_till_ready(nor); + if (ret) { + dev_err(nor->dev, + "timeout while writing configuration register\n"); + return ret; + } -#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff \ - }, \ - .id_len = 3, \ - .sector_size = (8*_page_size), \ - .n_sectors = (_n_sectors), \ - .page_size = _page_size, \ - .addr_width = 3, \ - .flags = SPI_NOR_NO_FR | SPI_S3AN, + return 0; +} -/* NOTE: double check command sets and memory organization when you add +/** + * macronix_quad_enable() - set QE bit in Status Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Status Register. + * + * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories. + * + * Return: 0 on success, -errno otherwise. + */ +static int macronix_quad_enable(struct spi_nor *nor) +{ + int ret, val; + + val = read_sr(nor); + if (val < 0) + return val; + if (val & SR_QUAD_EN_MX) + return 0; + + write_enable(nor); + + write_sr(nor, val | SR_QUAD_EN_MX); + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + ret = read_sr(nor); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/** + * spansion_quad_enable() - set QE bit in Configuraiton Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Configuration Register. + * This function is kept for legacy purpose because it has been used for a + * long time without anybody complaining but it should be considered as + * deprecated and maybe buggy. + * First, this function doesn't care about the previous values of the Status + * and Configuration Registers when it sets the QE bit (bit 1) in the + * Configuration Register: all other bits are cleared, which may have unwanted + * side effects like removing some block protections. + * Secondly, it uses the Read Configuration Register (35h) instruction though + * some very old and few memories don't support this instruction. If a pull-up + * resistor is present on the MISO/IO1 line, we might still be able to pass the + * "read back" test because the QSPI memory doesn't recognize the command, + * so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF. + * + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI + * memories. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_quad_enable(struct spi_nor *nor) +{ + u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN}; + int ret; + + ret = write_sr_cr(nor, sr_cr); + if (ret) + return ret; + + /* read back and check it */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/** + * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Configuration Register. + * This function should be used with QSPI memories not supporting the Read + * Configuration Register (35h) instruction. + * + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI + * memories. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) +{ + u8 sr_cr[2]; + int ret; + + /* Keep the current value of the Status Register. */ + ret = read_sr(nor); + if (ret < 0) { + dev_err(nor->dev, "error while reading status register\n"); + return -EINVAL; + } + sr_cr[0] = ret; + sr_cr[1] = CR_QUAD_EN_SPAN; + + return write_sr_cr(nor, sr_cr); +} + +/** + * spansion_read_cr_quad_enable() - set QE bit in Configuration Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Configuration Register. + * This function should be used with QSPI memories supporting the Read + * Configuration Register (35h) instruction. + * + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI + * memories. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_read_cr_quad_enable(struct spi_nor *nor) +{ + struct device *dev = nor->dev; + u8 sr_cr[2]; + int ret; + + /* Check current Quad Enable bit value. */ + ret = read_cr(nor); + if (ret < 0) { + dev_err(dev, "error while reading configuration register\n"); + return -EINVAL; + } + + if (ret & CR_QUAD_EN_SPAN) + return 0; + + sr_cr[1] = ret | CR_QUAD_EN_SPAN; + + /* Keep the current value of the Status Register. */ + ret = read_sr(nor); + if (ret < 0) { + dev_err(dev, "error while reading status register\n"); + return -EINVAL; + } + sr_cr[0] = ret; + + ret = write_sr_cr(nor, sr_cr); + if (ret) + return ret; + + /* Read back and check it. */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/** + * sr2_bit7_quad_enable() - set QE bit in Status Register 2. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Status Register 2. + * + * This is one of the procedures to set the QE bit described in the SFDP + * (JESD216 rev B) specification but no manufacturer using this procedure has + * been identified yet, hence the name of the function. + * + * Return: 0 on success, -errno otherwise. + */ +static int sr2_bit7_quad_enable(struct spi_nor *nor) +{ + u8 sr2; + int ret; + + /* Check current Quad Enable bit value. */ + ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); + if (ret) + return ret; + if (sr2 & SR2_QUAD_EN_BIT7) + return 0; + + /* Update the Quad Enable bit. */ + sr2 |= SR2_QUAD_EN_BIT7; + + write_enable(nor); + + ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1); + if (ret < 0) { + dev_err(nor->dev, "error while writing status register 2\n"); + return -EINVAL; + } + + ret = spi_nor_wait_till_ready(nor); + if (ret < 0) { + dev_err(nor->dev, "timeout while writing status register 2\n"); + return ret; + } + + /* Read back and check it. */ + ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); + if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) { + dev_err(nor->dev, "SR2 Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 16) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = 6, \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = (_page_size), \ + .addr_width = (_addr_width), \ + .flags = (_flags), + +#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff \ + }, \ + .id_len = 3, \ + .sector_size = (8*_page_size), \ + .n_sectors = (_n_sectors), \ + .page_size = _page_size, \ + .addr_width = 3, \ + .flags = SPI_NOR_NO_FR | SPI_S3AN, + +static int +mx25l25635_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + /* + * MX25L25635F supports 4B opcodes but MX25L25635E does not. + * Unfortunately, Macronix has re-used the same JEDEC ID for both + * variants which prevents us from defining a new entry in the parts + * table. + * We need a way to differentiate MX25L25635E and MX25L25635F, and it + * seems that the F version advertises support for Fast Read 4-4-4 in + * its BFPT table. + */ + if (bfpt->dwords[BFPT_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) + nor->flags |= SNOR_F_4B_OPCODES; + + return 0; +} + +static struct spi_nor_fixups mx25l25635_fixups = { + .post_bfpt = mx25l25635_post_bfpt_fixups, +}; + +/* NOTE: double check command sets and memory organization when you add * more nor chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. * @@ -1352,12 +1806,19 @@ static const struct flash_info spi_nor_ids[] = { { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) }, { "is25lq040b", INFO(0x9d4013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25lp016d", INFO(0x9d6015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "is25lp080d", INFO(0x9d6014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25lp032", INFO(0x9d6016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "is25lp064", INFO(0x9d6017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ) }, { "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ) }, { "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512, - SECT_4K | SPI_NOR_DUAL_READ) }, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) }, { "is25wp032", INFO(0x9d7016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "is25wp064", INFO(0x9d7017, 0, 64 * 1024, 128, @@ -1380,7 +1841,11 @@ static const struct flash_info spi_nor_ids[] = { { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, - { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) + .fixups = &mx25l25635_fixups }, { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, @@ -1388,7 +1853,7 @@ static const struct flash_info spi_nor_ids[] = { { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, - /* Micron */ + /* Micron <--> ST Micro */ { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, @@ -1404,6 +1869,12 @@ static const struct flash_info spi_nor_ids[] = { { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + /* Micron */ + { + "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512, + SECT_4K | USE_FSR | SPI_NOR_4B_OPCODES) + }, + /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, @@ -1531,6 +2002,11 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, @@ -1559,450 +2035,208 @@ static const struct flash_info spi_nor_ids[] = { }; static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) -{ - int tmp; - u8 id[SPI_NOR_MAX_ID_LEN]; - const struct flash_info *info; - - tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); - if (tmp < 0) { - dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); - return ERR_PTR(tmp); - } - - for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { - info = &spi_nor_ids[tmp]; - if (info->id_len) { - if (!memcmp(info->id, id, info->id_len)) - return &spi_nor_ids[tmp]; - } - } - dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n", - id[0], id[1], id[2]); - return ERR_PTR(-ENODEV); -} - -static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct spi_nor *nor = mtd_to_spi_nor(mtd); - int ret; - - dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); - - ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); - if (ret) - return ret; - - while (len) { - loff_t addr = from; - - if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) - addr = spi_nor_s3an_addr_convert(nor, addr); - - ret = nor->read(nor, addr, len, buf); - if (ret == 0) { - /* We shouldn't see 0-length reads */ - ret = -EIO; - goto read_err; - } - if (ret < 0) - goto read_err; - - WARN_ON(ret > len); - *retlen += ret; - buf += ret; - from += ret; - len -= ret; - } - ret = 0; - -read_err: - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); - return ret; -} - -static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct spi_nor *nor = mtd_to_spi_nor(mtd); - size_t actual; - int ret; - - dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); - - ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); - if (ret) - return ret; - - write_enable(nor); - - nor->sst_write_second = false; - - actual = to % 2; - /* Start write from odd address. */ - if (actual) { - nor->program_opcode = SPINOR_OP_BP; - - /* write one byte. */ - ret = nor->write(nor, to, 1, buf); - if (ret < 0) - goto sst_write_err; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", - (int)ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto sst_write_err; - } - to += actual; - - /* Write out most of the data here. */ - for (; actual < len - 1; actual += 2) { - nor->program_opcode = SPINOR_OP_AAI_WP; - - /* write two bytes. */ - ret = nor->write(nor, to, 2, buf + actual); - if (ret < 0) - goto sst_write_err; - WARN(ret != 2, "While writing 2 bytes written %i bytes\n", - (int)ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto sst_write_err; - to += 2; - nor->sst_write_second = true; - } - nor->sst_write_second = false; - - write_disable(nor); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto sst_write_err; - - /* Write out trailing byte if it exists. */ - if (actual != len) { - write_enable(nor); - - nor->program_opcode = SPINOR_OP_BP; - ret = nor->write(nor, to, 1, buf + actual); - if (ret < 0) - goto sst_write_err; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", - (int)ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto sst_write_err; - write_disable(nor); - actual += 1; - } -sst_write_err: - *retlen += actual; - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); - return ret; -} - -/* - * Write an address range to the nor chip. Data must be written in - * FLASH_PAGESIZE chunks. The address range may be any size provided - * it is within the physical boundaries. - */ -static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct spi_nor *nor = mtd_to_spi_nor(mtd); - size_t page_offset, page_remain, i; - ssize_t ret; - - dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); - - ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); - if (ret) - return ret; - - for (i = 0; i < len; ) { - ssize_t written; - loff_t addr = to + i; - - /* - * If page_size is a power of two, the offset can be quickly - * calculated with an AND operation. On the other cases we - * need to do a modulus operation (more expensive). - * Power of two numbers have only one bit set and we can use - * the instruction hweight32 to detect if we need to do a - * modulus (do_div()) or not. - */ - if (hweight32(nor->page_size) == 1) { - page_offset = addr & (nor->page_size - 1); - } else { - uint64_t aux = addr; - - page_offset = do_div(aux, nor->page_size); - } - /* the size of data remaining on the first page */ - page_remain = min_t(size_t, - nor->page_size - page_offset, len - i); - - if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) - addr = spi_nor_s3an_addr_convert(nor, addr); - - write_enable(nor); - ret = nor->write(nor, addr, page_remain, buf + i); - if (ret < 0) - goto write_err; - written = ret; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto write_err; - *retlen += written; - i += written; - } - -write_err: - spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); - return ret; -} - -/** - * macronix_quad_enable() - set QE bit in Status Register. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Status Register. - * - * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int macronix_quad_enable(struct spi_nor *nor) -{ - int ret, val; - - val = read_sr(nor); - if (val < 0) - return val; - if (val & SR_QUAD_EN_MX) - return 0; - - write_enable(nor); - - write_sr(nor, val | SR_QUAD_EN_MX); - - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; - - ret = read_sr(nor); - if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { - dev_err(nor->dev, "Macronix Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} - -/* - * Write status Register and configuration register with 2 bytes - * The first byte will be written to the status register, while the - * second byte will be written to the configuration register. - * Return negative if error occurred. - */ -static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) -{ - int ret; - - write_enable(nor); +{ + int tmp; + u8 id[SPI_NOR_MAX_ID_LEN]; + const struct flash_info *info; - ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); - if (ret < 0) { - dev_err(nor->dev, - "error while writing configuration register\n"); - return -EINVAL; + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + if (tmp < 0) { + dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); } - ret = spi_nor_wait_till_ready(nor); - if (ret) { - dev_err(nor->dev, - "timeout while writing configuration register\n"); - return ret; + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { + info = &spi_nor_ids[tmp]; + if (info->id_len) { + if (!memcmp(info->id, id, info->id_len)) + return &spi_nor_ids[tmp]; + } } - - return 0; + dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n", + id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); } -/** - * spansion_quad_enable() - set QE bit in Configuraiton Register. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function is kept for legacy purpose because it has been used for a - * long time without anybody complaining but it should be considered as - * deprecated and maybe buggy. - * First, this function doesn't care about the previous values of the Status - * and Configuration Registers when it sets the QE bit (bit 1) in the - * Configuration Register: all other bits are cleared, which may have unwanted - * side effects like removing some block protections. - * Secondly, it uses the Read Configuration Register (35h) instruction though - * some very old and few memories don't support this instruction. If a pull-up - * resistor is present on the MISO/IO1 line, we might still be able to pass the - * "read back" test because the QSPI memory doesn't recognize the command, - * so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int spansion_quad_enable(struct spi_nor *nor) +static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { - u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN}; + struct spi_nor *nor = mtd_to_spi_nor(mtd); int ret; - ret = write_sr_cr(nor, sr_cr); + dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); if (ret) return ret; - /* read back and check it */ - ret = read_cr(nor); - if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { - dev_err(nor->dev, "Spansion Quad bit not set\n"); - return -EINVAL; - } + while (len) { + loff_t addr = from; - return 0; -} + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) + addr = spi_nor_s3an_addr_convert(nor, addr); -/** - * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function should be used with QSPI memories not supporting the Read - * Configuration Register (35h) instruction. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) -{ - u8 sr_cr[2]; - int ret; + ret = nor->read(nor, addr, len, buf); + if (ret == 0) { + /* We shouldn't see 0-length reads */ + ret = -EIO; + goto read_err; + } + if (ret < 0) + goto read_err; - /* Keep the current value of the Status Register. */ - ret = read_sr(nor); - if (ret < 0) { - dev_err(nor->dev, "error while reading status register\n"); - return -EINVAL; + WARN_ON(ret > len); + *retlen += ret; + buf += ret; + from += ret; + len -= ret; } - sr_cr[0] = ret; - sr_cr[1] = CR_QUAD_EN_SPAN; + ret = 0; - return write_sr_cr(nor, sr_cr); +read_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); + return ret; } -/** - * spansion_read_cr_quad_enable() - set QE bit in Configuration Register. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function should be used with QSPI memories supporting the Read - * Configuration Register (35h) instruction. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int spansion_read_cr_quad_enable(struct spi_nor *nor) +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { - struct device *dev = nor->dev; - u8 sr_cr[2]; + struct spi_nor *nor = mtd_to_spi_nor(mtd); + size_t actual; int ret; - /* Check current Quad Enable bit value. */ - ret = read_cr(nor); - if (ret < 0) { - dev_err(dev, "error while reading configuration register\n"); - return -EINVAL; - } + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); - if (ret & CR_QUAD_EN_SPAN) - return 0; + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; - sr_cr[1] = ret | CR_QUAD_EN_SPAN; + write_enable(nor); - /* Keep the current value of the Status Register. */ - ret = read_sr(nor); - if (ret < 0) { - dev_err(dev, "error while reading status register\n"); - return -EINVAL; + nor->sst_write_second = false; + + actual = to % 2; + /* Start write from odd address. */ + if (actual) { + nor->program_opcode = SPINOR_OP_BP; + + /* write one byte. */ + ret = nor->write(nor, to, 1, buf); + if (ret < 0) + goto sst_write_err; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", + (int)ret); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto sst_write_err; } - sr_cr[0] = ret; + to += actual; - ret = write_sr_cr(nor, sr_cr); - if (ret) - return ret; + /* Write out most of the data here. */ + for (; actual < len - 1; actual += 2) { + nor->program_opcode = SPINOR_OP_AAI_WP; - /* Read back and check it. */ - ret = read_cr(nor); - if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { - dev_err(nor->dev, "Spansion Quad bit not set\n"); - return -EINVAL; + /* write two bytes. */ + ret = nor->write(nor, to, 2, buf + actual); + if (ret < 0) + goto sst_write_err; + WARN(ret != 2, "While writing 2 bytes written %i bytes\n", + (int)ret); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto sst_write_err; + to += 2; + nor->sst_write_second = true; } + nor->sst_write_second = false; - return 0; + write_disable(nor); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto sst_write_err; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + write_enable(nor); + + nor->program_opcode = SPINOR_OP_BP; + ret = nor->write(nor, to, 1, buf + actual); + if (ret < 0) + goto sst_write_err; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", + (int)ret); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto sst_write_err; + write_disable(nor); + actual += 1; + } +sst_write_err: + *retlen += actual; + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return ret; } -/** - * sr2_bit7_quad_enable() - set QE bit in Status Register 2. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Status Register 2. - * - * This is one of the procedures to set the QE bit described in the SFDP - * (JESD216 rev B) specification but no manufacturer using this procedure has - * been identified yet, hence the name of the function. - * - * Return: 0 on success, -errno otherwise. +/* + * Write an address range to the nor chip. Data must be written in + * FLASH_PAGESIZE chunks. The address range may be any size provided + * it is within the physical boundaries. */ -static int sr2_bit7_quad_enable(struct spi_nor *nor) +static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { - u8 sr2; - int ret; + struct spi_nor *nor = mtd_to_spi_nor(mtd); + size_t page_offset, page_remain, i; + ssize_t ret; - /* Check current Quad Enable bit value. */ - ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); if (ret) return ret; - if (sr2 & SR2_QUAD_EN_BIT7) - return 0; - /* Update the Quad Enable bit. */ - sr2 |= SR2_QUAD_EN_BIT7; + for (i = 0; i < len; ) { + ssize_t written; + loff_t addr = to + i; - write_enable(nor); + /* + * If page_size is a power of two, the offset can be quickly + * calculated with an AND operation. On the other cases we + * need to do a modulus operation (more expensive). + * Power of two numbers have only one bit set and we can use + * the instruction hweight32 to detect if we need to do a + * modulus (do_div()) or not. + */ + if (hweight32(nor->page_size) == 1) { + page_offset = addr & (nor->page_size - 1); + } else { + uint64_t aux = addr; - ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1); - if (ret < 0) { - dev_err(nor->dev, "error while writing status register 2\n"); - return -EINVAL; - } + page_offset = do_div(aux, nor->page_size); + } + /* the size of data remaining on the first page */ + page_remain = min_t(size_t, + nor->page_size - page_offset, len - i); - ret = spi_nor_wait_till_ready(nor); - if (ret < 0) { - dev_err(nor->dev, "timeout while writing status register 2\n"); - return ret; - } + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) + addr = spi_nor_s3an_addr_convert(nor, addr); - /* Read back and check it. */ - ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); - if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) { - dev_err(nor->dev, "SR2 Quad bit not set\n"); - return -EINVAL; + write_enable(nor); + ret = nor->write(nor, addr, page_remain, buf + i); + if (ret < 0) + goto write_err; + written = ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto write_err; + *retlen += written; + i += written; } - return 0; +write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return ret; } static int spi_nor_check(struct spi_nor *nor) @@ -2016,7 +2250,7 @@ static int spi_nor_check(struct spi_nor *nor) return 0; } -static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) +static int s3an_nor_scan(struct spi_nor *nor) { int ret; u8 val; @@ -2047,7 +2281,7 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) /* Flash in Power of 2 mode */ nor->page_size = (nor->page_size == 264) ? 256 : 512; nor->mtd.writebufsize = nor->page_size; - nor->mtd.size = 8 * nor->page_size * info->n_sectors; + nor->mtd.size = 8 * nor->page_size * nor->info->n_sectors; nor->mtd.erasesize = 8 * nor->page_size; } else { /* Flash in Default addressing mode */ @@ -2057,71 +2291,6 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) return 0; } -struct spi_nor_read_command { - u8 num_mode_clocks; - u8 num_wait_states; - u8 opcode; - enum spi_nor_protocol proto; -}; - -struct spi_nor_pp_command { - u8 opcode; - enum spi_nor_protocol proto; -}; - -enum spi_nor_read_command_index { - SNOR_CMD_READ, - SNOR_CMD_READ_FAST, - SNOR_CMD_READ_1_1_1_DTR, - - /* Dual SPI */ - SNOR_CMD_READ_1_1_2, - SNOR_CMD_READ_1_2_2, - SNOR_CMD_READ_2_2_2, - SNOR_CMD_READ_1_2_2_DTR, - - /* Quad SPI */ - SNOR_CMD_READ_1_1_4, - SNOR_CMD_READ_1_4_4, - SNOR_CMD_READ_4_4_4, - SNOR_CMD_READ_1_4_4_DTR, - - /* Octo SPI */ - SNOR_CMD_READ_1_1_8, - SNOR_CMD_READ_1_8_8, - SNOR_CMD_READ_8_8_8, - SNOR_CMD_READ_1_8_8_DTR, - - SNOR_CMD_READ_MAX -}; - -enum spi_nor_pp_command_index { - SNOR_CMD_PP, - - /* Quad SPI */ - SNOR_CMD_PP_1_1_4, - SNOR_CMD_PP_1_4_4, - SNOR_CMD_PP_4_4_4, - - /* Octo SPI */ - SNOR_CMD_PP_1_1_8, - SNOR_CMD_PP_1_8_8, - SNOR_CMD_PP_8_8_8, - - SNOR_CMD_PP_MAX -}; - -struct spi_nor_flash_parameter { - u64 size; - u32 page_size; - - struct spi_nor_hwcaps hwcaps; - struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; - struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; - - int (*quad_enable)(struct spi_nor *nor); -}; - static void spi_nor_set_read_settings(struct spi_nor_read_command *read, u8 num_mode_clocks, @@ -2144,6 +2313,57 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, pp->proto = proto; } +static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + if (table[i][0] == (int)hwcaps) + return table[i][1]; + + return -EINVAL; +} + +static int spi_nor_hwcaps_read2cmd(u32 hwcaps) +{ + static const int hwcaps_read2cmd[][2] = { + { SNOR_HWCAPS_READ, SNOR_CMD_READ }, + { SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST }, + { SNOR_HWCAPS_READ_1_1_1_DTR, SNOR_CMD_READ_1_1_1_DTR }, + { SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 }, + { SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 }, + { SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 }, + { SNOR_HWCAPS_READ_1_2_2_DTR, SNOR_CMD_READ_1_2_2_DTR }, + { SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 }, + { SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 }, + { SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 }, + { SNOR_HWCAPS_READ_1_4_4_DTR, SNOR_CMD_READ_1_4_4_DTR }, + { SNOR_HWCAPS_READ_1_1_8, SNOR_CMD_READ_1_1_8 }, + { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 }, + { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 }, + { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR }, + }; + + return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd, + ARRAY_SIZE(hwcaps_read2cmd)); +} + +static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) +{ + static const int hwcaps_pp2cmd[][2] = { + { SNOR_HWCAPS_PP, SNOR_CMD_PP }, + { SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 }, + { SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 }, + { SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 }, + { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 }, + { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 }, + { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 }, + }; + + return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd, + ARRAY_SIZE(hwcaps_pp2cmd)); +} + /* * Serial Flash Discoverable Parameters (SFDP) parsing. */ @@ -2201,163 +2421,52 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, addr_width = nor->addr_width; read_dummy = nor->read_dummy; - nor->read_opcode = SPINOR_OP_RDSFDP; - nor->addr_width = 3; - nor->read_dummy = 8; - - ret = spi_nor_read_raw(nor, addr, len, buf); - - nor->read_opcode = read_opcode; - nor->addr_width = addr_width; - nor->read_dummy = read_dummy; - - return ret; -} - -/** - * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters. - * @nor: pointer to a 'struct spi_nor' - * @addr: offset in the SFDP area to start reading data from - * @len: number of bytes to read - * @buf: buffer where the SFDP data are copied into - * - * Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not - * guaranteed to be dma-safe. - * - * Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp() - * otherwise. - */ -static int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr, - size_t len, void *buf) -{ - void *dma_safe_buf; - int ret; - - dma_safe_buf = kmalloc(len, GFP_KERNEL); - if (!dma_safe_buf) - return -ENOMEM; - - ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf); - memcpy(buf, dma_safe_buf, len); - kfree(dma_safe_buf); - - return ret; -} - -struct sfdp_parameter_header { - u8 id_lsb; - u8 minor; - u8 major; - u8 length; /* in double words */ - u8 parameter_table_pointer[3]; /* byte address */ - u8 id_msb; -}; - -#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb) -#define SFDP_PARAM_HEADER_PTP(p) \ - (((p)->parameter_table_pointer[2] << 16) | \ - ((p)->parameter_table_pointer[1] << 8) | \ - ((p)->parameter_table_pointer[0] << 0)) - -#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ -#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ - -#define SFDP_SIGNATURE 0x50444653U -#define SFDP_JESD216_MAJOR 1 -#define SFDP_JESD216_MINOR 0 -#define SFDP_JESD216A_MINOR 5 -#define SFDP_JESD216B_MINOR 6 - -struct sfdp_header { - u32 signature; /* Ox50444653U <=> "SFDP" */ - u8 minor; - u8 major; - u8 nph; /* 0-base number of parameter headers */ - u8 unused; - - /* Basic Flash Parameter Table. */ - struct sfdp_parameter_header bfpt_header; -}; - -/* Basic Flash Parameter Table */ - -/* - * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs. - * They are indexed from 1 but C arrays are indexed from 0. - */ -#define BFPT_DWORD(i) ((i) - 1) -#define BFPT_DWORD_MAX 16 - -/* The first version of JESB216 defined only 9 DWORDs. */ -#define BFPT_DWORD_MAX_JESD216 9 - -/* 1st DWORD. */ -#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16) -#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17) -#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17) -#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17) -#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17) -#define BFPT_DWORD1_DTR BIT(19) -#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20) -#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21) -#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22) + nor->read_opcode = SPINOR_OP_RDSFDP; + nor->addr_width = 3; + nor->read_dummy = 8; -/* 5th DWORD. */ -#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0) -#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4) + ret = spi_nor_read_raw(nor, addr, len, buf); -/* 11th DWORD. */ -#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4 -#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4) + nor->read_opcode = read_opcode; + nor->addr_width = addr_width; + nor->read_dummy = read_dummy; -/* 15th DWORD. */ + return ret; +} -/* - * (from JESD216 rev B) - * Quad Enable Requirements (QER): - * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4 - * reads based on instruction. DQ3/HOLD# functions are hold during - * instruction phase. - * - 001b: QE is bit 1 of status register 2. It is set via Write Status with - * two data bytes where bit 1 of the second byte is one. - * [...] - * Writing only one byte to the status register has the side-effect of - * clearing status register 2, including the QE bit. The 100b code is - * used if writing one byte to the status register does not modify - * status register 2. - * - 010b: QE is bit 6 of status register 1. It is set via Write Status with - * one data byte where bit 6 is one. - * [...] - * - 011b: QE is bit 7 of status register 2. It is set via Write status - * register 2 instruction 3Eh with one data byte where bit 7 is one. - * [...] - * The status register 2 is read using instruction 3Fh. - * - 100b: QE is bit 1 of status register 2. It is set via Write Status with - * two data bytes where bit 1 of the second byte is one. - * [...] - * In contrast to the 001b code, writing one byte to the status - * register does not modify status register 2. - * - 101b: QE is bit 1 of status register 2. Status register 1 is read using - * Read Status instruction 05h. Status register2 is read using - * instruction 35h. QE is set via Writ Status instruction 01h with - * two data bytes where bit 1 of the second byte is one. - * [...] +/** + * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters. + * @nor: pointer to a 'struct spi_nor' + * @addr: offset in the SFDP area to start reading data from + * @len: number of bytes to read + * @buf: buffer where the SFDP data are copied into + * + * Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not + * guaranteed to be dma-safe. + * + * Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp() + * otherwise. */ -#define BFPT_DWORD15_QER_MASK GENMASK(22, 20) -#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ -#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) -#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ -#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) -#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) -#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +static int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr, + size_t len, void *buf) +{ + void *dma_safe_buf; + int ret; -struct sfdp_bfpt { - u32 dwords[BFPT_DWORD_MAX]; -}; + dma_safe_buf = kmalloc(len, GFP_KERNEL); + if (!dma_safe_buf) + return -ENOMEM; + + ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf); + memcpy(buf, dma_safe_buf, len); + kfree(dma_safe_buf); + + return ret; +} /* Fast Read settings. */ -static inline void +static void spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read, u16 half, enum spi_nor_protocol proto) @@ -2464,8 +2573,6 @@ static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = { {BFPT_DWORD(9), 16}, }; -static int spi_nor_hwcaps_read2cmd(u32 hwcaps); - /** * spi_nor_set_erase_type() - set a SPI NOR erase type * @erase: pointer to a structure that describes a SPI NOR erase type @@ -2598,6 +2705,19 @@ static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, map->uniform_erase_type = erase_mask; } +static int +spi_nor_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + if (nor->info->fixups && nor->info->fixups->post_bfpt) + return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt, + params); + + return 0; +} + /** * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. * @nor: pointer to a 'struct spi_nor' @@ -2750,7 +2870,8 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* Stop here if not JESD216 rev A or later. */ if (bfpt_header->length < BFPT_DWORD_MAX) - return 0; + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, + params); /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ params->page_size = bfpt.dwords[BFPT_DWORD(11)]; @@ -2785,7 +2906,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, return -EINVAL; } - return 0; + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); } #define SMPT_CMD_ADDRESS_LEN_MASK GENMASK(23, 22) @@ -3091,7 +3212,7 @@ static int spi_nor_parse_smpt(struct spi_nor *nor, /* Read the Sector Map Parameter Table. */ len = smpt_header->length * sizeof(*smpt); - smpt = kzalloc(len, GFP_KERNEL); + smpt = kmalloc(len, GFP_KERNEL); if (!smpt) return -ENOMEM; @@ -3121,6 +3242,191 @@ out: return ret; } +#define SFDP_4BAIT_DWORD_MAX 2 + +struct sfdp_4bait { + /* The hardware capability. */ + u32 hwcaps; + + /* + * The <supported_bit> bit in DWORD1 of the 4BAIT tells us whether + * the associated 4-byte address op code is supported. + */ + u32 supported_bit; +}; + +/** + * spi_nor_parse_4bait() - parse the 4-Byte Address Instruction Table + * @nor: pointer to a 'struct spi_nor'. + * @param_header: pointer to the 'struct sfdp_parameter_header' describing + * the 4-Byte Address Instruction Table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_4bait(struct spi_nor *nor, + const struct sfdp_parameter_header *param_header, + struct spi_nor_flash_parameter *params) +{ + static const struct sfdp_4bait reads[] = { + { SNOR_HWCAPS_READ, BIT(0) }, + { SNOR_HWCAPS_READ_FAST, BIT(1) }, + { SNOR_HWCAPS_READ_1_1_2, BIT(2) }, + { SNOR_HWCAPS_READ_1_2_2, BIT(3) }, + { SNOR_HWCAPS_READ_1_1_4, BIT(4) }, + { SNOR_HWCAPS_READ_1_4_4, BIT(5) }, + { SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) }, + { SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) }, + { SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) }, + }; + static const struct sfdp_4bait programs[] = { + { SNOR_HWCAPS_PP, BIT(6) }, + { SNOR_HWCAPS_PP_1_1_4, BIT(7) }, + { SNOR_HWCAPS_PP_1_4_4, BIT(8) }, + }; + static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = { + { 0u /* not used */, BIT(9) }, + { 0u /* not used */, BIT(10) }, + { 0u /* not used */, BIT(11) }, + { 0u /* not used */, BIT(12) }, + }; + struct spi_nor_pp_command *params_pp = params->page_programs; + struct spi_nor_erase_map *map = &nor->erase_map; + struct spi_nor_erase_type *erase_type = map->erase_type; + u32 *dwords; + size_t len; + u32 addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask; + int i, ret; + + if (param_header->major != SFDP_JESD216_MAJOR || + param_header->length < SFDP_4BAIT_DWORD_MAX) + return -EINVAL; + + /* Read the 4-byte Address Instruction Table. */ + len = sizeof(*dwords) * SFDP_4BAIT_DWORD_MAX; + + /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ + dwords = kmalloc(len, GFP_KERNEL); + if (!dwords) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(param_header); + ret = spi_nor_read_sfdp(nor, addr, len, dwords); + if (ret) + return ret; + + /* Fix endianness of the 4BAIT DWORDs. */ + for (i = 0; i < SFDP_4BAIT_DWORD_MAX; i++) + dwords[i] = le32_to_cpu(dwords[i]); + + /* + * Compute the subset of (Fast) Read commands for which the 4-byte + * version is supported. + */ + discard_hwcaps = 0; + read_hwcaps = 0; + for (i = 0; i < ARRAY_SIZE(reads); i++) { + const struct sfdp_4bait *read = &reads[i]; + + discard_hwcaps |= read->hwcaps; + if ((params->hwcaps.mask & read->hwcaps) && + (dwords[0] & read->supported_bit)) + read_hwcaps |= read->hwcaps; + } + + /* + * Compute the subset of Page Program commands for which the 4-byte + * version is supported. + */ + pp_hwcaps = 0; + for (i = 0; i < ARRAY_SIZE(programs); i++) { + const struct sfdp_4bait *program = &programs[i]; + + /* + * The 4 Byte Address Instruction (Optional) Table is the only + * SFDP table that indicates support for Page Program Commands. + * Bypass the params->hwcaps.mask and consider 4BAIT the biggest + * authority for specifying Page Program support. + */ + discard_hwcaps |= program->hwcaps; + if (dwords[0] & program->supported_bit) + pp_hwcaps |= program->hwcaps; + } + + /* + * Compute the subset of Sector Erase commands for which the 4-byte + * version is supported. + */ + erase_mask = 0; + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + const struct sfdp_4bait *erase = &erases[i]; + + if (dwords[0] & erase->supported_bit) + erase_mask |= BIT(i); + } + + /* Replicate the sort done for the map's erase types in BFPT. */ + erase_mask = spi_nor_sort_erase_mask(map, erase_mask); + + /* + * We need at least one 4-byte op code per read, program and erase + * operation; the .read(), .write() and .erase() hooks share the + * nor->addr_width value. + */ + if (!read_hwcaps || !pp_hwcaps || !erase_mask) + goto out; + + /* + * Discard all operations from the 4-byte instruction set which are + * not supported by this memory. + */ + params->hwcaps.mask &= ~discard_hwcaps; + params->hwcaps.mask |= (read_hwcaps | pp_hwcaps); + + /* Use the 4-byte address instruction set. */ + for (i = 0; i < SNOR_CMD_READ_MAX; i++) { + struct spi_nor_read_command *read_cmd = &params->reads[i]; + + read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode); + } + + /* 4BAIT is the only SFDP table that indicates page program support. */ + if (pp_hwcaps & SNOR_HWCAPS_PP) + spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP], + SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4) + spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_1_4], + SPINOR_OP_PP_1_1_4_4B, + SNOR_PROTO_1_1_4); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_4_4) + spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_4_4], + SPINOR_OP_PP_1_4_4_4B, + SNOR_PROTO_1_4_4); + + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + if (erase_mask & BIT(i)) + erase_type[i].opcode = (dwords[1] >> + erase_type[i].idx * 8) & 0xFF; + else + spi_nor_set_erase_type(&erase_type[i], 0u, 0xFF); + } + + /* + * We set SNOR_F_HAS_4BAIT in order to skip spi_nor_set_4byte_opcodes() + * later because we already did the conversion to 4byte opcodes. Also, + * this latest function implements a legacy quirk for the erase size of + * Spansion memory. However this quirk is no longer needed with new + * SFDP compliant memories. + */ + nor->addr_width = 4; + nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; + + /* fall through */ +out: + kfree(dwords); + return ret; +} + /** * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' @@ -3218,6 +3524,10 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_parse_smpt(nor, param_header); break; + case SFDP_4BAIT_ID: + err = spi_nor_parse_4bait(nor, param_header, params); + break; + default: break; } @@ -3241,17 +3551,17 @@ exit: } static int spi_nor_init_params(struct spi_nor *nor, - const struct flash_info *info, struct spi_nor_flash_parameter *params) { struct spi_nor_erase_map *map = &nor->erase_map; + const struct flash_info *info = nor->info; u8 i, erase_mask; /* Set legacy flash parameters as default. */ memset(params, 0, sizeof(*params)); /* Set SPI NOR sizes. */ - params->size = info->sector_size * info->n_sectors; + params->size = (u64)info->sector_size * info->n_sectors; params->page_size = info->page_size; /* (Fast) Read settings. */ @@ -3316,6 +3626,7 @@ static int spi_nor_init_params(struct spi_nor *nor, params->quad_enable = macronix_quad_enable; break; + case SNOR_MFR_ST: case SNOR_MFR_MICRON: break; @@ -3345,6 +3656,7 @@ static int spi_nor_init_params(struct spi_nor *nor, if (spi_nor_parse_sfdp(nor, &sfdp_params)) { nor->addr_width = 0; + nor->flags &= ~SNOR_F_4B_OPCODES; /* restore previous erase map */ memcpy(&nor->erase_map, &prev_map, sizeof(nor->erase_map)); @@ -3356,57 +3668,6 @@ static int spi_nor_init_params(struct spi_nor *nor, return 0; } -static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size) -{ - size_t i; - - for (i = 0; i < size; i++) - if (table[i][0] == (int)hwcaps) - return table[i][1]; - - return -EINVAL; -} - -static int spi_nor_hwcaps_read2cmd(u32 hwcaps) -{ - static const int hwcaps_read2cmd[][2] = { - { SNOR_HWCAPS_READ, SNOR_CMD_READ }, - { SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST }, - { SNOR_HWCAPS_READ_1_1_1_DTR, SNOR_CMD_READ_1_1_1_DTR }, - { SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 }, - { SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 }, - { SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 }, - { SNOR_HWCAPS_READ_1_2_2_DTR, SNOR_CMD_READ_1_2_2_DTR }, - { SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 }, - { SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 }, - { SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 }, - { SNOR_HWCAPS_READ_1_4_4_DTR, SNOR_CMD_READ_1_4_4_DTR }, - { SNOR_HWCAPS_READ_1_1_8, SNOR_CMD_READ_1_1_8 }, - { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 }, - { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 }, - { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR }, - }; - - return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd, - ARRAY_SIZE(hwcaps_read2cmd)); -} - -static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) -{ - static const int hwcaps_pp2cmd[][2] = { - { SNOR_HWCAPS_PP, SNOR_CMD_PP }, - { SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 }, - { SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 }, - { SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 }, - { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 }, - { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 }, - { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 }, - }; - - return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd, - ARRAY_SIZE(hwcaps_pp2cmd)); -} - static int spi_nor_select_read(struct spi_nor *nor, const struct spi_nor_flash_parameter *params, u32 shared_hwcaps) @@ -3559,7 +3820,7 @@ static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) return 0; } -static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, +static int spi_nor_setup(struct spi_nor *nor, const struct spi_nor_flash_parameter *params, const struct spi_nor_hwcaps *hwcaps) { @@ -3602,7 +3863,7 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, } /* Select the Sector Erase command. */ - err = spi_nor_select_erase(nor, info->sector_size); + err = spi_nor_select_erase(nor, nor->info->sector_size); if (err) { dev_err(nor->dev, "can't select erase settings supported by both the SPI controller and memory.\n"); @@ -3645,9 +3906,7 @@ static int spi_nor_init(struct spi_nor *nor) } } - if ((nor->addr_width == 4) && - (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) && - !(nor->info->flags & SPI_NOR_4B_OPCODES)) { + if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) { /* * If the RESET# pin isn't hooked up properly, or the system * otherwise doesn't perform a reset command in the boot @@ -3657,7 +3916,7 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - set_4byte(nor, nor->info, 1); + set_4byte(nor, true); } return 0; @@ -3679,14 +3938,24 @@ static void spi_nor_resume(struct mtd_info *mtd) void spi_nor_restore(struct spi_nor *nor) { /* restore the addressing mode */ - if ((nor->addr_width == 4) && - (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) && - !(nor->info->flags & SPI_NOR_4B_OPCODES) && - (nor->flags & SNOR_F_BROKEN_RESET)) - set_4byte(nor, nor->info, 0); + if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && + nor->flags & SNOR_F_BROKEN_RESET) + set_4byte(nor, false); } EXPORT_SYMBOL_GPL(spi_nor_restore); +static const struct flash_info *spi_nor_match_id(const char *name) +{ + const struct flash_info *id = spi_nor_ids; + + while (id->name) { + if (!strcmp(name, id->name)) + return id; + id++; + } + return NULL; +} + int spi_nor_scan(struct spi_nor *nor, const char *name, const struct spi_nor_hwcaps *hwcaps) { @@ -3739,6 +4008,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } } + nor->info = info; + mutex_init(&nor->lock); /* @@ -3750,7 +4021,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, nor->flags |= SNOR_F_READY_XSR_RDY; /* Parse the Serial Flash Discoverable Parameters table. */ - ret = spi_nor_init_params(nor, info, &params); + ret = spi_nor_init_params(nor, &params); if (ret) return ret; @@ -3766,8 +4037,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->_resume = spi_nor_resume; /* NOR protection support for STmicro/Micron chips and similar */ - if (JEDEC_MFR(info) == SNOR_MFR_MICRON || - info->flags & SPI_NOR_HAS_LOCK) { + if (JEDEC_MFR(info) == SNOR_MFR_ST || + JEDEC_MFR(info) == SNOR_MFR_MICRON || + info->flags & SPI_NOR_HAS_LOCK) { nor->flash_lock = stm_lock; nor->flash_unlock = stm_unlock; nor->flash_is_locked = stm_is_locked; @@ -3826,7 +4098,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, * - set the SPI protocols for register and memory accesses. * - set the Quad Enable bit if needed (required by SPI x-y-4 protos). */ - ret = spi_nor_setup(nor, info, &params, hwcaps); + ret = spi_nor_setup(nor, &params, hwcaps); if (ret) return ret; @@ -3837,13 +4109,18 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } else if (mtd->size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ nor->addr_width = 4; - if (JEDEC_MFR(info) == SNOR_MFR_SPANSION || - info->flags & SPI_NOR_4B_OPCODES) - spi_nor_set_4byte_opcodes(nor, info); } else { nor->addr_width = 3; } + if (info->flags & SPI_NOR_4B_OPCODES || + (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M)) + nor->flags |= SNOR_F_4B_OPCODES; + + if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && + !(nor->flags & SNOR_F_HAS_4BAIT)) + spi_nor_set_4byte_opcodes(nor); + if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { dev_err(dev, "address width is too large: %u\n", nor->addr_width); @@ -3851,13 +4128,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } if (info->flags & SPI_S3AN) { - ret = s3an_nor_scan(info, nor); + ret = s3an_nor_scan(nor); if (ret) return ret; } /* Send all the required SPI flash commands to initialize device */ - nor->info = info; ret = spi_nor_init(nor); if (ret) return ret; @@ -3885,19 +4161,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } EXPORT_SYMBOL_GPL(spi_nor_scan); -static const struct flash_info *spi_nor_match_id(const char *name) -{ - const struct flash_info *id = spi_nor_ids; - - while (id->name) { - if (!strcmp(name, id->name)) - return id; - id++; - } - return NULL; -} - -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>"); MODULE_AUTHOR("Mike Lavender"); MODULE_DESCRIPTION("framework for SPI NOR"); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig @@ -78,8 +78,6 @@ source "drivers/staging/goldfish/Kconfig" source "drivers/staging/netlogic/Kconfig" -source "drivers/staging/mt29f_spinand/Kconfig" - source "drivers/staging/gs_fpgaboot/Kconfig" source "drivers/staging/unisys/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_STAGING_BOARD) += board/ obj-$(CONFIG_LTE_GDM724X) += gdm724x/ obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ obj-$(CONFIG_GOLDFISH) += goldfish/ -obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ obj-$(CONFIG_UNISYSSPAR) += unisys/ obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/ diff --git a/drivers/staging/mt29f_spinand/Kconfig b/drivers/staging/mt29f_spinand/Kconfig @@ -1,16 +0,0 @@ -config MTD_SPINAND_MT29F - tristate "SPINAND Device Support for Micron" - depends on MTD_NAND && SPI - help - This enables support for accessing Micron SPI NAND flash - devices. - If you have Micron SPI NAND chip say yes. - - If unsure, say no here. - -config MTD_SPINAND_ONDIEECC - bool "Use SPINAND internal ECC" - depends on MTD_SPINAND_MT29F - help - Internal ECC. - Enables Hardware ECC support for Micron SPI NAND. diff --git a/drivers/staging/mt29f_spinand/Makefile b/drivers/staging/mt29f_spinand/Makefile @@ -1 +0,0 @@ -obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand.o diff --git a/drivers/staging/mt29f_spinand/TODO b/drivers/staging/mt29f_spinand/TODO @@ -1,13 +0,0 @@ -TODO: - - Tested on XLP platform, needs to be tested on other platforms. - - Checkpatch.pl cleanups - - Sparce fixes. - - Clean up coding style to meet kernel standard. - -Please send patches -To: -Kamlakant Patel <kamlakant.patel@broadcom.com> -Cc: -Greg Kroah-Hartman <gregkh@linuxfoundation.org> -Mona Anonuevo <manonuevo@micron.com> -linux-mtd@lists.infradead.org diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -1,980 +0,0 @@ -/* - * Copyright (c) 2003-2013 Broadcom Corporation - * - * Copyright (c) 2009-2010 Micron Technology, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> -#include <linux/mtd/rawnand.h> -#include <linux/spi/spi.h> - -#include "mt29f_spinand.h" - -#define BUFSIZE (10 * 64 * 2048) -#define CACHE_BUF 2112 -/* - * OOB area specification layout: Total 32 available free bytes. - */ - -static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct spinand_info *info = nand_get_controller_data(chip); - struct spinand_state *state = info->priv; - - return state; -} - -#ifdef CONFIG_MTD_SPINAND_ONDIEECC -static int enable_hw_ecc; -static int enable_read_hw_ecc; - -static int spinand_ooblayout_64_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 3) - return -ERANGE; - - oobregion->offset = (section * 16) + 1; - oobregion->length = 6; - - return 0; -} - -static int spinand_ooblayout_64_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 3) - return -ERANGE; - - oobregion->offset = (section * 16) + 8; - oobregion->length = 8; - - return 0; -} - -static const struct mtd_ooblayout_ops spinand_oob_64_ops = { - .ecc = spinand_ooblayout_64_ecc, - .free = spinand_ooblayout_64_free, -}; -#endif - -/** - * spinand_cmd - process a command to send to the SPI Nand - * Description: - * Set up the command buffer to send to the SPI controller. - * The command buffer has to initialized to 0. - */ - -static int spinand_cmd(struct spi_device *spi, struct spinand_cmd *cmd) -{ - struct spi_message message; - struct spi_transfer x[4]; - u8 dummy = 0xff; - - spi_message_init(&message); - memset(x, 0, sizeof(x)); - - x[0].len = 1; - x[0].tx_buf = &cmd->cmd; - spi_message_add_tail(&x[0], &message); - - if (cmd->n_addr) { - x[1].len = cmd->n_addr; - x[1].tx_buf = cmd->addr; - spi_message_add_tail(&x[1], &message); - } - - if (cmd->n_dummy) { - x[2].len = cmd->n_dummy; - x[2].tx_buf = &dummy; - spi_message_add_tail(&x[2], &message); - } - - if (cmd->n_tx) { - x[3].len = cmd->n_tx; - x[3].tx_buf = cmd->tx_buf; - spi_message_add_tail(&x[3], &message); - } - - if (cmd->n_rx) { - x[3].len = cmd->n_rx; - x[3].rx_buf = cmd->rx_buf; - spi_message_add_tail(&x[3], &message); - } - - return spi_sync(spi, &message); -} - -/** - * spinand_read_id - Read SPI Nand ID - * Description: - * read two ID bytes from the SPI Nand device - */ -static int spinand_read_id(struct spi_device *spi_nand, u8 *id) -{ - int retval; - u8 nand_id[3]; - struct spinand_cmd cmd = {0}; - - cmd.cmd = CMD_READ_ID; - cmd.n_rx = 3; - cmd.rx_buf = &nand_id[0]; - - retval = spinand_cmd(spi_nand, &cmd); - if (retval < 0) { - dev_err(&spi_nand->dev, "error %d reading id\n", retval); - return retval; - } - id[0] = nand_id[1]; - id[1] = nand_id[2]; - return retval; -} - -/** - * spinand_read_status - send command 0xf to the SPI Nand status register - * Description: - * After read, write, or erase, the Nand device is expected to set the - * busy status. - * This function is to allow reading the status of the command: read, - * write, and erase. - * Once the status turns to be ready, the other status bits also are - * valid status bits. - */ -static int spinand_read_status(struct spi_device *spi_nand, u8 *status) -{ - struct spinand_cmd cmd = {0}; - int ret; - - cmd.cmd = CMD_READ_REG; - cmd.n_addr = 1; - cmd.addr[0] = REG_STATUS; - cmd.n_rx = 1; - cmd.rx_buf = status; - - ret = spinand_cmd(spi_nand, &cmd); - if (ret < 0) - dev_err(&spi_nand->dev, "err: %d read status register\n", ret); - - return ret; -} - -#define MAX_WAIT_JIFFIES (40 * HZ) -static int wait_till_ready(struct spi_device *spi_nand) -{ - unsigned long deadline; - int retval; - u8 stat = 0; - - deadline = jiffies + MAX_WAIT_JIFFIES; - do { - retval = spinand_read_status(spi_nand, &stat); - if (retval < 0) - return -1; - if (!(stat & 0x1)) - break; - - cond_resched(); - } while (!time_after_eq(jiffies, deadline)); - - if ((stat & 0x1) == 0) - return 0; - - return -1; -} - -/** - * spinand_get_otp - send command 0xf to read the SPI Nand OTP register - * Description: - * There is one bit( bit 0x10 ) to set or to clear the internal ECC. - * Enable chip internal ECC, set the bit to 1 - * Disable chip internal ECC, clear the bit to 0 - */ -static int spinand_get_otp(struct spi_device *spi_nand, u8 *otp) -{ - struct spinand_cmd cmd = {0}; - int retval; - - cmd.cmd = CMD_READ_REG; - cmd.n_addr = 1; - cmd.addr[0] = REG_OTP; - cmd.n_rx = 1; - cmd.rx_buf = otp; - - retval = spinand_cmd(spi_nand, &cmd); - if (retval < 0) - dev_err(&spi_nand->dev, "error %d get otp\n", retval); - return retval; -} - -/** - * spinand_set_otp - send command 0x1f to write the SPI Nand OTP register - * Description: - * There is one bit( bit 0x10 ) to set or to clear the internal ECC. - * Enable chip internal ECC, set the bit to 1 - * Disable chip internal ECC, clear the bit to 0 - */ -static int spinand_set_otp(struct spi_device *spi_nand, u8 *otp) -{ - int retval; - struct spinand_cmd cmd = {0}; - - cmd.cmd = CMD_WRITE_REG; - cmd.n_addr = 1; - cmd.addr[0] = REG_OTP; - cmd.n_tx = 1; - cmd.tx_buf = otp; - - retval = spinand_cmd(spi_nand, &cmd); - if (retval < 0) - dev_err(&spi_nand->dev, "error %d set otp\n", retval); - - return retval; -} - -#ifdef CONFIG_MTD_SPINAND_ONDIEECC -/** - * spinand_enable_ecc - send command 0x1f to write the SPI Nand OTP register - * Description: - * There is one bit( bit 0x10 ) to set or to clear the internal ECC. - * Enable chip internal ECC, set the bit to 1 - * Disable chip internal ECC, clear the bit to 0 - */ -static int spinand_enable_ecc(struct spi_device *spi_nand) -{ - int retval; - u8 otp = 0; - - retval = spinand_get_otp(spi_nand, &otp); - if (retval < 0) - return retval; - - if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) - return 0; - otp |= OTP_ECC_MASK; - retval = spinand_set_otp(spi_nand, &otp); - if (retval < 0) - return retval; - return spinand_get_otp(spi_nand, &otp); -} -#endif - -static int spinand_disable_ecc(struct spi_device *spi_nand) -{ - int retval; - u8 otp = 0; - - retval = spinand_get_otp(spi_nand, &otp); - if (retval < 0) - return retval; - - if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) { - otp &= ~OTP_ECC_MASK; - retval = spinand_set_otp(spi_nand, &otp); - if (retval < 0) - return retval; - return spinand_get_otp(spi_nand, &otp); - } - return 0; -} - -/** - * spinand_write_enable - send command 0x06 to enable write or erase the - * Nand cells - * Description: - * Before write and erase the Nand cells, the write enable has to be set. - * After the write or erase, the write enable bit is automatically - * cleared (status register bit 2) - * Set the bit 2 of the status register has the same effect - */ -static int spinand_write_enable(struct spi_device *spi_nand) -{ - struct spinand_cmd cmd = {0}; - - cmd.cmd = CMD_WR_ENABLE; - return spinand_cmd(spi_nand, &cmd); -} - -static int spinand_read_page_to_cache(struct spi_device *spi_nand, u16 page_id) -{ - struct spinand_cmd cmd = {0}; - u16 row; - - row = page_id; - cmd.cmd = CMD_READ; - cmd.n_addr = 3; - cmd.addr[0] = (u8)((row & 0xff0000) >> 16); - cmd.addr[1] = (u8)((row & 0xff00) >> 8); - cmd.addr[2] = (u8)(row & 0x00ff); - - return spinand_cmd(spi_nand, &cmd); -} - -/** - * spinand_read_from_cache - send command 0x03 to read out the data from the - * cache register (2112 bytes max) - * Description: - * The read can specify 1 to 2112 bytes of data read at the corresponding - * locations. - * No tRd delay. - */ -static int spinand_read_from_cache(struct spi_device *spi_nand, u16 page_id, - u16 byte_id, u16 len, u8 *rbuf) -{ - struct spinand_cmd cmd = {0}; - u16 column; - - column = byte_id; - cmd.cmd = CMD_READ_RDM; - cmd.n_addr = 3; - cmd.addr[0] = (u8)((column & 0xff00) >> 8); - cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4); - cmd.addr[1] = (u8)(column & 0x00ff); - cmd.addr[2] = (u8)(0xff); - cmd.n_dummy = 0; - cmd.n_rx = len; - cmd.rx_buf = rbuf; - - return spinand_cmd(spi_nand, &cmd); -} - -/** - * spinand_read_page - read a page - * @page_id: the physical page number - * @offset: the location from 0 to 2111 - * @len: number of bytes to read - * @rbuf: read buffer to hold @len bytes - * - * Description: - * The read includes two commands to the Nand - 0x13 and 0x03 commands - * Poll to read status to wait for tRD time. - */ -static int spinand_read_page(struct spi_device *spi_nand, u16 page_id, - u16 offset, u16 len, u8 *rbuf) -{ - int ret; - u8 status = 0; - -#ifdef CONFIG_MTD_SPINAND_ONDIEECC - if (enable_read_hw_ecc) { - if (spinand_enable_ecc(spi_nand) < 0) - dev_err(&spi_nand->dev, "enable HW ECC failed!"); - } -#endif - ret = spinand_read_page_to_cache(spi_nand, page_id); - if (ret < 0) - return ret; - - if (wait_till_ready(spi_nand)) - dev_err(&spi_nand->dev, "WAIT timedout!!!\n"); - - while (1) { - ret = spinand_read_status(spi_nand, &status); - if (ret < 0) { - dev_err(&spi_nand->dev, - "err %d read status register\n", ret); - return ret; - } - - if ((status & STATUS_OIP_MASK) == STATUS_READY) { - if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) { - dev_err(&spi_nand->dev, "ecc error, page=%d\n", - page_id); - return 0; - } - break; - } - } - - ret = spinand_read_from_cache(spi_nand, page_id, offset, len, rbuf); - if (ret < 0) { - dev_err(&spi_nand->dev, "read from cache failed!!\n"); - return ret; - } - -#ifdef CONFIG_MTD_SPINAND_ONDIEECC - if (enable_read_hw_ecc) { - ret = spinand_disable_ecc(spi_nand); - if (ret < 0) { - dev_err(&spi_nand->dev, "disable ecc failed!!\n"); - return ret; - } - enable_read_hw_ecc = 0; - } -#endif - return ret; -} - -/** - * spinand_program_data_to_cache - write a page to cache - * @byte_id: the location to write to the cache - * @len: number of bytes to write - * @wbuf: write buffer holding @len bytes - * - * Description: - * The write command used here is 0x84--indicating that the cache is - * not cleared first. - * Since it is writing the data to cache, there is no tPROG time. - */ -static int spinand_program_data_to_cache(struct spi_device *spi_nand, - u16 page_id, u16 byte_id, - u16 len, u8 *wbuf) -{ - struct spinand_cmd cmd = {0}; - u16 column; - - column = byte_id; - cmd.cmd = CMD_PROG_PAGE_CLRCACHE; - cmd.n_addr = 2; - cmd.addr[0] = (u8)((column & 0xff00) >> 8); - cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4); - cmd.addr[1] = (u8)(column & 0x00ff); - cmd.n_tx = len; - cmd.tx_buf = wbuf; - - return spinand_cmd(spi_nand, &cmd); -} - -/** - * spinand_program_execute - write a page from cache to the Nand array - * @page_id: the physical page location to write the page. - * - * Description: - * The write command used here is 0x10--indicating the cache is writing to - * the Nand array. - * Need to wait for tPROG time to finish the transaction. - */ -static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id) -{ - struct spinand_cmd cmd = {0}; - u16 row; - - row = page_id; - cmd.cmd = CMD_PROG_PAGE_EXC; - cmd.n_addr = 3; - cmd.addr[0] = (u8)((row & 0xff0000) >> 16); - cmd.addr[1] = (u8)((row & 0xff00) >> 8); - cmd.addr[2] = (u8)(row & 0x00ff); - - return spinand_cmd(spi_nand, &cmd); -} - -/** - * spinand_program_page - write a page - * @page_id: the physical page location to write the page. - * @offset: the location from the cache starting from 0 to 2111 - * @len: the number of bytes to write - * @buf: the buffer holding @len bytes - * - * Description: - * The commands used here are 0x06, 0x84, and 0x10--indicating that - * the write enable is first sent, the write cache command, and the - * write execute command. - * Poll to wait for the tPROG time to finish the transaction. - */ -static int spinand_program_page(struct spi_device *spi_nand, - u16 page_id, u16 offset, u16 len, u8 *buf) -{ - int retval; - u8 status = 0; - u8 *wbuf; -#ifdef CONFIG_MTD_SPINAND_ONDIEECC - unsigned int i, j; - - wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL); - if (!wbuf) - return -ENOMEM; - - enable_read_hw_ecc = 1; - retval = spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf); - if (retval < 0) { - dev_err(&spi_nand->dev, "ecc error on read page!!!\n"); - return retval; - } - - for (i = offset, j = 0; i < len; i++, j++) - wbuf[i] &= buf[j]; - - if (enable_hw_ecc) { - retval = spinand_enable_ecc(spi_nand); - if (retval < 0) { - dev_err(&spi_nand->dev, "enable ecc failed!!\n"); - return retval; - } - } -#else - wbuf = buf; -#endif - retval = spinand_write_enable(spi_nand); - if (retval < 0) { - dev_err(&spi_nand->dev, "write enable failed!!\n"); - return retval; - } - if (wait_till_ready(spi_nand)) - dev_err(&spi_nand->dev, "wait timedout!!!\n"); - - retval = spinand_program_data_to_cache(spi_nand, page_id, - offset, len, wbuf); - if (retval < 0) - return retval; - retval = spinand_program_execute(spi_nand, page_id); - if (retval < 0) - return retval; - while (1) { - retval = spinand_read_status(spi_nand, &status); - if (retval < 0) { - dev_err(&spi_nand->dev, - "error %d reading status register\n", retval); - return retval; - } - - if ((status & STATUS_OIP_MASK) == STATUS_READY) { - if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) { - dev_err(&spi_nand->dev, - "program error, page %d\n", page_id); - return -1; - } - break; - } - } -#ifdef CONFIG_MTD_SPINAND_ONDIEECC - if (enable_hw_ecc) { - retval = spinand_disable_ecc(spi_nand); - if (retval < 0) { - dev_err(&spi_nand->dev, "disable ecc failed!!\n"); - return retval; - } - enable_hw_ecc = 0; - } -#endif - - return 0; -} - -/** - * spinand_erase_block_erase - erase a page - * @block_id: the physical block location to erase. - * - * Description: - * The command used here is 0xd8--indicating an erase command to erase - * one block--64 pages - * Need to wait for tERS. - */ -static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id) -{ - struct spinand_cmd cmd = {0}; - u16 row; - - row = block_id; - cmd.cmd = CMD_ERASE_BLK; - cmd.n_addr = 3; - cmd.addr[0] = (u8)((row & 0xff0000) >> 16); - cmd.addr[1] = (u8)((row & 0xff00) >> 8); - cmd.addr[2] = (u8)(row & 0x00ff); - - return spinand_cmd(spi_nand, &cmd); -} - -/** - * spinand_erase_block - erase a page - * @block_id: the physical block location to erase. - * - * Description: - * The commands used here are 0x06 and 0xd8--indicating an erase - * command to erase one block--64 pages - * It will first to enable the write enable bit (0x06 command), - * and then send the 0xd8 erase command - * Poll to wait for the tERS time to complete the tranaction. - */ -static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id) -{ - int retval; - u8 status = 0; - - retval = spinand_write_enable(spi_nand); - if (wait_till_ready(spi_nand)) - dev_err(&spi_nand->dev, "wait timedout!!!\n"); - - retval = spinand_erase_block_erase(spi_nand, block_id); - while (1) { - retval = spinand_read_status(spi_nand, &status); - if (retval < 0) { - dev_err(&spi_nand->dev, - "error %d reading status register\n", retval); - return retval; - } - - if ((status & STATUS_OIP_MASK) == STATUS_READY) { - if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) { - dev_err(&spi_nand->dev, - "erase error, block %d\n", block_id); - return -1; - } - break; - } - } - return 0; -} - -#ifdef CONFIG_MTD_SPINAND_ONDIEECC -static int spinand_write_page_hwecc(struct nand_chip *chip, - const u8 *buf, int oob_required, - int page) -{ - const u8 *p = buf; - int eccsize = chip->ecc.size; - int eccsteps = chip->ecc.steps; - - enable_hw_ecc = 1; - return nand_prog_page_op(chip, page, 0, p, eccsize * eccsteps); -} - -static int spinand_read_page_hwecc(struct nand_chip *chip, u8 *buf, - int oob_required, int page) -{ - int retval; - u8 status; - u8 *p = buf; - int eccsize = chip->ecc.size; - int eccsteps = chip->ecc.steps; - struct mtd_info *mtd = nand_to_mtd(chip); - struct spinand_info *info = nand_get_controller_data(chip); - - enable_read_hw_ecc = 1; - - nand_read_page_op(chip, page, 0, p, eccsize * eccsteps); - if (oob_required) - chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize); - - while (1) { - retval = spinand_read_status(info->spi, &status); - if (retval < 0) { - dev_err(&mtd->dev, - "error %d reading status register\n", retval); - return retval; - } - - if ((status & STATUS_OIP_MASK) == STATUS_READY) { - if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) { - pr_info("spinand: ECC error\n"); - mtd->ecc_stats.failed++; - } else if ((status & STATUS_ECC_MASK) == - STATUS_ECC_1BIT_CORRECTED) - mtd->ecc_stats.corrected++; - break; - } - } - return 0; -} -#endif - -static void spinand_select_chip(struct nand_chip *chip, int dev) -{ -} - -static u8 spinand_read_byte(struct nand_chip *chip) -{ - struct spinand_state *state = mtd_to_state(nand_to_mtd(chip)); - u8 data; - - data = state->buf[state->buf_ptr]; - state->buf_ptr++; - return data; -} - -static int spinand_wait(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct spinand_info *info = nand_get_controller_data(chip); - - unsigned long timeo = jiffies; - int retval, state = chip->state; - u8 status; - - if (state == FL_ERASING) - timeo += (HZ * 400) / 1000; - else - timeo += (HZ * 20) / 1000; - - while (time_before(jiffies, timeo)) { - retval = spinand_read_status(info->spi, &status); - if (retval < 0) { - dev_err(&mtd->dev, - "error %d reading status register\n", retval); - return retval; - } - - if ((status & STATUS_OIP_MASK) == STATUS_READY) - return 0; - - cond_resched(); - } - return 0; -} - -static void spinand_write_buf(struct nand_chip *chip, const u8 *buf, int len) -{ - struct spinand_state *state = mtd_to_state(nand_to_mtd(chip)); - - memcpy(state->buf + state->buf_ptr, buf, len); - state->buf_ptr += len; -} - -static void spinand_read_buf(struct nand_chip *chip, u8 *buf, int len) -{ - struct spinand_state *state = mtd_to_state(nand_to_mtd(chip)); - - memcpy(buf, state->buf + state->buf_ptr, len); - state->buf_ptr += len; -} - -/* - * spinand_reset- send RESET command "0xff" to the Nand device. - */ -static void spinand_reset(struct spi_device *spi_nand) -{ - struct spinand_cmd cmd = {0}; - - cmd.cmd = CMD_RESET; - - if (spinand_cmd(spi_nand, &cmd) < 0) - pr_info("spinand reset failed!\n"); - - /* elapse 1ms before issuing any other command */ - usleep_range(1000, 2000); - - if (wait_till_ready(spi_nand)) - dev_err(&spi_nand->dev, "wait timedout!\n"); -} - -static void spinand_cmdfunc(struct nand_chip *chip, unsigned int command, - int column, int page) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct spinand_info *info = nand_get_controller_data(chip); - struct spinand_state *state = info->priv; - - switch (command) { - /* - * READ0 - read in first 0x800 bytes - */ - case NAND_CMD_READ1: - case NAND_CMD_READ0: - state->buf_ptr = 0; - spinand_read_page(info->spi, page, 0x0, 0x840, state->buf); - break; - /* READOOB reads only the OOB because no ECC is performed. */ - case NAND_CMD_READOOB: - state->buf_ptr = 0; - spinand_read_page(info->spi, page, 0x800, 0x40, state->buf); - break; - case NAND_CMD_RNDOUT: - state->buf_ptr = column; - break; - case NAND_CMD_READID: - state->buf_ptr = 0; - spinand_read_id(info->spi, state->buf); - break; - case NAND_CMD_PARAM: - state->buf_ptr = 0; - break; - /* ERASE1 stores the block and page address */ - case NAND_CMD_ERASE1: - spinand_erase_block(info->spi, page); - break; - /* ERASE2 uses the block and page address from ERASE1 */ - case NAND_CMD_ERASE2: - break; - /* SEQIN sets up the addr buffer and all registers except the length */ - case NAND_CMD_SEQIN: - state->col = column; - state->row = page; - state->buf_ptr = 0; - break; - /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ - case NAND_CMD_PAGEPROG: - spinand_program_page(info->spi, state->row, state->col, - state->buf_ptr, state->buf); - break; - case NAND_CMD_STATUS: - spinand_get_otp(info->spi, state->buf); - if (!(state->buf[0] & 0x80)) - state->buf[0] = 0x80; - state->buf_ptr = 0; - break; - /* RESET command */ - case NAND_CMD_RESET: - if (wait_till_ready(info->spi)) - dev_err(&info->spi->dev, "WAIT timedout!!!\n"); - /* a minimum of 250us must elapse before issuing RESET cmd*/ - usleep_range(250, 1000); - spinand_reset(info->spi); - break; - default: - dev_err(&mtd->dev, "Unknown CMD: 0x%x\n", command); - } -} - -/** - * spinand_lock_block - send write register 0x1f command to the Nand device - * - * Description: - * After power up, all the Nand blocks are locked. This function allows - * one to unlock the blocks, and so it can be written or erased. - */ -static int spinand_lock_block(struct spi_device *spi_nand, u8 lock) -{ - struct spinand_cmd cmd = {0}; - int ret; - u8 otp = 0; - - ret = spinand_get_otp(spi_nand, &otp); - - cmd.cmd = CMD_WRITE_REG; - cmd.n_addr = 1; - cmd.addr[0] = REG_BLOCK_LOCK; - cmd.n_tx = 1; - cmd.tx_buf = &lock; - - ret = spinand_cmd(spi_nand, &cmd); - if (ret < 0) - dev_err(&spi_nand->dev, "error %d lock block\n", ret); - - return ret; -} - -/** - * spinand_probe - [spinand Interface] - * @spi_nand: registered device driver. - * - * Description: - * Set up the device driver parameters to make the device available. - */ -static int spinand_probe(struct spi_device *spi_nand) -{ - struct mtd_info *mtd; - struct nand_chip *chip; - struct spinand_info *info; - struct spinand_state *state; - - info = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->spi = spi_nand; - - spinand_lock_block(spi_nand, BL_ALL_UNLOCKED); - - state = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_state), - GFP_KERNEL); - if (!state) - return -ENOMEM; - - info->priv = state; - state->buf_ptr = 0; - state->buf = devm_kzalloc(&spi_nand->dev, BUFSIZE, GFP_KERNEL); - if (!state->buf) - return -ENOMEM; - - chip = devm_kzalloc(&spi_nand->dev, sizeof(struct nand_chip), - GFP_KERNEL); - if (!chip) - return -ENOMEM; - -#ifdef CONFIG_MTD_SPINAND_ONDIEECC - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.size = 0x200; - chip->ecc.bytes = 0x6; - chip->ecc.steps = 0x4; - - chip->ecc.strength = 1; - chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; - chip->ecc.read_page = spinand_read_page_hwecc; - chip->ecc.write_page = spinand_write_page_hwecc; -#else - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - if (spinand_disable_ecc(spi_nand) < 0) - dev_info(&spi_nand->dev, "%s: disable ecc failed!\n", - __func__); -#endif - - nand_set_flash_node(chip, spi_nand->dev.of_node); - nand_set_controller_data(chip, info); - chip->legacy.read_buf = spinand_read_buf; - chip->legacy.write_buf = spinand_write_buf; - chip->legacy.read_byte = spinand_read_byte; - chip->legacy.cmdfunc = spinand_cmdfunc; - chip->legacy.waitfunc = spinand_wait; - chip->options |= NAND_CACHEPRG; - chip->select_chip = spinand_select_chip; - chip->legacy.set_features = nand_get_set_features_notsupp; - chip->legacy.get_features = nand_get_set_features_notsupp; - - mtd = nand_to_mtd(chip); - - dev_set_drvdata(&spi_nand->dev, mtd); - - mtd->dev.parent = &spi_nand->dev; - mtd->oobsize = 64; -#ifdef CONFIG_MTD_SPINAND_ONDIEECC - mtd_set_ooblayout(mtd, &spinand_oob_64_ops); -#endif - - if (nand_scan(chip, 1)) - return -ENXIO; - - return mtd_device_register(mtd, NULL, 0); -} - -/** - * spinand_remove - remove the device driver - * @spi: the spi device. - * - * Description: - * Remove the device driver parameters and free up allocated memories. - */ -static int spinand_remove(struct spi_device *spi) -{ - mtd_device_unregister(dev_get_drvdata(&spi->dev)); - - return 0; -} - -static const struct of_device_id spinand_dt[] = { - { .compatible = "spinand,mt29f", }, - {} -}; -MODULE_DEVICE_TABLE(of, spinand_dt); - -/* - * Device name structure description - */ -static struct spi_driver spinand_driver = { - .driver = { - .name = "mt29f", - .of_match_table = spinand_dt, - }, - .probe = spinand_probe, - .remove = spinand_remove, -}; - -module_spi_driver(spinand_driver); - -MODULE_DESCRIPTION("SPI NAND driver for Micron"); -MODULE_AUTHOR("Henry Pan <hspan@micron.com>, Kamlakant Patel <kamlakant.patel@broadcom.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.h b/drivers/staging/mt29f_spinand/mt29f_spinand.h @@ -1,106 +0,0 @@ -/*- - * Copyright 2013 Broadcom Corporation - * - * Copyright (c) 2009-2010 Micron Technology, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Henry Pan <hspan@micron.com> - * - * based on nand.h - */ -#ifndef __LINUX_MTD_SPI_NAND_H -#define __LINUX_MTD_SPI_NAND_H - -#include <linux/wait.h> -#include <linux/spinlock.h> -#include <linux/mtd/mtd.h> - -/* cmd */ -#define CMD_READ 0x13 -#define CMD_READ_RDM 0x03 -#define CMD_PROG_PAGE_CLRCACHE 0x02 -#define CMD_PROG_PAGE 0x84 -#define CMD_PROG_PAGE_EXC 0x10 -#define CMD_ERASE_BLK 0xd8 -#define CMD_WR_ENABLE 0x06 -#define CMD_WR_DISABLE 0x04 -#define CMD_READ_ID 0x9f -#define CMD_RESET 0xff -#define CMD_READ_REG 0x0f -#define CMD_WRITE_REG 0x1f - -/* feature/ status reg */ -#define REG_BLOCK_LOCK 0xa0 -#define REG_OTP 0xb0 -#define REG_STATUS 0xc0/* timing */ - -/* status */ -#define STATUS_OIP_MASK 0x01 -#define STATUS_READY 0 -#define STATUS_BUSY BIT(0) - -#define STATUS_E_FAIL_MASK 0x04 -#define STATUS_E_FAIL BIT(2) - -#define STATUS_P_FAIL_MASK 0x08 -#define STATUS_P_FAIL BIT(3) - -#define STATUS_ECC_MASK 0x30 -#define STATUS_ECC_1BIT_CORRECTED BIT(4) -#define STATUS_ECC_ERROR BIT(5) -#define STATUS_ECC_RESERVED (BIT(5) | BIT(4)) - -/*ECC enable defines*/ -#define OTP_ECC_MASK 0x10 -#define OTP_ECC_OFF 0 -#define OTP_ECC_ON 1 - -#define ECC_DISABLED -#define ECC_IN_NAND -#define ECC_SOFT - -/* block lock */ -#define BL_ALL_LOCKED 0x38 -#define BL_1_2_LOCKED 0x30 -#define BL_1_4_LOCKED 0x28 -#define BL_1_8_LOCKED 0x20 -#define BL_1_16_LOCKED 0x18 -#define BL_1_32_LOCKED 0x10 -#define BL_1_64_LOCKED 0x08 -#define BL_ALL_UNLOCKED 0 - -struct spinand_info { - struct spi_device *spi; - void *priv; -}; - -struct spinand_state { - u32 col; - u32 row; - int buf_ptr; - u8 *buf; -}; - -struct spinand_cmd { - u8 cmd; - u32 n_addr; /* Number of address */ - u8 addr[3]; /* Reg Offset */ - u32 n_dummy; /* Dummy use */ - u32 n_tx; /* Number of tx bytes */ - u8 *tx_buf; /* Tx buf */ - u32 n_rx; /* Number of rx bytes */ - u8 *rx_buf; /* Rx buf */ -}; - -int spinand_mtd(struct mtd_info *mtd); -void spinand_mtd_release(struct mtd_info *mtd); - -#endif /* __LINUX_MTD_SPI_NAND_H */ diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c @@ -101,7 +101,8 @@ static int jffs2_sync_fs(struct super_block *sb, int wait) struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER - cancel_delayed_work_sync(&c->wbuf_dwork); + if (jffs2_is_writebuffered(c)) + cancel_delayed_work_sync(&c->wbuf_dwork); #endif mutex_lock(&c->alloc_sem); diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h @@ -377,6 +377,7 @@ struct cfi_fixup { #define CFI_MFR_SHARP 0x00B0 #define CFI_MFR_SST 0x00BF #define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_MICRON 0x002C /* Micron */ #define CFI_MFR_TOSHIBA 0x0098 #define CFI_MFR_WINBOND 0x00DA diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h @@ -207,6 +207,7 @@ struct mtd_debug_info { struct mtd_info { u_char type; uint32_t flags; + uint32_t orig_flags; /* Flags as before running mtd checks */ uint64_t size; // Total size of the MTD /* "Major" erase size for the device. Naïve users may take this @@ -386,7 +387,7 @@ static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd) return dev_of_node(&mtd->dev); } -static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) +static inline u32 mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) { return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize; } diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h @@ -203,9 +203,12 @@ enum nand_ecc_algo { */ #define NAND_IS_BOOT_MEDIUM 0x00400000 -/* Options set by nand scan */ -/* Nand scan has allocated controller struct */ -#define NAND_CONTROLLER_ALLOC 0x80000000 +/* + * Do not try to tweak the timings at runtime. This is needed when the + * controller initializes the timings on itself or when it relies on + * configuration done by the bootloader. + */ +#define NAND_KEEP_TIMINGS 0x00800000 /* Cell info constants */ #define NAND_CI_CHIPNR_MSK 0x03 @@ -245,49 +248,6 @@ struct nand_id { }; /** - * struct nand_controller_ops - Controller operations - * - * @attach_chip: this method is called after the NAND detection phase after - * flash ID and MTD fields such as erase size, page size and OOB - * size have been set up. ECC requirements are available if - * provided by the NAND chip or device tree. Typically used to - * choose the appropriate ECC configuration and allocate - * associated resources. - * This hook is optional. - * @detach_chip: free all resources allocated/claimed in - * nand_controller_ops->attach_chip(). - * This hook is optional. - */ -struct nand_controller_ops { - int (*attach_chip)(struct nand_chip *chip); - void (*detach_chip)(struct nand_chip *chip); -}; - -/** - * struct nand_controller - Structure used to describe a NAND controller - * - * @lock: protection lock - * @active: the mtd device which holds the controller currently - * @wq: wait queue to sleep on if a NAND operation is in - * progress used instead of the per chip wait queue - * when a hw controller is available. - * @ops: NAND controller operations. - */ -struct nand_controller { - spinlock_t lock; - struct nand_chip *active; - wait_queue_head_t wq; - const struct nand_controller_ops *ops; -}; - -static inline void nand_controller_init(struct nand_controller *nfc) -{ - nfc->active = NULL; - spin_lock_init(&nfc->lock); - init_waitqueue_head(&nfc->wq); -} - -/** * struct nand_ecc_step_info - ECC step information of ECC engine * @stepsize: data bytes per ECC step * @strengths: array of supported strengths @@ -879,18 +839,21 @@ struct nand_op_parser { /** * struct nand_operation - NAND operation descriptor + * @cs: the CS line to select for this NAND operation * @instrs: array of instructions to execute * @ninstrs: length of the @instrs array * * The actual operation structure that will be passed to chip->exec_op(). */ struct nand_operation { + unsigned int cs; const struct nand_op_instr *instrs; unsigned int ninstrs; }; -#define NAND_OPERATION(_instrs) \ +#define NAND_OPERATION(_cs, _instrs) \ { \ + .cs = _cs, \ .instrs = _instrs, \ .ninstrs = ARRAY_SIZE(_instrs), \ } @@ -898,11 +861,68 @@ struct nand_operation { int nand_op_parser_exec_op(struct nand_chip *chip, const struct nand_op_parser *parser, const struct nand_operation *op, bool check_only); +/** + * struct nand_controller_ops - Controller operations + * + * @attach_chip: this method is called after the NAND detection phase after + * flash ID and MTD fields such as erase size, page size and OOB + * size have been set up. ECC requirements are available if + * provided by the NAND chip or device tree. Typically used to + * choose the appropriate ECC configuration and allocate + * associated resources. + * This hook is optional. + * @detach_chip: free all resources allocated/claimed in + * nand_controller_ops->attach_chip(). + * This hook is optional. + * @exec_op: controller specific method to execute NAND operations. + * This method replaces chip->legacy.cmdfunc(), + * chip->legacy.{read,write}_{buf,byte,word}(), + * chip->legacy.dev_ready() and chip->legacy.waifunc(). + * @setup_data_interface: setup the data interface and timing. If + * chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this + * means the configuration should not be applied but + * only checked. + * This hook is optional. + */ +struct nand_controller_ops { + int (*attach_chip)(struct nand_chip *chip); + void (*detach_chip)(struct nand_chip *chip); + int (*exec_op)(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only); + int (*setup_data_interface)(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf); +}; + +/** + * struct nand_controller - Structure used to describe a NAND controller + * + * @lock: protection lock + * @active: the mtd device which holds the controller currently + * @wq: wait queue to sleep on if a NAND operation is in + * progress used instead of the per chip wait queue + * when a hw controller is available. + * @ops: NAND controller operations. + */ +struct nand_controller { + spinlock_t lock; + struct nand_chip *active; + wait_queue_head_t wq; + const struct nand_controller_ops *ops; +}; + +static inline void nand_controller_init(struct nand_controller *nfc) +{ + nfc->active = NULL; + spin_lock_init(&nfc->lock); + init_waitqueue_head(&nfc->wq); +} /** * struct nand_legacy - NAND chip legacy fields/hooks * @IO_ADDR_R: address to read the 8 I/O lines of the flash device * @IO_ADDR_W: address to write the 8 I/O lines of the flash device + * @select_chip: select/deselect a specific target/die * @read_byte: read one byte from the chip * @write_byte: write a single byte to the chip on the low 8 I/O lines * @write_buf: write data from the buffer to the chip @@ -921,6 +941,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * @get_features: get the NAND chip features * @chip_delay: chip dependent delay for transferring data from array to read * regs (tR). + * @dummy_controller: dummy controller implementation for drivers that can + * only control a single chip * * If you look at this structure you're already wrong. These fields/hooks are * all deprecated. @@ -928,6 +950,7 @@ int nand_op_parser_exec_op(struct nand_chip *chip, struct nand_legacy { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; + void (*select_chip)(struct nand_chip *chip, int cs); u8 (*read_byte)(struct nand_chip *chip); void (*write_byte)(struct nand_chip *chip, u8 byte); void (*write_buf)(struct nand_chip *chip, const u8 *buf, int len); @@ -945,6 +968,7 @@ struct nand_legacy { int (*get_features)(struct nand_chip *chip, int feature_addr, u8 *subfeature_para); int chip_delay; + struct nand_controller dummy_controller; }; /** @@ -955,17 +979,10 @@ struct nand_legacy { * you're modifying an existing driver that is using those * fields/hooks, you should consider reworking the driver * avoid using them. - * @select_chip: [REPLACEABLE] select chip nr - * @exec_op: controller specific method to execute NAND operations. - * This method replaces ->cmdfunc(), - * ->legacy.{read,write}_{buf,byte,word}(), - * ->legacy.dev_ready() and ->waifunc(). * @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for * setting the read-retry mode. Mostly needed for MLC NAND. * @ecc: [BOARDSPECIFIC] ECC control structure * @buf_align: minimum buffer alignment required by a platform - * @dummy_controller: dummy controller implementation for drivers that can - * only control a single chip * @state: [INTERN] the current state of the NAND device * @oob_poi: "poison value buffer," used for laying out OOB data * before writing @@ -1012,11 +1029,11 @@ struct nand_legacy { * this nand device will encounter their life times. * @blocks_per_die: [INTERN] The number of PEBs in a die * @data_interface: [INTERN] NAND interface timing information + * @cur_cs: currently selected target. -1 means no target selected, + * otherwise we should always have cur_cs >= 0 && + * cur_cs < numchips. NAND Controller drivers should not + * modify this value, but they're allowed to read it. * @read_retries: [INTERN] the number of read retry modes supported - * @setup_data_interface: [OPTIONAL] setup the data interface and timing. If - * chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this - * means the configuration should not be applied but - * only checked. * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash * lookup. @@ -1037,13 +1054,7 @@ struct nand_chip { struct nand_legacy legacy; - void (*select_chip)(struct nand_chip *chip, int cs); - int (*exec_op)(struct nand_chip *chip, - const struct nand_operation *op, - bool check_only); int (*setup_read_retry)(struct nand_chip *chip, int retry_mode); - int (*setup_data_interface)(struct nand_chip *chip, int chipnr, - const struct nand_data_interface *conf); unsigned int options; unsigned int bbt_options; @@ -1073,6 +1084,8 @@ struct nand_chip { struct nand_data_interface data_interface; + int cur_cs; + int read_retries; flstate_t state; @@ -1082,7 +1095,6 @@ struct nand_chip { struct nand_ecc_ctrl ecc; unsigned long buf_align; - struct nand_controller dummy_controller; uint8_t *bbt; struct nand_bbt_descr *bbt_td; @@ -1098,15 +1110,6 @@ struct nand_chip { } manufacturer; }; -static inline int nand_exec_op(struct nand_chip *chip, - const struct nand_operation *op) -{ - if (!chip->exec_op) - return -ENOTSUPP; - - return chip->exec_op(chip, op, false); -} - extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops; @@ -1345,5 +1348,12 @@ void nand_release(struct nand_chip *chip); * instruction and have no physical pin to check it. */ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms); +struct gpio_desc; +int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod, + unsigned long timeout_ms); + +/* Select/deselect a NAND target. */ +void nand_select_target(struct nand_chip *chip, unsigned int cs); +void nand_deselect_target(struct nand_chip *chip); #endif /* __LINUX_MTD_RAWNAND_H */ diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h @@ -1,20 +1,8 @@ -/* +/* SPDX-License-Identifier: GPL-2.0 + * * SuperH FLCTL nand controller * * Copyright © 2008 Renesas Solutions Corp. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __SH_FLCTL_H__ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h @@ -1,10 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __LINUX_MTD_SPI_NOR_H @@ -23,7 +19,8 @@ #define SNOR_MFR_ATMEL CFI_MFR_ATMEL #define SNOR_MFR_GIGADEVICE 0xc8 #define SNOR_MFR_INTEL CFI_MFR_INTEL -#define SNOR_MFR_MICRON CFI_MFR_ST /* ST Micro <--> Micron */ +#define SNOR_MFR_ST CFI_MFR_ST /* ST Micro */ +#define SNOR_MFR_MICRON CFI_MFR_MICRON /* Micron */ #define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX #define SNOR_MFR_SPANSION CFI_MFR_AMD #define SNOR_MFR_SST CFI_MFR_SST @@ -236,6 +233,8 @@ enum spi_nor_option_flags { SNOR_F_READY_XSR_RDY = BIT(4), SNOR_F_USE_CLSR = BIT(5), SNOR_F_BROKEN_RESET = BIT(6), + SNOR_F_4B_OPCODES = BIT(7), + SNOR_F_HAS_4BAIT = BIT(8), }; /** diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h @@ -194,8 +194,10 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; +extern const struct spinand_manufacturer toshiba_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; /**