mirror of
https://github.com/torvalds/linux.git
synced 2026-05-05 23:05:25 -04:00
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:
@@ -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
Reference in New Issue
Block a user