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:
commit
eaa7649971
7
.mailmap
7
.mailmap
@ -36,9 +36,10 @@ Bart Van Assche <bvanassche@acm.org> <bart.vanassche@sandisk.com>
|
||||
Ben Gardner <bgardner@wabtec.com>
|
||||
Ben M Cahill <ben.m.cahill@intel.com>
|
||||
Björn Steinbrink <B.Steinbrink@gmx.de>
|
||||
Boris Brezillon <boris.brezillon@bootlin.com> <boris.brezillon@free-electrons.com>
|
||||
Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon.dev@gmail.com>
|
||||
Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon@overkiz.com>
|
||||
Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com>
|
||||
Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@free-electrons.com>
|
||||
Boris Brezillon <bbrezillon@kernel.org> <b.brezillon.dev@gmail.com>
|
||||
Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com>
|
||||
Brian Avery <b.avery@hp.com>
|
||||
Brian King <brking@us.ibm.com>
|
||||
Christoph Hellwig <hch@lst.de>
|
||||
|
@ -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:
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -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),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
@ -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");
|
665
drivers/mtd/maps/physmap-core.c
Normal file
665
drivers/mtd/maps/physmap-core.c
Normal 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
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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;
|
||||
|
@ -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);
|
@ -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
|
@ -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");
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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).
|
||||
|
@ -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)");
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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[] = {
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -764,8 +764,10 @@ static const struct nand_ops spinand_ops = {
|
||||
};
|
||||
|
||||
static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
||||
&gigadevice_spinand_manufacturer,
|
||||
¯onix_spinand_manufacturer,
|
||||
µn_spinand_manufacturer,
|
||||
&toshiba_spinand_manufacturer,
|
||||
&winbond_spinand_manufacturer,
|
||||
};
|
||||
|
||||
|
148
drivers/mtd/nand/spi/gigadevice.c
Normal file
148
drivers/mtd/nand/spi/gigadevice.c
Normal 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,
|
||||
};
|
137
drivers/mtd/nand/spi/toshiba.c
Normal file
137
drivers/mtd/nand/spi/toshiba.c
Normal 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,
|
||||
};
|
@ -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)),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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) */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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"
|
||||
|
@ -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/
|
||||
|
@ -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.
|
@ -1 +0,0 @@
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand.o
|
@ -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
|
@ -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");
|
@ -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 */
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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__
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user