SPI NOR Changes

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 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):
     * Stopping to pass mtd_info objects to internal functions
     * Reorganizing code to avoid forward declarations
     * Dropping useless test in nand_legacy_set_defaults()
     * Moving nand_exec_op() to internal.h
     * Adding nand_[de]select_target() helpers
     * Passing the CS line to be selected in struct nand_operation
     * Making ->select_chip() optional when ->exec_op() is implemented
     * Deprecating the ->select_chip() hook
     * Moving the ->exec_op() method to nand_controller_ops
     * Moving ->setup_data_interface() to nand_controller_ops
     * Deprecating the dummy_controller field
     * Fixing JEDEC detection
     * Providing a helper for polling GPIO R/B pin
 
   Raw NAND chip drivers changes:
   - Macronix:
     * Flagging 1.8V AC chips with a broken GET_FEATURES(TIMINGS)
 
   Raw NAND controllers drivers changes:
   - Ams-delta:
     * Fixing the error path
     * SPDX tag added
     * May be compiled with COMPILE_TEST=y
     * Conversion to ->exec_op() interface
     * Dropping .IOADDR_R/W use
     * Use GPIO API for data I/O
   - Denali:
     * Removing denali_reset_banks()
     * Removing ->dev_ready() hook
     * Including <linux/bits.h> instead of <linux/bitops.h>
     * Changes to comply with the above fixes/cleanup done in the core.
   - FSMC:
     * Adding an SPDX tag to replace the license text
     * Making conversion from chip to fsmc consistent
     * Fixing unchecked return value in fsmc_read_page_hwecc
     * Changes to comply with the above fixes/cleanup done in the core.
   - Marvell:
     * Preventing 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:
     * Converting to SPDX identifiers
   - Sunxi:
     * Write pageprog related opcodes to the right register: WCMD_SET (fix)
   - Tegra:
     * Stop implementing ->select_chip()
   - VF610:
     * Adding 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:
   - Removing the depreacated mt29f_spinand driver from staging.
   - Adding 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
 -----BEGIN PGP SIGNATURE-----
 
 iQJQBAABCgA6FiEEKmCqpbOU668PNA69Ze02AX4ItwAFAlwZRoocHGJvcmlzLmJy
 ZXppbGxvbkBib290bGluLmNvbQAKCRBl7TYBfgi3ALhmEACGoyJLVxRsE0yXOex7
 JdPhZxnOkwnzaxQPQBQ7pFrWvRinV1Th8JenCCLiyM6DBcIDRo/87etC6cnjfnH/
 eJ8e7xPLiESCsAB8xixP1YvLaZQzjBKOH/+qNR6anp505ZNKhgsuUDATLSFfaEvz
 bZv3f1V70dPXdEK/3QXDZakqVtfvVBMSdyJMWdKSdVn70yA72wCPP4igcGdfYfoZ
 KKaanhS1EdxA++WFrRqocQay9rtjnFGONHLfrefop9YKSevLp1UDZSAYSk4CHcEf
 yaMFD+qlxc0wHlOk4XyDY3vREcdO50r2vXfN/Hxf0D6JeC/6RT0Hrm0YyY8X/u7l
 xLhSv+DKGyft3SnQ4MDdwg57bvDSOkPryI3cxBul3S6I00pCdo8l5zQskezDbk0E
 CkUuB+f7Wn3lmV7W8ZNbYHx0ljVJEXMxEQ6m/6ZLizIr/aC7m5ncgv8a3mL7QQQA
 KXuLamw9pPlf/tAOQxB1PTiE1n8ECYqcInJXzoxaRzUdn8TlIYjxUb7GIDoOFpzm
 6a3oCx6tUZq4j/GAoyYgo12NhH0aYW3X/V8N7SfPjmIofvFiUL1y1VQ2ghNDLCqH
 2cz6WsFGoA0oSVnxl/TSSM6/ZXbvnLgM4zq6+wZITyCFNwyC956MddZrjL6lVkMT
 s6kvh/h3wBWN+GxwlDeY+KZMlQ==
 =myqK
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2018-12-25 12:49:46 -08:00
commit eaa7649971
96 changed files with 3302 additions and 4108 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,10 +10,12 @@
#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 "physmap_of_gemini.h"
#include <linux/pinctrl/consumer.h>
#include "physmap-gemini.h"
/*
* The Flash-relevant parts of the global status register
@ -44,6 +46,82 @@
#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)
@ -57,6 +135,11 @@ int of_flash_probe_gemini(struct platform_device *pdev,
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");
@ -91,7 +174,32 @@ int of_flash_probe_gemini(struct platform_device *pdev,
map->bankwidth * 8);
}
dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n");
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;
}

View File

@ -2,7 +2,7 @@
#include <linux/of.h>
#include <linux/mtd/map.h>
#ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
#ifdef CONFIG_MTD_PHYSMAP_GEMINI
int of_flash_probe_gemini(struct platform_device *pdev,
struct device_node *np,
struct map_info *map);

View File

@ -28,7 +28,7 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/bitops.h>
#include "physmap_of_versatile.h"
#include "physmap-versatile.h"
static struct regmap *syscon_regmap;

View File

@ -2,7 +2,7 @@
#include <linux/of.h>
#include <linux/mtd/map.h>
#ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE
#ifdef CONFIG_MTD_PHYSMAP_VERSATILE
int of_flash_probe_versatile(struct platform_device *pdev,
struct device_node *np,
struct map_info *map);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
{
u_char res;
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)
{
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;
}
static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf,
int len)
{
struct ams_delta_nand *priv = nand_get_controller_data(this);
struct gpio_descs *data_gpiods = priv->data_gpiods;
int i;
if (priv->data_in)
ams_delta_dir_input(priv, false);
for (i = 0; i < data_gpiods->ndescs; i++)
gpiod_direction_input(data_gpiods->desc[i]);
for (i = 0; i < len; i++)
ams_delta_io_write(priv, buf[i]);
priv->data_in = true;
}
static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf,
int len)
{
int i = 0;
if (len > 0 && priv->data_in)
ams_delta_dir_output(priv, buf[i++]);
while (i < len)
ams_delta_io_write(priv, buf[i++]);
}
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);
const struct nand_op_instr *instr;
int ret = 0;
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));
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)");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
u32 pc = readl(host->regs_va + FSMC_PC);
/* Support only one CS */
if (chipnr > 0)
return;
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");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
@ -1028,17 +1032,72 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
return 0;
}
/**
* 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] = {

View File

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

View File

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

View File

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

View File

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

View File

@ -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 int tegra_nand_exec_op(struct nand_chip *chip,
const struct nand_operation *op,
bool check_only)
{
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)
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);
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 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_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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)),
};
/**

View File

@ -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++;
/* 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;
/* 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;
/* 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;
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) */

View File

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

View File

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

View File

@ -25,7 +25,7 @@
#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>
@ -56,6 +56,27 @@ static inline int redboot_checksum(struct fis_image_desc *img)
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)
@ -76,6 +97,8 @@ static int parse_redboot_partitions(struct mtd_info *master,
static char nullstring[] = "unallocated";
#endif
parse_redboot_of(master);
if ( directory < 0 ) {
offset = master->size + directory * master->erasesize;
while (mtd_block_isbad(master, offset)) {
@ -289,9 +312,16 @@ static int parse_redboot_partitions(struct mtd_info *master,
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);

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand.o

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
@ -244,49 +247,6 @@ struct nand_id {
int len;
};
/**
* 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
@ -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 */

View File

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

View File

@ -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),
};
/**

View File

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