MTD updates for 3.15:
- A few SPI NOR ID definitions - Kill the NAND "max pagesize" restriction - Fix some x16 bus-width NAND support - Add NAND JEDEC parameter page support - DT bindings for NAND ECC - GPMI NAND updates (subpage reads) - More OMAP NAND refactoring - New STMicro SPI NOR driver (now in 40 patches!) - A few other random bugfixes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJTP6x+AAoJEFySrpd9RFgtit0P/jLWsjMK8G2ldPC4bMZsXmDF n3c71GcCRlUq4Qzb4rtZx9DANLAh+JyRrMOKCPg6dAMegFdmUqDOpZpNp0vF57KG myFjqTk+n5y0tfSkWLMUFt0tQ8ArDp3IBkQCUWkD5LgG50EWmjveIQGH0kFnkE39 Kytqvw17RV7f81tIs+WvKt8++YWD2X1VTpTi0S4fx2bJ99bJDBf/GgdoQpj2oirt igXmloUFEsob9JHZ3qumcUm9vaHwv2TiouZTvRyGdJCCoPdpJEZO4Ka6e4uAVarT 6kMKXBk3lj2GsilOSFFCNetXfy5Bf0TkJkv4rDjh3R1Y4J/hSgraVCbWXdKhb6tj RmwesdFMjsyS4f/Rhk5PXwJgGL9uK2mi6bk/SmXU0AMgCDSa5zjshY8Wq6C6uXwk LqlnK8l3h8Txotbc/XJIL+QGMbMkYQI8gxWTHFaqzDtkMe36mnGs9Zec3oso/s2d CNRpq5+dMZ6qF0z3zpOQHmFbaOekivMy7kCKMXer6ONsrQBNwTdmkwy+SdqnWsLF YdJttwV/RRcE0SRvK6GrhvzkGlV83Z8RPny6hC1kbrgQ0ffoy2CmIqyWNObK1RXf sYqoF8TCtR6Y8rHHi5dzZ1lria+nm8pb4+UfQLRI0mK8og7YW+fIfHXxqRrZZIrt No8NCPBKWzyew2UE0AiQ =CKih -----END PGP SIGNATURE----- Merge tag 'for-linus-20140405' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: - A few SPI NOR ID definitions - Kill the NAND "max pagesize" restriction - Fix some x16 bus-width NAND support - Add NAND JEDEC parameter page support - DT bindings for NAND ECC - GPMI NAND updates (subpage reads) - More OMAP NAND refactoring - New STMicro SPI NOR driver (now in 40 patches!) - A few other random bugfixes * tag 'for-linus-20140405' of git://git.infradead.org/linux-mtd: (120 commits) Fix index regression in nand_read_subpage mtd: diskonchip: mem resource name is not optional mtd: nand: fix mention to CONFIG_MTD_NAND_ECC_BCH mtd: nand: fix GET/SET_FEATURES address on 16-bit devices mtd: omap2: Use devm_ioremap_resource() mtd: denali_dt: Use devm_ioremap_resource() mtd: devices: elm: update DRIVER_NAME as "omap-elm" mtd: devices: elm: configure parallel channels based on ecc_steps mtd: devices: elm: clean elm_load_syndrome mtd: devices: elm: check for hardware engine's design constraints mtd: st_spi_fsm: Succinctly reorganise .remove() mtd: st_spi_fsm: Allow loop to run at least once before giving up CPU mtd: st_spi_fsm: Correct vendor name spelling issue - missing "M" mtd: st_spi_fsm: Avoid duplicating MTD core code mtd: st_spi_fsm: Remove useless consts from function arguments mtd: st_spi_fsm: Convert ST SPI FSM (NOR) Flash driver to new DT partitions mtd: st_spi_fsm: Move runtime configurable msg sequences into device's struct mtd: st_spi_fsm: Supply the W25Qxxx chip specific configuration call-back mtd: st_spi_fsm: Supply the S25FLxxx chip specific configuration call-back mtd: st_spi_fsm: Supply the MX25xxx chip specific configuration call-back ...
This commit is contained in:
commit
c29aa153ef
|
@ -5,3 +5,17 @@
|
|||
"soft_bch".
|
||||
- nand-bus-width : 8 or 16 bus width if not present 8
|
||||
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
|
||||
|
||||
- nand-ecc-strength: integer representing the number of bits to correct
|
||||
per ECC step.
|
||||
|
||||
- nand-ecc-step-size: integer representing the number of data bytes
|
||||
that are covered by a single ECC step.
|
||||
|
||||
The ECC strength and ECC step size properties define the correction capability
|
||||
of a controller. Together, they say a controller can correct "{strength} bit
|
||||
errors per {size} bytes".
|
||||
|
||||
The interpretation of these parameters is implementation-defined, so not all
|
||||
implementations must support all possible combinations. However, implementations
|
||||
are encouraged to further specify the value(s) they support.
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
* ST-Microelectronics SPI FSM Serial (NOR) Flash Controller
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "st,spi-fsm"
|
||||
- reg : Contains register's location and length.
|
||||
- reg-names : Should contain the reg names "spi-fsm"
|
||||
- interrupts : The interrupt number
|
||||
- pinctrl-0 : Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
|
||||
|
||||
Optional properties:
|
||||
- st,syscfg : Phandle to boot-device system configuration registers
|
||||
- st,boot-device-reg : Address of the aforementioned boot-device register(s)
|
||||
- st,boot-device-spi : Expected boot-device value if booted via this device
|
||||
|
||||
Example:
|
||||
spifsm: spifsm@fe902000{
|
||||
compatible = "st,spi-fsm";
|
||||
reg = <0xfe902000 0x1000>;
|
||||
reg-names = "spi-fsm";
|
||||
pinctrl-0 = <&pinctrl_fsm>;
|
||||
st,syscfg = <&syscfg_rear>;
|
||||
st,boot-device-reg = <0x958>;
|
||||
st,boot-device-spi = <0x1a>;
|
||||
status = "okay";
|
||||
};
|
||||
|
|
@ -150,7 +150,7 @@ config MTD_BCM63XX_PARTS
|
|||
|
||||
config MTD_BCM47XX_PARTS
|
||||
tristate "BCM47XX partitioning support"
|
||||
depends on BCM47XX
|
||||
depends on BCM47XX || ARCH_BCM_5301X
|
||||
help
|
||||
This provides partitions parser for devices based on BCM47xx
|
||||
boards.
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <bcm47xx_nvram.h>
|
||||
|
||||
/* 10 parts were found on sflash on Netgear WNDR4500 */
|
||||
#define BCM47XXPART_MAX_PARTS 12
|
||||
|
@ -30,6 +29,7 @@
|
|||
#define BOARD_DATA_MAGIC2 0xBD0D0BBD
|
||||
#define CFE_MAGIC 0x43464531 /* 1EFC */
|
||||
#define FACTORY_MAGIC 0x59544346 /* FCTY */
|
||||
#define NVRAM_HEADER 0x48534C46 /* FLSH */
|
||||
#define POT_MAGIC1 0x54544f50 /* POTT */
|
||||
#define POT_MAGIC2 0x504f /* OP */
|
||||
#define ML_MAGIC1 0x39685a42
|
||||
|
@ -91,7 +91,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
|
|||
if (offset >= 0x2000000)
|
||||
break;
|
||||
|
||||
if (curr_part > BCM47XXPART_MAX_PARTS) {
|
||||
if (curr_part >= BCM47XXPART_MAX_PARTS) {
|
||||
pr_warn("Reached maximum number of partitions, scanning stopped!\n");
|
||||
break;
|
||||
}
|
||||
|
@ -147,6 +147,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
|
|||
|
||||
/* TRX */
|
||||
if (buf[0x000 / 4] == TRX_MAGIC) {
|
||||
if (BCM47XXPART_MAX_PARTS - curr_part < 4) {
|
||||
pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
trx = (struct trx_header *)buf;
|
||||
|
||||
trx_part = curr_part;
|
||||
|
@ -212,7 +217,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
|
|||
|
||||
/* Look for NVRAM at the end of the last block. */
|
||||
for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
|
||||
if (curr_part > BCM47XXPART_MAX_PARTS) {
|
||||
if (curr_part >= BCM47XXPART_MAX_PARTS) {
|
||||
pr_warn("Reached maximum number of partitions, scanning stopped!\n");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
|
@ -69,10 +68,10 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, s
|
|||
static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
|
||||
static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
|
||||
struct otp_info *, size_t);
|
||||
static int cfi_intelext_get_user_prot_info (struct mtd_info *,
|
||||
struct otp_info *, size_t);
|
||||
static int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t,
|
||||
size_t *, struct otp_info *);
|
||||
static int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t,
|
||||
size_t *, struct otp_info *);
|
||||
#endif
|
||||
static int cfi_intelext_suspend (struct mtd_info *);
|
||||
static void cfi_intelext_resume (struct mtd_info *);
|
||||
|
@ -435,10 +434,8 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
|||
int i;
|
||||
|
||||
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
|
||||
if (!mtd) {
|
||||
printk(KERN_ERR "Failed to allocate memory for MTD device\n");
|
||||
if (!mtd)
|
||||
return NULL;
|
||||
}
|
||||
mtd->priv = map;
|
||||
mtd->type = MTD_NORFLASH;
|
||||
|
||||
|
@ -564,10 +561,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
|
|||
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
|
||||
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
|
||||
* mtd->numeraseregions, GFP_KERNEL);
|
||||
if (!mtd->eraseregions) {
|
||||
printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
|
||||
if (!mtd->eraseregions)
|
||||
goto setup_err;
|
||||
}
|
||||
|
||||
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
|
||||
unsigned long ernum, ersize;
|
||||
|
@ -2399,24 +2394,19 @@ static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
|
|||
NULL, do_otp_lock, 1);
|
||||
}
|
||||
|
||||
static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret;
|
||||
static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
|
||||
ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
|
||||
return ret ? : retlen;
|
||||
{
|
||||
return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret;
|
||||
|
||||
ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
|
||||
return ret ? : retlen;
|
||||
return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
|
||||
NULL, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
|
@ -507,10 +506,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
|||
int i;
|
||||
|
||||
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
|
||||
if (!mtd) {
|
||||
printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
|
||||
if (!mtd)
|
||||
return NULL;
|
||||
}
|
||||
mtd->priv = map;
|
||||
mtd->type = MTD_NORFLASH;
|
||||
|
||||
|
@ -661,10 +658,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
|
|||
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
|
||||
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
|
||||
* mtd->numeraseregions, GFP_KERNEL);
|
||||
if (!mtd->eraseregions) {
|
||||
printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
|
||||
if (!mtd->eraseregions)
|
||||
goto setup_err;
|
||||
}
|
||||
|
||||
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
|
||||
unsigned long ernum, ersize;
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
|
@ -176,7 +175,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
|
|||
//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
|
||||
|
||||
if (!mtd) {
|
||||
printk(KERN_ERR "Failed to allocate memory for MTD device\n");
|
||||
kfree(cfi->cmdset_priv);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -189,7 +187,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
|
|||
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
|
||||
* mtd->numeraseregions, GFP_KERNEL);
|
||||
if (!mtd->eraseregions) {
|
||||
printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
|
||||
kfree(cfi->cmdset_priv);
|
||||
kfree(mtd);
|
||||
return NULL;
|
||||
|
|
|
@ -168,10 +168,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
|
|||
return 0;
|
||||
|
||||
cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
|
||||
if (!cfi->cfiq) {
|
||||
printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name);
|
||||
if (!cfi->cfiq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(cfi->cfiq,0,sizeof(struct cfi_ident));
|
||||
|
||||
|
|
|
@ -116,10 +116,8 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n
|
|||
printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr);
|
||||
|
||||
extp = kmalloc(size, GFP_KERNEL);
|
||||
if (!extp) {
|
||||
printk(KERN_ERR "Failed to allocate memory\n");
|
||||
if (!extp)
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_XIP
|
||||
local_irq_disable();
|
||||
|
|
|
@ -114,7 +114,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
|
|||
mapsize = sizeof(long) * DIV_ROUND_UP(max_chips, BITS_PER_LONG);
|
||||
chip_map = kzalloc(mapsize, GFP_KERNEL);
|
||||
if (!chip_map) {
|
||||
printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
|
||||
kfree(cfi.cfiq);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -139,7 +138,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
|
|||
retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
|
||||
|
||||
if (!retcfi) {
|
||||
printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
|
||||
kfree(cfi.cfiq);
|
||||
kfree(chip_map);
|
||||
return NULL;
|
||||
|
|
|
@ -210,6 +210,14 @@ config MTD_DOCG3
|
|||
M-Systems and now Sandisk. The support is very experimental,
|
||||
and doesn't give access to any write operations.
|
||||
|
||||
config MTD_ST_SPI_FSM
|
||||
tristate "ST Microelectronics SPI FSM Serial Flash Controller"
|
||||
depends on ARM || SH
|
||||
help
|
||||
This provides an MTD device driver for the ST Microelectronics
|
||||
SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
|
||||
for a subset of connected Serial Flash devices.
|
||||
|
||||
if MTD_DOCG3
|
||||
config BCH_CONST_M
|
||||
default 14
|
||||
|
|
|
@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o
|
|||
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
|
||||
obj-$(CONFIG_MTD_SST25L) += sst25l.o
|
||||
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
|
||||
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
|
||||
|
||||
|
||||
CFLAGS_docg3.o += -I$(src)
|
||||
|
|
|
@ -209,7 +209,6 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)
|
|||
}
|
||||
|
||||
|
||||
/* FIXME: ensure that mtd->size % erase_size == 0 */
|
||||
static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
||||
{
|
||||
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
|
||||
|
@ -240,13 +239,18 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
|||
|
||||
if (IS_ERR(bdev)) {
|
||||
pr_err("error: cannot open device %s\n", devname);
|
||||
goto devinit_err;
|
||||
goto err_free_block2mtd;
|
||||
}
|
||||
dev->blkdev = bdev;
|
||||
|
||||
if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
|
||||
pr_err("attempting to use an MTD device as a block device\n");
|
||||
goto devinit_err;
|
||||
goto err_free_block2mtd;
|
||||
}
|
||||
|
||||
if ((long)dev->blkdev->bd_inode->i_size % erase_size) {
|
||||
pr_err("erasesize must be a divisor of device size\n");
|
||||
goto err_free_block2mtd;
|
||||
}
|
||||
|
||||
mutex_init(&dev->write_mutex);
|
||||
|
@ -255,7 +259,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
|||
/* make the name contain the block device in */
|
||||
name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
|
||||
if (!name)
|
||||
goto devinit_err;
|
||||
goto err_destroy_mutex;
|
||||
|
||||
dev->mtd.name = name;
|
||||
|
||||
|
@ -274,7 +278,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
|||
|
||||
if (mtd_device_register(&dev->mtd, NULL, 0)) {
|
||||
/* Device didn't get added, so free the entry */
|
||||
goto devinit_err;
|
||||
goto err_destroy_mutex;
|
||||
}
|
||||
list_add(&dev->list, &blkmtd_device_list);
|
||||
pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
|
||||
|
@ -283,7 +287,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
|||
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
|
||||
return dev;
|
||||
|
||||
devinit_err:
|
||||
err_destroy_mutex:
|
||||
mutex_destroy(&dev->write_mutex);
|
||||
err_free_block2mtd:
|
||||
block2mtd_free_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -448,6 +454,7 @@ static void block2mtd_exit(void)
|
|||
struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
|
||||
block2mtd_sync(&dev->mtd);
|
||||
mtd_device_unregister(&dev->mtd);
|
||||
mutex_destroy(&dev->write_mutex);
|
||||
pr_info("mtd%d: [%s] removed\n",
|
||||
dev->mtd.index,
|
||||
dev->mtd.name + strlen("block2mtd: "));
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "omap-elm"
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -84,6 +86,8 @@ struct elm_info {
|
|||
struct list_head list;
|
||||
enum bch_ecc bch_type;
|
||||
struct elm_registers elm_regs;
|
||||
int ecc_steps;
|
||||
int ecc_syndrome_size;
|
||||
};
|
||||
|
||||
static LIST_HEAD(elm_devices);
|
||||
|
@ -103,7 +107,8 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
|
|||
* @dev: ELM device
|
||||
* @bch_type: Type of BCH ecc
|
||||
*/
|
||||
int elm_config(struct device *dev, enum bch_ecc bch_type)
|
||||
int elm_config(struct device *dev, enum bch_ecc bch_type,
|
||||
int ecc_steps, int ecc_step_size, int ecc_syndrome_size)
|
||||
{
|
||||
u32 reg_val;
|
||||
struct elm_info *info = dev_get_drvdata(dev);
|
||||
|
@ -112,10 +117,22 @@ int elm_config(struct device *dev, enum bch_ecc bch_type)
|
|||
dev_err(dev, "Unable to configure elm - device not probed?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* ELM cannot detect ECC errors for chunks > 1KB */
|
||||
if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {
|
||||
dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* ELM support 8 error syndrome process */
|
||||
if (ecc_steps > ERROR_VECTOR_MAX) {
|
||||
dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
|
||||
elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
|
||||
info->bch_type = bch_type;
|
||||
info->bch_type = bch_type;
|
||||
info->ecc_steps = ecc_steps;
|
||||
info->ecc_syndrome_size = ecc_syndrome_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -157,17 +174,15 @@ static void elm_load_syndrome(struct elm_info *info,
|
|||
int i, offset;
|
||||
u32 val;
|
||||
|
||||
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||
for (i = 0; i < info->ecc_steps; i++) {
|
||||
|
||||
/* Check error reported */
|
||||
if (err_vec[i].error_reported) {
|
||||
elm_configure_page_mode(info, i, true);
|
||||
offset = ELM_SYNDROME_FRAGMENT_0 +
|
||||
SYNDROME_FRAGMENT_REG_SIZE * i;
|
||||
|
||||
/* BCH8 */
|
||||
if (info->bch_type) {
|
||||
|
||||
switch (info->bch_type) {
|
||||
case BCH8_ECC:
|
||||
/* syndrome fragment 0 = ecc[9-12B] */
|
||||
val = cpu_to_be32(*(u32 *) &ecc[9]);
|
||||
elm_write_reg(info, offset, val);
|
||||
|
@ -186,7 +201,8 @@ static void elm_load_syndrome(struct elm_info *info,
|
|||
offset += 4;
|
||||
val = ecc[0];
|
||||
elm_write_reg(info, offset, val);
|
||||
} else {
|
||||
break;
|
||||
case BCH4_ECC:
|
||||
/* syndrome fragment 0 = ecc[20-52b] bits */
|
||||
val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
|
||||
((ecc[2] & 0xf) << 28);
|
||||
|
@ -196,11 +212,14 @@ static void elm_load_syndrome(struct elm_info *info,
|
|||
offset += 4;
|
||||
val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
|
||||
elm_write_reg(info, offset, val);
|
||||
break;
|
||||
default:
|
||||
pr_err("invalid config bch_type\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Update ecc pointer with ecc byte size */
|
||||
ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
|
||||
ecc += info->ecc_syndrome_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +242,7 @@ static void elm_start_processing(struct elm_info *info,
|
|||
* Set syndrome vector valid, so that ELM module
|
||||
* will process it for vectors error is reported
|
||||
*/
|
||||
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||
for (i = 0; i < info->ecc_steps; i++) {
|
||||
if (err_vec[i].error_reported) {
|
||||
offset = ELM_SYNDROME_FRAGMENT_6 +
|
||||
SYNDROME_FRAGMENT_REG_SIZE * i;
|
||||
|
@ -252,7 +271,7 @@ static void elm_error_correction(struct elm_info *info,
|
|||
int offset;
|
||||
u32 reg_val;
|
||||
|
||||
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||
for (i = 0; i < info->ecc_steps; i++) {
|
||||
|
||||
/* Check error reported */
|
||||
if (err_vec[i].error_reported) {
|
||||
|
@ -354,10 +373,8 @@ static int elm_probe(struct platform_device *pdev)
|
|||
struct elm_info *info;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory\n");
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
|
@ -380,7 +397,7 @@ static int elm_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (pm_runtime_get_sync(&pdev->dev)) {
|
||||
if (pm_runtime_get_sync(&pdev->dev) < 0) {
|
||||
ret = -EINVAL;
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
dev_err(&pdev->dev, "can't enable clock\n");
|
||||
|
@ -505,7 +522,7 @@ MODULE_DEVICE_TABLE(of, elm_of_match);
|
|||
|
||||
static struct platform_driver elm_driver = {
|
||||
.driver = {
|
||||
.name = "elm",
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(elm_of_match),
|
||||
.pm = &elm_pm_ops,
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -41,7 +40,8 @@
|
|||
#define OPCODE_WRSR 0x01 /* Write status register 1 byte */
|
||||
#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
|
||||
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
|
||||
#define OPCODE_QUAD_READ 0x6b /* Read data bytes */
|
||||
#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */
|
||||
#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */
|
||||
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
|
||||
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
|
||||
#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
|
||||
|
@ -54,7 +54,8 @@
|
|||
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
||||
#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */
|
||||
#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */
|
||||
#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */
|
||||
#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */
|
||||
#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */
|
||||
#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */
|
||||
#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */
|
||||
|
||||
|
@ -95,6 +96,7 @@
|
|||
enum read_type {
|
||||
M25P80_NORMAL = 0,
|
||||
M25P80_FAST,
|
||||
M25P80_DUAL,
|
||||
M25P80_QUAD,
|
||||
};
|
||||
|
||||
|
@ -479,6 +481,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
|
|||
{
|
||||
switch (flash->flash_read) {
|
||||
case M25P80_FAST:
|
||||
case M25P80_DUAL:
|
||||
case M25P80_QUAD:
|
||||
return 1;
|
||||
case M25P80_NORMAL:
|
||||
|
@ -492,6 +495,8 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
|
|||
static inline unsigned int m25p80_rx_nbits(const struct m25p *flash)
|
||||
{
|
||||
switch (flash->flash_read) {
|
||||
case M25P80_DUAL:
|
||||
return 2;
|
||||
case M25P80_QUAD:
|
||||
return 4;
|
||||
default:
|
||||
|
@ -855,7 +860,8 @@ struct flash_info {
|
|||
#define SST_WRITE 0x04 /* use SST byte programming */
|
||||
#define M25P_NO_FR 0x08 /* Can't do fastread */
|
||||
#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */
|
||||
#define M25P80_QUAD_READ 0x20 /* Flash supports Quad Read */
|
||||
#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */
|
||||
#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */
|
||||
};
|
||||
|
||||
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||
|
@ -934,6 +940,7 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) },
|
||||
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) },
|
||||
|
||||
/* Micron */
|
||||
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
|
||||
|
@ -953,8 +960,8 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) },
|
||||
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) },
|
||||
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
|
||||
{ "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_QUAD_READ) },
|
||||
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_QUAD_READ) },
|
||||
{ "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) },
|
||||
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) },
|
||||
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
|
||||
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
||||
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
||||
|
@ -965,6 +972,7 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
|
||||
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
|
||||
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
|
||||
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
|
||||
|
@ -1072,9 +1080,8 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)
|
|||
for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
|
||||
info = (void *)m25p_ids[tmp].driver_data;
|
||||
if (info->jedec_id == jedec) {
|
||||
if (info->ext_id != 0 && info->ext_id != ext_jedec)
|
||||
continue;
|
||||
return &m25p_ids[tmp];
|
||||
if (info->ext_id == 0 || info->ext_id == ext_jedec)
|
||||
return &m25p_ids[tmp];
|
||||
}
|
||||
}
|
||||
dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
|
||||
|
@ -1226,7 +1233,7 @@ static int m25p_probe(struct spi_device *spi)
|
|||
if (info->flags & M25P_NO_FR)
|
||||
flash->flash_read = M25P80_NORMAL;
|
||||
|
||||
/* Quad-read mode takes precedence over fast/normal */
|
||||
/* Quad/Dual-read mode takes precedence over fast/normal */
|
||||
if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) {
|
||||
ret = set_quad_mode(flash, info->jedec_id);
|
||||
if (ret) {
|
||||
|
@ -1234,6 +1241,8 @@ static int m25p_probe(struct spi_device *spi)
|
|||
return ret;
|
||||
}
|
||||
flash->flash_read = M25P80_QUAD;
|
||||
} else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) {
|
||||
flash->flash_read = M25P80_DUAL;
|
||||
}
|
||||
|
||||
/* Default commands */
|
||||
|
@ -1241,6 +1250,9 @@ static int m25p_probe(struct spi_device *spi)
|
|||
case M25P80_QUAD:
|
||||
flash->read_opcode = OPCODE_QUAD_READ;
|
||||
break;
|
||||
case M25P80_DUAL:
|
||||
flash->read_opcode = OPCODE_DUAL_READ;
|
||||
break;
|
||||
case M25P80_FAST:
|
||||
flash->read_opcode = OPCODE_FAST_READ;
|
||||
break;
|
||||
|
@ -1265,6 +1277,9 @@ static int m25p_probe(struct spi_device *spi)
|
|||
case M25P80_QUAD:
|
||||
flash->read_opcode = OPCODE_QUAD_READ_4B;
|
||||
break;
|
||||
case M25P80_DUAL:
|
||||
flash->read_opcode = OPCODE_DUAL_READ_4B;
|
||||
break;
|
||||
case M25P80_FAST:
|
||||
flash->read_opcode = OPCODE_FAST_READ_4B;
|
||||
break;
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -440,8 +439,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
|
||||
#ifdef CONFIG_MTD_DATAFLASH_OTP
|
||||
|
||||
static int dataflash_get_otp_info(struct mtd_info *mtd,
|
||||
struct otp_info *info, size_t len)
|
||||
static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *info)
|
||||
{
|
||||
/* Report both blocks as identical: bytes 0..64, locked.
|
||||
* Unless the user block changed from all-ones, we can't
|
||||
|
@ -450,7 +449,8 @@ static int dataflash_get_otp_info(struct mtd_info *mtd,
|
|||
info->start = 0;
|
||||
info->length = 64;
|
||||
info->locked = 1;
|
||||
return sizeof(*info);
|
||||
*retlen = sizeof(*info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t otp_read(struct spi_device *spi, unsigned base,
|
||||
|
@ -542,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
|
|||
struct dataflash *priv = mtd->priv;
|
||||
int status;
|
||||
|
||||
if (len > 64)
|
||||
return -EINVAL;
|
||||
if (from >= 64) {
|
||||
/*
|
||||
* Attempting to write beyond the end of OTP memory,
|
||||
* no data can be written.
|
||||
*/
|
||||
*retlen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Strictly speaking, we *could* truncate the write ... but
|
||||
* let's not do that for the only write that's ever possible.
|
||||
*/
|
||||
/* Truncate the write to fit into OTP memory. */
|
||||
if ((from + len) > 64)
|
||||
return -EINVAL;
|
||||
len = 64 - from;
|
||||
|
||||
/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
|
||||
* IN: ignore all
|
||||
|
|
|
@ -205,6 +205,8 @@ static inline void kill_final_newline(char *str)
|
|||
return 1; \
|
||||
} while (0)
|
||||
|
||||
#ifndef MODULE
|
||||
static int phram_init_called;
|
||||
/*
|
||||
* This shall contain the module parameter if any. It is of the form:
|
||||
* - phram=<device>,<address>,<size> for module case
|
||||
|
@ -213,9 +215,10 @@ static inline void kill_final_newline(char *str)
|
|||
* size.
|
||||
* Example: phram.phram=rootfs,0xa0000000,512Mi
|
||||
*/
|
||||
static __initdata char phram_paramline[64 + 20 + 20];
|
||||
static char phram_paramline[64 + 20 + 20];
|
||||
#endif
|
||||
|
||||
static int __init phram_setup(const char *val)
|
||||
static int phram_setup(const char *val)
|
||||
{
|
||||
char buf[64 + 20 + 20], *str = buf;
|
||||
char *token[3];
|
||||
|
@ -264,17 +267,36 @@ static int __init phram_setup(const char *val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int __init phram_param_call(const char *val, struct kernel_param *kp)
|
||||
static int phram_param_call(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
#ifdef MODULE
|
||||
return phram_setup(val);
|
||||
#else
|
||||
/*
|
||||
* This function is always called before 'init_phram()', whether
|
||||
* built-in or module.
|
||||
* If more parameters are later passed in via
|
||||
* /sys/module/phram/parameters/phram
|
||||
* and init_phram() has already been called,
|
||||
* we can parse the argument now.
|
||||
*/
|
||||
|
||||
if (phram_init_called)
|
||||
return phram_setup(val);
|
||||
|
||||
/*
|
||||
* During early boot stage, we only save the parameters
|
||||
* here. We must parse them later: if the param passed
|
||||
* from kernel boot command line, phram_param_call() is
|
||||
* called so early that it is not possible to resolve
|
||||
* the device (even kmalloc() fails). Defer that work to
|
||||
* phram_setup().
|
||||
*/
|
||||
|
||||
if (strlen(val) >= sizeof(phram_paramline))
|
||||
return -ENOSPC;
|
||||
strcpy(phram_paramline, val);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
module_param_call(phram, phram_param_call, NULL, NULL, 000);
|
||||
|
@ -283,10 +305,15 @@ MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"
|
|||
|
||||
static int __init init_phram(void)
|
||||
{
|
||||
if (phram_paramline[0])
|
||||
return phram_setup(phram_paramline);
|
||||
int ret = 0;
|
||||
|
||||
return 0;
|
||||
#ifndef MODULE
|
||||
if (phram_paramline[0])
|
||||
ret = phram_setup(phram_paramline);
|
||||
phram_init_called = 1;
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit cleanup_phram(void)
|
||||
|
|
|
@ -725,16 +725,11 @@ static int __init init_pmc551(void)
|
|||
}
|
||||
|
||||
mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
|
||||
if (!mtd) {
|
||||
printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
|
||||
"device.\n");
|
||||
if (!mtd)
|
||||
break;
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
|
||||
"device.\n");
|
||||
kfree(mtd);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Generic/SFDP Flash Commands and Device Capabilities
|
||||
*
|
||||
* Copyright (C) 2013 Lee Jones <lee.jones@lianro.org>
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MTD_SERIAL_FLASH_CMDS_H
|
||||
#define _MTD_SERIAL_FLASH_CMDS_H
|
||||
|
||||
/* Generic Flash Commands/OPCODEs */
|
||||
#define FLASH_CMD_WREN 0x06
|
||||
#define FLASH_CMD_WRDI 0x04
|
||||
#define FLASH_CMD_RDID 0x9f
|
||||
#define FLASH_CMD_RDSR 0x05
|
||||
#define FLASH_CMD_RDSR2 0x35
|
||||
#define FLASH_CMD_WRSR 0x01
|
||||
#define FLASH_CMD_SE_4K 0x20
|
||||
#define FLASH_CMD_SE_32K 0x52
|
||||
#define FLASH_CMD_SE 0xd8
|
||||
#define FLASH_CMD_CHIPERASE 0xc7
|
||||
#define FLASH_CMD_WRVCR 0x81
|
||||
#define FLASH_CMD_RDVCR 0x85
|
||||
|
||||
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
|
||||
#define FLASH_CMD_READ 0x03 /* READ */
|
||||
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
|
||||
#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||
#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
|
||||
|
||||
#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
||||
|
||||
#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
|
||||
#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
|
||||
|
||||
/* READ commands with 32-bit addressing */
|
||||
#define FLASH_CMD_READ4 0x13
|
||||
#define FLASH_CMD_READ4_FAST 0x0c
|
||||
#define FLASH_CMD_READ4_1_1_2 0x3c
|
||||
#define FLASH_CMD_READ4_1_2_2 0xbc
|
||||
#define FLASH_CMD_READ4_1_1_4 0x6c
|
||||
#define FLASH_CMD_READ4_1_4_4 0xec
|
||||
|
||||
/* Configuration flags */
|
||||
#define FLASH_FLAG_SINGLE 0x000000ff
|
||||
#define FLASH_FLAG_READ_WRITE 0x00000001
|
||||
#define FLASH_FLAG_READ_FAST 0x00000002
|
||||
#define FLASH_FLAG_SE_4K 0x00000004
|
||||
#define FLASH_FLAG_SE_32K 0x00000008
|
||||
#define FLASH_FLAG_CE 0x00000010
|
||||
#define FLASH_FLAG_32BIT_ADDR 0x00000020
|
||||
#define FLASH_FLAG_RESET 0x00000040
|
||||
#define FLASH_FLAG_DYB_LOCKING 0x00000080
|
||||
|
||||
#define FLASH_FLAG_DUAL 0x0000ff00
|
||||
#define FLASH_FLAG_READ_1_1_2 0x00000100
|
||||
#define FLASH_FLAG_READ_1_2_2 0x00000200
|
||||
#define FLASH_FLAG_READ_2_2_2 0x00000400
|
||||
#define FLASH_FLAG_WRITE_1_1_2 0x00001000
|
||||
#define FLASH_FLAG_WRITE_1_2_2 0x00002000
|
||||
#define FLASH_FLAG_WRITE_2_2_2 0x00004000
|
||||
|
||||
#define FLASH_FLAG_QUAD 0x00ff0000
|
||||
#define FLASH_FLAG_READ_1_1_4 0x00010000
|
||||
#define FLASH_FLAG_READ_1_4_4 0x00020000
|
||||
#define FLASH_FLAG_READ_4_4_4 0x00040000
|
||||
#define FLASH_FLAG_WRITE_1_1_4 0x00100000
|
||||
#define FLASH_FLAG_WRITE_1_4_4 0x00200000
|
||||
#define FLASH_FLAG_WRITE_4_4_4 0x00400000
|
||||
|
||||
#endif /* _MTD_SERIAL_FLASH_CMDS_H */
|
|
@ -913,7 +913,6 @@ static int spear_smi_probe(struct platform_device *pdev)
|
|||
if (np) {
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
pr_err("%s: ERROR: no memory", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
@ -943,7 +942,6 @@ static int spear_smi_probe(struct platform_device *pdev)
|
|||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
|
||||
if (!dev) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "mem alloc fail\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,7 +30,6 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nftl.h>
|
||||
#include <linux/mtd/inftl.h>
|
||||
|
|
|
@ -55,10 +55,8 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
|
|||
int i, j;
|
||||
|
||||
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
|
||||
if (!mtd) {
|
||||
printk(KERN_ERR "Failed to allocate memory for MTD device\n");
|
||||
if (!mtd)
|
||||
return NULL;
|
||||
}
|
||||
mtd->priv = map;
|
||||
mtd->type = MTD_NORFLASH;
|
||||
|
||||
|
|
|
@ -135,11 +135,8 @@ static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr)
|
|||
{
|
||||
|
||||
lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL);
|
||||
if (!lpddr->qinfo) {
|
||||
printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n",
|
||||
map->name);
|
||||
if (!lpddr->qinfo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the ManuID */
|
||||
lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID));
|
||||
|
|
|
@ -66,11 +66,11 @@ config MTD_PHYSMAP_BANKWIDTH
|
|||
used internally by the CFI drivers.
|
||||
|
||||
config MTD_PHYSMAP_OF
|
||||
tristate "Flash device in physical memory map based on OF description"
|
||||
depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM)
|
||||
tristate "Memory device in physical memory map based on OF description"
|
||||
depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM)
|
||||
help
|
||||
This provides a 'mapping' driver which allows the NOR Flash and
|
||||
ROM driver code to communicate with chips which are mapped
|
||||
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.
|
||||
|
||||
|
@ -124,7 +124,7 @@ config MTD_NETSC520
|
|||
|
||||
config MTD_TS5500
|
||||
tristate "JEDEC Flash device mapped on Technologic Systems TS-5500"
|
||||
depends on X86
|
||||
depends on TS5500 || COMPILE_TEST
|
||||
select MTD_JEDECPROBE
|
||||
select MTD_CFI_AMDSTD
|
||||
help
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ioport.h>
|
||||
|
@ -138,7 +137,6 @@ static int platram_probe(struct platform_device *pdev)
|
|||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
dev_err(&pdev->dev, "no memory for flash info\n");
|
||||
err = -ENOMEM;
|
||||
goto exit_error;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#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>
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include <linux/blkpg.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
|
|
@ -324,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
|
|||
default:
|
||||
ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return -ENOSPC only if no data could be written at all.
|
||||
* Otherwise just return the number of bytes that actually
|
||||
* have been written.
|
||||
*/
|
||||
if ((ret == -ENOSPC) && (total_retlen))
|
||||
break;
|
||||
|
||||
if (!ret) {
|
||||
*ppos += retlen;
|
||||
total_retlen += retlen;
|
||||
|
@ -889,25 +898,26 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||
case OTPGETREGIONINFO:
|
||||
{
|
||||
struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
|
||||
size_t retlen;
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
switch (mfi->mode) {
|
||||
case MTD_FILE_MODE_OTP_FACTORY:
|
||||
ret = mtd_get_fact_prot_info(mtd, buf, 4096);
|
||||
ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf);
|
||||
break;
|
||||
case MTD_FILE_MODE_OTP_USER:
|
||||
ret = mtd_get_user_prot_info(mtd, buf, 4096);
|
||||
ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (ret >= 0) {
|
||||
if (!ret) {
|
||||
if (cmd == OTPGETREGIONCOUNT) {
|
||||
int nbr = ret / sizeof(struct otp_info);
|
||||
int nbr = retlen / sizeof(struct otp_info);
|
||||
ret = copy_to_user(argp, &nbr, sizeof(int));
|
||||
} else
|
||||
ret = copy_to_user(argp, buf, ret);
|
||||
ret = copy_to_user(argp, buf, retlen);
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
|
|
@ -883,14 +883,14 @@ EXPORT_SYMBOL_GPL(mtd_read_oob);
|
|||
* devices. The user data is one time programmable but the factory data is read
|
||||
* only.
|
||||
*/
|
||||
int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len)
|
||||
int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
|
||||
struct otp_info *buf)
|
||||
{
|
||||
if (!mtd->_get_fact_prot_info)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_get_fact_prot_info(mtd, buf, len);
|
||||
return mtd->_get_fact_prot_info(mtd, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
|
||||
|
||||
|
@ -906,14 +906,14 @@ int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
|
||||
|
||||
int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len)
|
||||
int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
|
||||
struct otp_info *buf)
|
||||
{
|
||||
if (!mtd->_get_user_prot_info)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_get_user_prot_info(mtd, buf, len);
|
||||
return mtd->_get_user_prot_info(mtd, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
|
||||
|
||||
|
@ -932,12 +932,22 @@ EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
|
|||
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*retlen = 0;
|
||||
if (!mtd->_write_user_prot_reg)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
|
||||
ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If no data could be written at all, we are out of memory and
|
||||
* must return -ENOSPC.
|
||||
*/
|
||||
return (*retlen) ? 0 : -ENOSPC;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
|
||||
|
||||
|
|
|
@ -150,11 +150,12 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_user_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->_get_user_prot_info(part->master, buf, len);
|
||||
return part->master->_get_user_prot_info(part->master, len, retlen,
|
||||
buf);
|
||||
}
|
||||
|
||||
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
|
@ -165,11 +166,12 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len)
|
||||
static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->_get_fact_prot_info(part->master, buf, len);
|
||||
return part->master->_get_fact_prot_info(part->master, len, retlen,
|
||||
buf);
|
||||
}
|
||||
|
||||
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
|
|
@ -460,6 +460,8 @@ config MTD_NAND_MXC
|
|||
config MTD_NAND_SH_FLCTL
|
||||
tristate "Support for NAND on Renesas SuperH FLCTL"
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on HAS_DMA
|
||||
help
|
||||
Several Renesas SuperH CPU has FLCTL. This option enables support
|
||||
for NAND Flash using FLCTL.
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -430,7 +430,7 @@ err_dma:
|
|||
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
|
||||
err_buf:
|
||||
if (err != 0)
|
||||
dev_warn(host->dev, "Fall back to CPU I/O\n");
|
||||
dev_dbg(host->dev, "Fall back to CPU I/O\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1220,6 +1220,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
|
|||
goto err;
|
||||
}
|
||||
|
||||
nand_chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
|
||||
nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
|
||||
|
||||
|
@ -1659,8 +1660,8 @@ static void nfc_select_chip(struct mtd_info *mtd, int chip)
|
|||
nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE);
|
||||
}
|
||||
|
||||
static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
|
||||
unsigned int *addr1234, unsigned int *cycle0)
|
||||
static int nfc_make_addr(struct mtd_info *mtd, int command, int column,
|
||||
int page_addr, unsigned int *addr1234, unsigned int *cycle0)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
|
@ -1674,7 +1675,8 @@ static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
|
|||
*addr1234 = 0;
|
||||
|
||||
if (column != -1) {
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
if (chip->options & NAND_BUSWIDTH_16 &&
|
||||
!nand_opcode_8bits(command))
|
||||
column >>= 1;
|
||||
addr_bytes[acycle++] = column & 0xff;
|
||||
if (mtd->writesize > 512)
|
||||
|
@ -1787,8 +1789,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
|
|||
}
|
||||
|
||||
if (do_addr)
|
||||
acycle = nfc_make_addr(mtd, column, page_addr, &addr1234,
|
||||
&cycle0);
|
||||
acycle = nfc_make_addr(mtd, command, column, page_addr,
|
||||
&addr1234, &cycle0);
|
||||
|
||||
nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
|
||||
nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
@ -308,7 +307,8 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i
|
|||
/* Serially input address */
|
||||
if (column != -1) {
|
||||
/* Adjust columns for 16 bit buswidth */
|
||||
if (this->options & NAND_BUSWIDTH_16)
|
||||
if (this->options & NAND_BUSWIDTH_16 &&
|
||||
!nand_opcode_8bits(command))
|
||||
column >>= 1;
|
||||
ctx->write_byte(mtd, column);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ioport.h>
|
||||
|
|
|
@ -627,6 +627,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
struct cafe_priv *cafe;
|
||||
uint32_t ctrl;
|
||||
int err = 0;
|
||||
int old_dma;
|
||||
struct nand_buffers *nbuf;
|
||||
|
||||
/* Very old versions shared the same PCI ident for all three
|
||||
functions on the chip. Verify the class too... */
|
||||
|
@ -655,13 +657,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
err = -ENOMEM;
|
||||
goto out_free_mtd;
|
||||
}
|
||||
cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers),
|
||||
&cafe->dmaaddr, GFP_KERNEL);
|
||||
if (!cafe->dmabuf) {
|
||||
err = -ENOMEM;
|
||||
goto out_ior;
|
||||
}
|
||||
cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
|
||||
|
||||
cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
|
||||
if (!cafe->rs) {
|
||||
|
@ -721,7 +716,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
"CAFE NAND", mtd);
|
||||
if (err) {
|
||||
dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
|
||||
goto out_free_dma;
|
||||
goto out_ior;
|
||||
}
|
||||
|
||||
/* Disable master reset, enable NAND clock */
|
||||
|
@ -735,6 +730,32 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
|
||||
cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
|
||||
|
||||
/* Enable NAND IRQ in global IRQ mask register */
|
||||
cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
|
||||
cafe_readl(cafe, GLOBAL_CTRL),
|
||||
cafe_readl(cafe, GLOBAL_IRQ_MASK));
|
||||
|
||||
/* Do not use the DMA for the nand_scan_ident() */
|
||||
old_dma = usedma;
|
||||
usedma = 0;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan_ident(mtd, 2, NULL)) {
|
||||
err = -ENXIO;
|
||||
goto out_irq;
|
||||
}
|
||||
|
||||
cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev,
|
||||
2112 + sizeof(struct nand_buffers) +
|
||||
mtd->writesize + mtd->oobsize,
|
||||
&cafe->dmaaddr, GFP_KERNEL);
|
||||
if (!cafe->dmabuf) {
|
||||
err = -ENOMEM;
|
||||
goto out_irq;
|
||||
}
|
||||
cafe->nand.buffers = nbuf = (void *)cafe->dmabuf + 2112;
|
||||
|
||||
/* Set up DMA address */
|
||||
cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
|
||||
if (sizeof(cafe->dmaaddr) > 4)
|
||||
|
@ -746,16 +767,13 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
|
||||
cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
|
||||
|
||||
/* Enable NAND IRQ in global IRQ mask register */
|
||||
cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
|
||||
cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
|
||||
/* this driver does not need the @ecccalc and @ecccode */
|
||||
nbuf->ecccalc = NULL;
|
||||
nbuf->ecccode = NULL;
|
||||
nbuf->databuf = (uint8_t *)(nbuf + 1);
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan_ident(mtd, 2, NULL)) {
|
||||
err = -ENXIO;
|
||||
goto out_irq;
|
||||
}
|
||||
/* Restore the DMA flag */
|
||||
usedma = old_dma;
|
||||
|
||||
cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
|
||||
if (mtd->writesize == 2048)
|
||||
|
@ -773,7 +791,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
} else {
|
||||
printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
|
||||
mtd->writesize);
|
||||
goto out_irq;
|
||||
goto out_free_dma;
|
||||
}
|
||||
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
cafe->nand.ecc.size = mtd->writesize;
|
||||
|
@ -790,7 +808,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
|
||||
err = nand_scan_tail(mtd);
|
||||
if (err)
|
||||
goto out_irq;
|
||||
goto out_free_dma;
|
||||
|
||||
pci_set_drvdata(pdev, mtd);
|
||||
|
||||
|
@ -799,12 +817,15 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
|
||||
goto out;
|
||||
|
||||
out_free_dma:
|
||||
dma_free_coherent(&cafe->pdev->dev,
|
||||
2112 + sizeof(struct nand_buffers) +
|
||||
mtd->writesize + mtd->oobsize,
|
||||
cafe->dmabuf, cafe->dmaaddr);
|
||||
out_irq:
|
||||
/* Disable NAND IRQ in global IRQ mask register */
|
||||
cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
|
||||
free_irq(pdev->irq, mtd);
|
||||
out_free_dma:
|
||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||
out_ior:
|
||||
pci_iounmap(pdev, cafe->mmio);
|
||||
out_free_mtd:
|
||||
|
@ -824,7 +845,10 @@ static void cafe_nand_remove(struct pci_dev *pdev)
|
|||
nand_release(mtd);
|
||||
free_rs(cafe->rs);
|
||||
pci_iounmap(pdev, cafe->mmio);
|
||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||
dma_free_coherent(&cafe->pdev->dev,
|
||||
2112 + sizeof(struct nand_buffers) +
|
||||
mtd->writesize + mtd->oobsize,
|
||||
cafe->dmabuf, cafe->dmaaddr);
|
||||
kfree(mtd);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
@ -30,24 +30,6 @@ struct denali_dt {
|
|||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void __iomem *request_and_map(struct device *dev,
|
||||
const struct resource *res)
|
||||
{
|
||||
void __iomem *ptr;
|
||||
|
||||
if (!devm_request_mem_region(dev, res->start, resource_size(res),
|
||||
"denali-dt")) {
|
||||
dev_err(dev, "unable to request %s\n", res->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!ptr)
|
||||
dev_err(dev, "ioremap_nocache of %s failed!", res->name);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static const struct of_device_id denali_nand_dt_ids[] = {
|
||||
{ .compatible = "denali,denali-nand-dt" },
|
||||
{ /* sentinel */ }
|
||||
|
@ -78,13 +60,6 @@ static int denali_dt_probe(struct platform_device *ofdev)
|
|||
return -ENOMEM;
|
||||
denali = &dt->denali;
|
||||
|
||||
denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
|
||||
nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
|
||||
if (!denali_reg || !nand_data) {
|
||||
dev_err(&ofdev->dev, "resources not completely defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
denali->platform = DT;
|
||||
denali->dev = &ofdev->dev;
|
||||
denali->irq = platform_get_irq(ofdev, 0);
|
||||
|
@ -93,13 +68,15 @@ static int denali_dt_probe(struct platform_device *ofdev)
|
|||
return denali->irq;
|
||||
}
|
||||
|
||||
denali->flash_reg = request_and_map(&ofdev->dev, denali_reg);
|
||||
if (!denali->flash_reg)
|
||||
return -ENOMEM;
|
||||
denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
|
||||
denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg);
|
||||
if (IS_ERR(denali->flash_reg))
|
||||
return PTR_ERR(denali->flash_reg);
|
||||
|
||||
denali->flash_mem = request_and_map(&ofdev->dev, nand_data);
|
||||
if (!denali->flash_mem)
|
||||
return -ENOMEM;
|
||||
nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
|
||||
denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data);
|
||||
if (IS_ERR(denali->flash_mem))
|
||||
return PTR_ERR(denali->flash_mem);
|
||||
|
||||
if (!of_property_read_u32(ofdev->dev.of_node,
|
||||
"dma-mask", (u32 *)&denali_dma_mask)) {
|
||||
|
|
|
@ -698,7 +698,8 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu
|
|||
/* Serially input address */
|
||||
if (column != -1) {
|
||||
/* Adjust columns for 16 bit buswidth */
|
||||
if (this->options & NAND_BUSWIDTH_16)
|
||||
if (this->options & NAND_BUSWIDTH_16 &&
|
||||
!nand_opcode_8bits(command))
|
||||
column >>= 1;
|
||||
WriteDOC(column, docptr, Mplus_FlashAddress);
|
||||
}
|
||||
|
@ -1438,7 +1439,7 @@ static int __init doc_probe(unsigned long physadr)
|
|||
int reg, len, numchips;
|
||||
int ret = 0;
|
||||
|
||||
if (!request_mem_region(physadr, DOC_IOREMAP_LEN, NULL))
|
||||
if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip"))
|
||||
return -EBUSY;
|
||||
virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
|
||||
if (!virtadr) {
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ioport.h>
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/of_mtd.h>
|
||||
#include "gpmi-nand.h"
|
||||
#include "bch-regs.h"
|
||||
|
||||
/* Resource names for the GPMI NAND driver. */
|
||||
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand"
|
||||
|
@ -985,7 +986,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int ret;
|
||||
|
||||
dev_dbg(this->dev, "page number is : %d\n", page);
|
||||
ret = read_page_prepare(this, buf, mtd->writesize,
|
||||
ret = read_page_prepare(this, buf, nfc_geo->payload_size,
|
||||
this->payload_virt, this->payload_phys,
|
||||
nfc_geo->payload_size,
|
||||
&payload_virt, &payload_phys);
|
||||
|
@ -999,7 +1000,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
|
||||
/* go! */
|
||||
ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
|
||||
read_page_end(this, buf, mtd->writesize,
|
||||
read_page_end(this, buf, nfc_geo->payload_size,
|
||||
this->payload_virt, this->payload_phys,
|
||||
nfc_geo->payload_size,
|
||||
payload_virt, payload_phys);
|
||||
|
@ -1041,7 +1042,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
|
||||
}
|
||||
|
||||
read_page_swap_end(this, buf, mtd->writesize,
|
||||
read_page_swap_end(this, buf, nfc_geo->payload_size,
|
||||
this->payload_virt, this->payload_phys,
|
||||
nfc_geo->payload_size,
|
||||
payload_virt, payload_phys);
|
||||
|
@ -1049,6 +1050,90 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
return max_bitflips;
|
||||
}
|
||||
|
||||
/* Fake a virtual small page for the subpage read */
|
||||
static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len, uint8_t *buf, int page)
|
||||
{
|
||||
struct gpmi_nand_data *this = chip->priv;
|
||||
void __iomem *bch_regs = this->resources.bch_regs;
|
||||
struct bch_geometry old_geo = this->bch_geometry;
|
||||
struct bch_geometry *geo = &this->bch_geometry;
|
||||
int size = chip->ecc.size; /* ECC chunk size */
|
||||
int meta, n, page_size;
|
||||
u32 r1_old, r2_old, r1_new, r2_new;
|
||||
unsigned int max_bitflips;
|
||||
int first, last, marker_pos;
|
||||
int ecc_parity_size;
|
||||
int col = 0;
|
||||
|
||||
/* The size of ECC parity */
|
||||
ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
|
||||
|
||||
/* Align it with the chunk size */
|
||||
first = offs / size;
|
||||
last = (offs + len - 1) / size;
|
||||
|
||||
/*
|
||||
* Find the chunk which contains the Block Marker. If this chunk is
|
||||
* in the range of [first, last], we have to read out the whole page.
|
||||
* Why? since we had swapped the data at the position of Block Marker
|
||||
* to the metadata which is bound with the chunk 0.
|
||||
*/
|
||||
marker_pos = geo->block_mark_byte_offset / size;
|
||||
if (last >= marker_pos && first <= marker_pos) {
|
||||
dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n",
|
||||
page, first, last, marker_pos);
|
||||
return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
|
||||
}
|
||||
|
||||
meta = geo->metadata_size;
|
||||
if (first) {
|
||||
col = meta + (size + ecc_parity_size) * first;
|
||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
|
||||
|
||||
meta = 0;
|
||||
buf = buf + first * size;
|
||||
}
|
||||
|
||||
/* Save the old environment */
|
||||
r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0);
|
||||
r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1);
|
||||
|
||||
/* change the BCH registers and bch_geometry{} */
|
||||
n = last - first + 1;
|
||||
page_size = meta + (size + ecc_parity_size) * n;
|
||||
|
||||
r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
|
||||
BM_BCH_FLASH0LAYOUT0_META_SIZE);
|
||||
r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
|
||||
| BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
|
||||
writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
|
||||
|
||||
r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
|
||||
r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
|
||||
writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
|
||||
|
||||
geo->ecc_chunk_count = n;
|
||||
geo->payload_size = n * size;
|
||||
geo->page_size = page_size;
|
||||
geo->auxiliary_status_offset = ALIGN(meta, 4);
|
||||
|
||||
dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
|
||||
page, offs, len, col, first, n, page_size);
|
||||
|
||||
/* Read the subpage now */
|
||||
this->swap_block_mark = false;
|
||||
max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page);
|
||||
|
||||
/* Restore */
|
||||
writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
|
||||
writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
|
||||
this->bch_geometry = old_geo;
|
||||
this->swap_block_mark = true;
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
|
@ -1565,6 +1650,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
|
|||
ecc->strength = bch_geo->ecc_strength;
|
||||
ecc->layout = &gpmi_hw_ecclayout;
|
||||
|
||||
/*
|
||||
* We only enable the subpage read when:
|
||||
* (1) the chip is imx6, and
|
||||
* (2) the size of the ECC parity is byte aligned.
|
||||
*/
|
||||
if (GPMI_IS_MX6Q(this) &&
|
||||
((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
|
||||
ecc->read_subpage = gpmi_ecc_read_subpage;
|
||||
chip->options |= NAND_SUBPAGE_READ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Can we enable the extra features? such as EDO or Sync mode.
|
||||
*
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include <linux/gfp.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -1501,6 +1501,8 @@ static int mxcnd_probe(struct platform_device *pdev)
|
|||
init_completion(&host->op_completion);
|
||||
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
if (host->irq < 0)
|
||||
return host->irq;
|
||||
|
||||
/*
|
||||
* Use host->devtype_data->irq_control() here instead of irq_control()
|
||||
|
|
|
@ -589,7 +589,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
|
|||
/* Serially input address */
|
||||
if (column != -1) {
|
||||
/* Adjust columns for 16 bit buswidth */
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
if (chip->options & NAND_BUSWIDTH_16 &&
|
||||
!nand_opcode_8bits(command))
|
||||
column >>= 1;
|
||||
chip->cmd_ctrl(mtd, column, ctrl);
|
||||
ctrl &= ~NAND_CTRL_CHANGE;
|
||||
|
@ -680,7 +681,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|||
/* Serially input address */
|
||||
if (column != -1) {
|
||||
/* Adjust columns for 16 bit buswidth */
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
if (chip->options & NAND_BUSWIDTH_16 &&
|
||||
!nand_opcode_8bits(command))
|
||||
column >>= 1;
|
||||
chip->cmd_ctrl(mtd, column, ctrl);
|
||||
ctrl &= ~NAND_CTRL_CHANGE;
|
||||
|
@ -1160,9 +1162,11 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @data_offs: offset of requested data within the page
|
||||
* @readlen: data length
|
||||
* @bufpoi: buffer to store read data
|
||||
* @page: page number to read
|
||||
*/
|
||||
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
|
||||
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
|
||||
int page)
|
||||
{
|
||||
int start_step, end_step, num_steps;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
|
@ -1170,13 +1174,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int data_col_addr, i, gaps = 0;
|
||||
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
||||
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
|
||||
int index = 0;
|
||||
int index;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
/* Column address within the page aligned to ECC size (256bytes) */
|
||||
start_step = data_offs / chip->ecc.size;
|
||||
end_step = (data_offs + readlen - 1) / chip->ecc.size;
|
||||
num_steps = end_step - start_step + 1;
|
||||
index = start_step * chip->ecc.bytes;
|
||||
|
||||
/* Data size aligned to ECC ecc.size */
|
||||
datafrag_len = num_steps * chip->ecc.size;
|
||||
|
@ -1213,8 +1218,6 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* Send the command to read the particular ECC bytes take care
|
||||
* about buswidth alignment in read_buf.
|
||||
*/
|
||||
index = start_step * chip->ecc.bytes;
|
||||
|
||||
aligned_pos = eccpos[index] & ~(busw - 1);
|
||||
aligned_len = eccfrag_len;
|
||||
if (eccpos[index] & (busw - 1))
|
||||
|
@ -1538,7 +1541,8 @@ read_retry:
|
|||
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
|
||||
!oob)
|
||||
ret = chip->ecc.read_subpage(mtd, chip,
|
||||
col, bytes, bufpoi);
|
||||
col, bytes, bufpoi,
|
||||
page);
|
||||
else
|
||||
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
||||
oob_required, page);
|
||||
|
@ -2000,7 +2004,7 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
|||
oob += chip->ecc.prepad;
|
||||
}
|
||||
|
||||
chip->read_buf(mtd, oob, eccbytes);
|
||||
chip->write_buf(mtd, oob, eccbytes);
|
||||
oob += eccbytes;
|
||||
|
||||
if (chip->ecc.postpad) {
|
||||
|
@ -3063,7 +3067,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int *busw)
|
||||
{
|
||||
struct nand_onfi_params *p = &chip->onfi_params;
|
||||
int i;
|
||||
int i, j;
|
||||
int val;
|
||||
|
||||
/* Try ONFI for unknown chip or LP */
|
||||
|
@ -3072,18 +3076,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* ONFI must be probed in 8-bit mode or with NAND_BUSWIDTH_AUTO, not
|
||||
* with NAND_BUSWIDTH_16
|
||||
*/
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
pr_err("ONFI cannot be probed in 16-bit mode; aborting\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
|
||||
for (i = 0; i < 3; i++) {
|
||||
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
|
||||
for (j = 0; j < sizeof(*p); j++)
|
||||
((uint8_t *)p)[j] = chip->read_byte(mtd);
|
||||
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
|
||||
le16_to_cpu(p->crc)) {
|
||||
break;
|
||||
|
@ -3168,6 +3164,87 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
|
||||
*/
|
||||
static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int *busw)
|
||||
{
|
||||
struct nand_jedec_params *p = &chip->jedec_params;
|
||||
struct jedec_ecc_info *ecc;
|
||||
int val;
|
||||
int i, j;
|
||||
|
||||
/* Try JEDEC for unknown chip or LP */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1);
|
||||
if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' ||
|
||||
chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' ||
|
||||
chip->read_byte(mtd) != 'C')
|
||||
return 0;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1);
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (j = 0; j < sizeof(*p); j++)
|
||||
((uint8_t *)p)[j] = chip->read_byte(mtd);
|
||||
|
||||
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
|
||||
le16_to_cpu(p->crc))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 3) {
|
||||
pr_err("Could not find valid JEDEC parameter page; aborting\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check version */
|
||||
val = le16_to_cpu(p->revision);
|
||||
if (val & (1 << 2))
|
||||
chip->jedec_version = 10;
|
||||
else if (val & (1 << 1))
|
||||
chip->jedec_version = 1; /* vendor specific version */
|
||||
|
||||
if (!chip->jedec_version) {
|
||||
pr_info("unsupported JEDEC version: %d\n", val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
|
||||
sanitize_string(p->model, sizeof(p->model));
|
||||
if (!mtd->name)
|
||||
mtd->name = p->model;
|
||||
|
||||
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
||||
|
||||
/* Please reference to the comment for nand_flash_detect_onfi. */
|
||||
mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
|
||||
mtd->erasesize *= mtd->writesize;
|
||||
|
||||
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
|
||||
/* Please reference to the comment for nand_flash_detect_onfi. */
|
||||
chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
|
||||
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
|
||||
chip->bits_per_cell = p->bits_per_cell;
|
||||
|
||||
if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
|
||||
*busw = NAND_BUSWIDTH_16;
|
||||
else
|
||||
*busw = 0;
|
||||
|
||||
/* ECC info */
|
||||
ecc = &p->ecc_info[0];
|
||||
|
||||
if (ecc->codeword_size >= 9) {
|
||||
chip->ecc_strength_ds = ecc->ecc_bits;
|
||||
chip->ecc_step_ds = 1 << ecc->codeword_size;
|
||||
} else {
|
||||
pr_warn("Invalid codeword size\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* nand_id_has_period - Check if an ID string has a given wraparound period
|
||||
* @id_data: the ID string
|
||||
|
@ -3474,10 +3551,10 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
*/
|
||||
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
int busw,
|
||||
int *maf_id, int *dev_id,
|
||||
struct nand_flash_dev *type)
|
||||
{
|
||||
int busw;
|
||||
int i, maf_idx;
|
||||
u8 id_data[8];
|
||||
|
||||
|
@ -3533,6 +3610,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|||
/* Check is chip is ONFI compliant */
|
||||
if (nand_flash_detect_onfi(mtd, chip, &busw))
|
||||
goto ident_done;
|
||||
|
||||
/* Check if the chip is JEDEC compliant */
|
||||
if (nand_flash_detect_jedec(mtd, chip, &busw))
|
||||
goto ident_done;
|
||||
}
|
||||
|
||||
if (!type->name)
|
||||
|
@ -3612,8 +3693,17 @@ ident_done:
|
|||
|
||||
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
||||
*maf_id, *dev_id);
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
chip->onfi_version ? chip->onfi_params.model : type->name);
|
||||
|
||||
if (chip->onfi_version)
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
chip->onfi_params.model);
|
||||
else if (chip->jedec_version)
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
chip->jedec_params.model);
|
||||
else
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
type->name);
|
||||
|
||||
pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
|
||||
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||
mtd->writesize, mtd->oobsize);
|
||||
|
@ -3634,18 +3724,16 @@ ident_done:
|
|||
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
struct nand_flash_dev *table)
|
||||
{
|
||||
int i, busw, nand_maf_id, nand_dev_id;
|
||||
int i, nand_maf_id, nand_dev_id;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct nand_flash_dev *type;
|
||||
|
||||
/* Get buswidth to select the correct functions */
|
||||
busw = chip->options & NAND_BUSWIDTH_16;
|
||||
/* Set the default functions */
|
||||
nand_set_defaults(chip, busw);
|
||||
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
||||
|
||||
/* Read the flash type */
|
||||
type = nand_get_flash_type(mtd, chip, busw,
|
||||
&nand_maf_id, &nand_dev_id, table);
|
||||
type = nand_get_flash_type(mtd, chip, &nand_maf_id,
|
||||
&nand_dev_id, table);
|
||||
|
||||
if (IS_ERR(type)) {
|
||||
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
|
||||
|
@ -3696,15 +3784,26 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
int i;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
struct nand_buffers *nbuf;
|
||||
|
||||
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
|
||||
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
|
||||
!(chip->bbt_options & NAND_BBT_USE_FLASH));
|
||||
|
||||
if (!(chip->options & NAND_OWN_BUFFERS))
|
||||
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
|
||||
if (!chip->buffers)
|
||||
return -ENOMEM;
|
||||
if (!(chip->options & NAND_OWN_BUFFERS)) {
|
||||
nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
|
||||
+ mtd->oobsize * 3, GFP_KERNEL);
|
||||
if (!nbuf)
|
||||
return -ENOMEM;
|
||||
nbuf->ecccalc = (uint8_t *)(nbuf + 1);
|
||||
nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
|
||||
nbuf->databuf = nbuf->ecccode + mtd->oobsize;
|
||||
|
||||
chip->buffers = nbuf;
|
||||
} else {
|
||||
if (!chip->buffers)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set the internal oob buffer location, just after the page data */
|
||||
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
|
||||
|
@ -3825,7 +3924,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
|
||||
case NAND_ECC_SOFT_BCH:
|
||||
if (!mtd_nand_has_bch()) {
|
||||
pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
|
||||
pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
|
||||
BUG();
|
||||
}
|
||||
ecc->calculate = nand_bch_calculate_ecc;
|
||||
|
|
|
@ -43,6 +43,9 @@ struct nand_flash_dev nand_flash_ids[] = {
|
|||
{"TC58NVG6D2 64G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
|
||||
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
|
||||
{"SDTNRGAMA 64G 3.3V 8-bit",
|
||||
{ .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
|
||||
SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
|
||||
|
||||
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -152,7 +151,8 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|||
if (column != -1 || page_addr != -1) {
|
||||
|
||||
if (column != -1) {
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
if (chip->options & NAND_BUSWIDTH_16 &&
|
||||
!nand_opcode_8bits(command))
|
||||
column >>= 1;
|
||||
write_addr_reg(nand, column);
|
||||
write_addr_reg(nand, column >> 8 | ENDADDR);
|
||||
|
@ -225,7 +225,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand)
|
|||
val = __raw_readl(nand->reg + REG_FMICSR);
|
||||
|
||||
if (!(val & NAND_EN))
|
||||
__raw_writel(val | NAND_EN, REG_FMICSR);
|
||||
__raw_writel(val | NAND_EN, nand->reg + REG_FMICSR);
|
||||
|
||||
val = __raw_readl(nand->reg + REG_SMCSR);
|
||||
|
||||
|
|
|
@ -118,14 +118,9 @@
|
|||
|
||||
#define OMAP24XX_DMA_GPMC 4
|
||||
|
||||
#define BCH8_MAX_ERROR 8 /* upto 8 bit correctable */
|
||||
#define BCH4_MAX_ERROR 4 /* upto 4 bit correctable */
|
||||
|
||||
#define SECTOR_BYTES 512
|
||||
/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
|
||||
#define BCH4_BIT_PAD 4
|
||||
#define BCH8_ECC_MAX ((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8)
|
||||
#define BCH4_ECC_MAX ((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8)
|
||||
|
||||
/* GPMC ecc engine settings for read */
|
||||
#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
|
||||
|
@ -159,7 +154,7 @@ struct omap_nand_info {
|
|||
|
||||
int gpmc_cs;
|
||||
unsigned long phys_base;
|
||||
unsigned long mem_size;
|
||||
enum omap_ecc ecc_opt;
|
||||
struct completion comp;
|
||||
struct dma_chan *dma;
|
||||
int gpmc_irq_fifo;
|
||||
|
@ -172,7 +167,6 @@ struct omap_nand_info {
|
|||
int buf_len;
|
||||
struct gpmc_nand_regs reg;
|
||||
/* fields specific for BCHx_HW ECC scheme */
|
||||
bool is_elm_used;
|
||||
struct device *elm_dev;
|
||||
struct device_node *of_node;
|
||||
};
|
||||
|
@ -1043,9 +1037,8 @@ static int omap_dev_ready(struct mtd_info *mtd)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH)
|
||||
/**
|
||||
* omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
|
||||
* omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
|
||||
* @mtd: MTD device structure
|
||||
* @mode: Read/Write mode
|
||||
*
|
||||
|
@ -1056,50 +1049,73 @@ static int omap_dev_ready(struct mtd_info *mtd)
|
|||
* eccsize0 = 0 (no additional protected byte in spare area)
|
||||
* eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
|
||||
*/
|
||||
static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
|
||||
static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
int nerrors;
|
||||
unsigned int bch_type;
|
||||
unsigned int dev_width, nsectors;
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
enum omap_ecc ecc_opt = info->ecc_opt;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
u32 val, wr_mode;
|
||||
unsigned int ecc_size1, ecc_size0;
|
||||
|
||||
/* Using wrapping mode 6 for writing */
|
||||
wr_mode = BCH_WRAPMODE_6;
|
||||
|
||||
/*
|
||||
* ECC engine enabled for valid ecc_size0 nibbles
|
||||
* and disabled for ecc_size1 nibbles.
|
||||
*/
|
||||
ecc_size0 = BCH_ECC_SIZE0;
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
|
||||
/* Perform ecc calculation on 512-byte sector */
|
||||
nsectors = 1;
|
||||
|
||||
/* Update number of error correction */
|
||||
nerrors = info->nand.ecc.strength;
|
||||
|
||||
/* Multi sector reading/writing for NAND flash with page size < 4096 */
|
||||
if (info->is_elm_used && (mtd->writesize <= 4096)) {
|
||||
/* GPMC configurations for calculating ECC */
|
||||
switch (ecc_opt) {
|
||||
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
||||
bch_type = 0;
|
||||
nsectors = 1;
|
||||
if (mode == NAND_ECC_READ) {
|
||||
/* Using wrapping mode 1 for reading */
|
||||
wr_mode = BCH_WRAPMODE_1;
|
||||
|
||||
/*
|
||||
* ECC engine enabled for ecc_size0 nibbles
|
||||
* and disabled for ecc_size1 nibbles.
|
||||
*/
|
||||
ecc_size0 = (nerrors == 8) ?
|
||||
BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0;
|
||||
ecc_size1 = (nerrors == 8) ?
|
||||
BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1;
|
||||
wr_mode = BCH_WRAPMODE_6;
|
||||
ecc_size0 = BCH_ECC_SIZE0;
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
} else {
|
||||
wr_mode = BCH_WRAPMODE_6;
|
||||
ecc_size0 = BCH_ECC_SIZE0;
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
}
|
||||
|
||||
/* Perform ecc calculation for one page (< 4096) */
|
||||
nsectors = info->nand.ecc.steps;
|
||||
break;
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
bch_type = 0;
|
||||
nsectors = chip->ecc.steps;
|
||||
if (mode == NAND_ECC_READ) {
|
||||
wr_mode = BCH_WRAPMODE_1;
|
||||
ecc_size0 = BCH4R_ECC_SIZE0;
|
||||
ecc_size1 = BCH4R_ECC_SIZE1;
|
||||
} else {
|
||||
wr_mode = BCH_WRAPMODE_6;
|
||||
ecc_size0 = BCH_ECC_SIZE0;
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
}
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
bch_type = 1;
|
||||
nsectors = 1;
|
||||
if (mode == NAND_ECC_READ) {
|
||||
wr_mode = BCH_WRAPMODE_6;
|
||||
ecc_size0 = BCH_ECC_SIZE0;
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
} else {
|
||||
wr_mode = BCH_WRAPMODE_6;
|
||||
ecc_size0 = BCH_ECC_SIZE0;
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
}
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
bch_type = 1;
|
||||
nsectors = chip->ecc.steps;
|
||||
if (mode == NAND_ECC_READ) {
|
||||
wr_mode = BCH_WRAPMODE_1;
|
||||
ecc_size0 = BCH8R_ECC_SIZE0;
|
||||
ecc_size1 = BCH8R_ECC_SIZE1;
|
||||
} else {
|
||||
wr_mode = BCH_WRAPMODE_6;
|
||||
ecc_size0 = BCH_ECC_SIZE0;
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
writel(ECC1, info->reg.gpmc_ecc_control);
|
||||
|
@ -1112,7 +1128,7 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
|
|||
|
||||
/* BCH configuration */
|
||||
val = ((1 << 16) | /* enable BCH */
|
||||
(((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
|
||||
(bch_type << 12) | /* BCH4/BCH8/BCH16 */
|
||||
(wr_mode << 8) | /* wrap mode */
|
||||
(dev_width << 7) | /* bus width */
|
||||
(((nsectors-1) & 0x7) << 4) | /* number of sectors */
|
||||
|
@ -1124,132 +1140,40 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
|
|||
/* Clear ecc and enable bits */
|
||||
writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_ECC_BCH
|
||||
/**
|
||||
* omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
*/
|
||||
static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned long nsectors, val1, val2;
|
||||
int i;
|
||||
|
||||
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
|
||||
|
||||
for (i = 0; i < nsectors; i++) {
|
||||
|
||||
/* Read hw-computed remainder */
|
||||
val1 = readl(info->reg.gpmc_bch_result0[i]);
|
||||
val2 = readl(info->reg.gpmc_bch_result1[i]);
|
||||
|
||||
/*
|
||||
* Add constant polynomial to remainder, in order to get an ecc
|
||||
* sequence of 0xFFs for a buffer filled with 0xFFs; and
|
||||
* left-justify the resulting polynomial.
|
||||
*/
|
||||
*ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF);
|
||||
*ecc_code++ = 0x13 ^ ((val2 >> 4) & 0xFF);
|
||||
*ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
|
||||
*ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF);
|
||||
*ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF);
|
||||
*ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF);
|
||||
*ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
|
||||
static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
|
||||
0x97, 0x79, 0xe5, 0x24, 0xb5};
|
||||
|
||||
/**
|
||||
* omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
*/
|
||||
static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned long nsectors, val1, val2, val3, val4;
|
||||
int i;
|
||||
|
||||
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
|
||||
|
||||
for (i = 0; i < nsectors; i++) {
|
||||
|
||||
/* Read hw-computed remainder */
|
||||
val1 = readl(info->reg.gpmc_bch_result0[i]);
|
||||
val2 = readl(info->reg.gpmc_bch_result1[i]);
|
||||
val3 = readl(info->reg.gpmc_bch_result2[i]);
|
||||
val4 = readl(info->reg.gpmc_bch_result3[i]);
|
||||
|
||||
/*
|
||||
* Add constant polynomial to remainder, in order to get an ecc
|
||||
* sequence of 0xFFs for a buffer filled with 0xFFs.
|
||||
*/
|
||||
*ecc_code++ = 0xef ^ (val4 & 0xFF);
|
||||
*ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF);
|
||||
*ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF);
|
||||
*ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF);
|
||||
*ecc_code++ = 0xed ^ (val3 & 0xFF);
|
||||
*ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF);
|
||||
*ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF);
|
||||
*ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
|
||||
*ecc_code++ = 0x97 ^ (val2 & 0xFF);
|
||||
*ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF);
|
||||
*ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
|
||||
*ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF);
|
||||
*ecc_code++ = 0xb5 ^ (val1 & 0xFF);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_MTD_NAND_ECC_BCH */
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
/**
|
||||
* omap3_calculate_ecc_bch - Generate bytes of ECC bytes
|
||||
* omap_calculate_ecc_bch - Generate bytes of ECC bytes
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
*
|
||||
* Support calculating of BCH4/8 ecc vectors for the page
|
||||
*/
|
||||
static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_calc)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
int eccbytes = info->nand.ecc.bytes;
|
||||
struct gpmc_nand_regs *gpmc_regs = &info->reg;
|
||||
u8 *ecc_code;
|
||||
unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
|
||||
int i, eccbchtsel;
|
||||
int i;
|
||||
|
||||
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
|
||||
/*
|
||||
* find BCH scheme used
|
||||
* 0 -> BCH4
|
||||
* 1 -> BCH8
|
||||
*/
|
||||
eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3);
|
||||
|
||||
for (i = 0; i < nsectors; i++) {
|
||||
|
||||
/* Read hw-computed remainder */
|
||||
bch_val1 = readl(info->reg.gpmc_bch_result0[i]);
|
||||
bch_val2 = readl(info->reg.gpmc_bch_result1[i]);
|
||||
if (eccbchtsel) {
|
||||
bch_val3 = readl(info->reg.gpmc_bch_result2[i]);
|
||||
bch_val4 = readl(info->reg.gpmc_bch_result3[i]);
|
||||
}
|
||||
|
||||
if (eccbchtsel) {
|
||||
/* BCH8 ecc scheme */
|
||||
ecc_code = ecc_calc;
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
|
||||
bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
|
||||
bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]);
|
||||
bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]);
|
||||
*ecc_code++ = (bch_val4 & 0xFF);
|
||||
*ecc_code++ = ((bch_val3 >> 24) & 0xFF);
|
||||
*ecc_code++ = ((bch_val3 >> 16) & 0xFF);
|
||||
|
@ -1263,14 +1187,11 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
|
|||
*ecc_code++ = ((bch_val1 >> 16) & 0xFF);
|
||||
*ecc_code++ = ((bch_val1 >> 8) & 0xFF);
|
||||
*ecc_code++ = (bch_val1 & 0xFF);
|
||||
/*
|
||||
* Setting 14th byte to zero to handle
|
||||
* erased page & maintain compatibility
|
||||
* with RBL
|
||||
*/
|
||||
*ecc_code++ = 0x0;
|
||||
} else {
|
||||
/* BCH4 ecc scheme */
|
||||
break;
|
||||
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
|
||||
bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
|
||||
*ecc_code++ = ((bch_val2 >> 12) & 0xFF);
|
||||
*ecc_code++ = ((bch_val2 >> 4) & 0xFF);
|
||||
*ecc_code++ = ((bch_val2 & 0xF) << 4) |
|
||||
|
@ -1279,12 +1200,38 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
|
|||
*ecc_code++ = ((bch_val1 >> 12) & 0xFF);
|
||||
*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
|
||||
*ecc_code++ = ((bch_val1 & 0xF) << 4);
|
||||
/*
|
||||
* Setting 8th byte to zero to handle
|
||||
* erased page
|
||||
*/
|
||||
*ecc_code++ = 0x0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* ECC scheme specific syndrome customizations */
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
||||
/* Add constant polynomial to remainder, so that
|
||||
* ECC of blank pages results in 0x0 on reading back */
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
ecc_calc[i] ^= bch4_polynomial[i];
|
||||
break;
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
/* Set 8th ECC byte as 0x0 for ROM compatibility */
|
||||
ecc_calc[eccbytes - 1] = 0x0;
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
/* Add constant polynomial to remainder, so that
|
||||
* ECC of blank pages results in 0x0 on reading back */
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
ecc_calc[i] ^= bch8_polynomial[i];
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
/* Set 14th ECC byte as 0x0 for ROM compatibility */
|
||||
ecc_calc[eccbytes - 1] = 0x0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ecc_calc += eccbytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1329,6 +1276,7 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
|
|||
return flip_bits;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
/**
|
||||
* omap_elm_correct_data - corrects page data area in case error reported
|
||||
* @mtd: MTD device structure
|
||||
|
@ -1337,56 +1285,47 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
|
|||
* @calc_ecc: ecc read from HW ECC registers
|
||||
*
|
||||
* Calculated ecc vector reported as zero in case of non-error pages.
|
||||
* In case of error/erased pages non-zero error vector is reported.
|
||||
* In case of non-zero ecc vector, check read_ecc at fixed offset
|
||||
* (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not.
|
||||
* To handle bit flips in this data, count the number of 0's in
|
||||
* read_ecc[x] and check if it greater than 4. If it is less, it is
|
||||
* programmed page, else erased page.
|
||||
*
|
||||
* 1. If page is erased, check with standard ecc vector (ecc vector
|
||||
* for erased page to find any bit flip). If check fails, bit flip
|
||||
* is present in erased page. Count the bit flips in erased page and
|
||||
* if it falls under correctable level, report page with 0xFF and
|
||||
* update the correctable bit information.
|
||||
* 2. If error is reported on programmed page, update elm error
|
||||
* vector and correct the page with ELM error correction routine.
|
||||
*
|
||||
* In case of non-zero ecc vector, first filter out erased-pages, and
|
||||
* then process data via ELM to detect bit-flips.
|
||||
*/
|
||||
static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
struct nand_ecc_ctrl *ecc = &info->nand.ecc;
|
||||
int eccsteps = info->nand.ecc.steps;
|
||||
int i , j, stat = 0;
|
||||
int eccsize, eccflag, ecc_vector_size;
|
||||
int eccflag, actual_eccbytes;
|
||||
struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
|
||||
u_char *ecc_vec = calc_ecc;
|
||||
u_char *spare_ecc = read_ecc;
|
||||
u_char *erased_ecc_vec;
|
||||
enum bch_ecc type;
|
||||
u_char *buf;
|
||||
int bitflip_count;
|
||||
bool is_error_reported = false;
|
||||
u32 bit_pos, byte_pos, error_max, pos;
|
||||
int err;
|
||||
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
/* omit 7th ECC byte reserved for ROM code compatibility */
|
||||
actual_eccbytes = ecc->bytes - 1;
|
||||
erased_ecc_vec = bch4_vector;
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
/* omit 14th ECC byte reserved for ROM code compatibility */
|
||||
actual_eccbytes = ecc->bytes - 1;
|
||||
erased_ecc_vec = bch8_vector;
|
||||
break;
|
||||
default:
|
||||
pr_err("invalid driver configuration\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Initialize elm error vector to zero */
|
||||
memset(err_vec, 0, sizeof(err_vec));
|
||||
|
||||
if (info->nand.ecc.strength == BCH8_MAX_ERROR) {
|
||||
type = BCH8_ECC;
|
||||
erased_ecc_vec = bch8_vector;
|
||||
} else {
|
||||
type = BCH4_ECC;
|
||||
erased_ecc_vec = bch4_vector;
|
||||
}
|
||||
|
||||
ecc_vector_size = info->nand.ecc.bytes;
|
||||
|
||||
/*
|
||||
* Remove extra byte padding for BCH8 RBL
|
||||
* compatibility and erased page handling
|
||||
*/
|
||||
eccsize = ecc_vector_size - 1;
|
||||
|
||||
for (i = 0; i < eccsteps ; i++) {
|
||||
eccflag = 0; /* initialize eccflag */
|
||||
|
||||
|
@ -1394,8 +1333,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
|||
* Check any error reported,
|
||||
* In case of error, non zero ecc reported.
|
||||
*/
|
||||
|
||||
for (j = 0; (j < eccsize); j++) {
|
||||
for (j = 0; j < actual_eccbytes; j++) {
|
||||
if (calc_ecc[j] != 0) {
|
||||
eccflag = 1; /* non zero ecc, error present */
|
||||
break;
|
||||
|
@ -1403,50 +1341,43 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
|||
}
|
||||
|
||||
if (eccflag == 1) {
|
||||
/*
|
||||
* Set threshold to minimum of 4, half of ecc.strength/2
|
||||
* to allow max bit flip in byte to 4
|
||||
*/
|
||||
unsigned int threshold = min_t(unsigned int, 4,
|
||||
info->nand.ecc.strength / 2);
|
||||
|
||||
/*
|
||||
* Check data area is programmed by counting
|
||||
* number of 0's at fixed offset in spare area.
|
||||
* Checking count of 0's against threshold.
|
||||
* In case programmed page expects at least threshold
|
||||
* zeros in byte.
|
||||
* If zeros are less than threshold for programmed page/
|
||||
* zeros are more than threshold erased page, either
|
||||
* case page reported as uncorrectable.
|
||||
*/
|
||||
if (hweight8(~read_ecc[eccsize]) >= threshold) {
|
||||
if (memcmp(calc_ecc, erased_ecc_vec,
|
||||
actual_eccbytes) == 0) {
|
||||
/*
|
||||
* Update elm error vector as
|
||||
* data area is programmed
|
||||
* calc_ecc[] matches pattern for ECC(all 0xff)
|
||||
* so this is definitely an erased-page
|
||||
*/
|
||||
err_vec[i].error_reported = true;
|
||||
is_error_reported = true;
|
||||
} else {
|
||||
/* Error reported in erased page */
|
||||
int bitflip_count;
|
||||
u_char *buf = &data[info->nand.ecc.size * i];
|
||||
|
||||
if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) {
|
||||
bitflip_count = erased_sector_bitflips(
|
||||
buf, read_ecc, info);
|
||||
|
||||
if (bitflip_count)
|
||||
stat += bitflip_count;
|
||||
else
|
||||
return -EINVAL;
|
||||
buf = &data[info->nand.ecc.size * i];
|
||||
/*
|
||||
* count number of 0-bits in read_buf.
|
||||
* This check can be removed once a similar
|
||||
* check is introduced in generic NAND driver
|
||||
*/
|
||||
bitflip_count = erased_sector_bitflips(
|
||||
buf, read_ecc, info);
|
||||
if (bitflip_count) {
|
||||
/*
|
||||
* number of 0-bits within ECC limits
|
||||
* So this may be an erased-page
|
||||
*/
|
||||
stat += bitflip_count;
|
||||
} else {
|
||||
/*
|
||||
* Too many 0-bits. It may be a
|
||||
* - programmed-page, OR
|
||||
* - erased-page with many bit-flips
|
||||
* So this page requires check by ELM
|
||||
*/
|
||||
err_vec[i].error_reported = true;
|
||||
is_error_reported = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the ecc vector */
|
||||
calc_ecc += ecc_vector_size;
|
||||
read_ecc += ecc_vector_size;
|
||||
calc_ecc += ecc->bytes;
|
||||
read_ecc += ecc->bytes;
|
||||
}
|
||||
|
||||
/* Check if any error reported */
|
||||
|
@ -1456,23 +1387,26 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
|||
/* Decode BCH error using ELM module */
|
||||
elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
|
||||
|
||||
err = 0;
|
||||
for (i = 0; i < eccsteps; i++) {
|
||||
if (err_vec[i].error_reported) {
|
||||
if (err_vec[i].error_uncorrectable) {
|
||||
pr_err("nand: uncorrectable bit-flips found\n");
|
||||
err = -EBADMSG;
|
||||
} else if (err_vec[i].error_reported) {
|
||||
for (j = 0; j < err_vec[i].error_count; j++) {
|
||||
u32 bit_pos, byte_pos, error_max, pos;
|
||||
|
||||
if (type == BCH8_ECC)
|
||||
error_max = BCH8_ECC_MAX;
|
||||
else
|
||||
error_max = BCH4_ECC_MAX;
|
||||
|
||||
if (info->nand.ecc.strength == BCH8_MAX_ERROR)
|
||||
pos = err_vec[i].error_loc[j];
|
||||
else
|
||||
/* Add 4 to take care 4 bit padding */
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
/* Add 4 bits to take care of padding */
|
||||
pos = err_vec[i].error_loc[j] +
|
||||
BCH4_BIT_PAD;
|
||||
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
pos = err_vec[i].error_loc[j];
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
error_max = (ecc->size + actual_eccbytes) * 8;
|
||||
/* Calculate bit position of error */
|
||||
bit_pos = pos % 8;
|
||||
|
||||
|
@ -1480,13 +1414,22 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
|||
byte_pos = (error_max - pos - 1) / 8;
|
||||
|
||||
if (pos < error_max) {
|
||||
if (byte_pos < 512)
|
||||
if (byte_pos < 512) {
|
||||
pr_debug("bitflip@dat[%d]=%x\n",
|
||||
byte_pos, data[byte_pos]);
|
||||
data[byte_pos] ^= 1 << bit_pos;
|
||||
else
|
||||
} else {
|
||||
pr_debug("bitflip@oob[%d]=%x\n",
|
||||
(byte_pos - 512),
|
||||
spare_ecc[byte_pos - 512]);
|
||||
spare_ecc[byte_pos - 512] ^=
|
||||
1 << bit_pos;
|
||||
}
|
||||
} else {
|
||||
pr_err("invalid bit-flip @ %d:%d\n",
|
||||
byte_pos, bit_pos);
|
||||
err = -EBADMSG;
|
||||
}
|
||||
/* else, not interested to correct ecc */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1494,16 +1437,11 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
|||
stat += err_vec[i].error_count;
|
||||
|
||||
/* Update page data with sector size */
|
||||
data += info->nand.ecc.size;
|
||||
spare_ecc += ecc_vector_size;
|
||||
data += ecc->size;
|
||||
spare_ecc += ecc->bytes;
|
||||
}
|
||||
|
||||
for (i = 0; i < eccsteps; i++)
|
||||
/* Return error if uncorrectable error present */
|
||||
if (err_vec[i].error_uncorrectable)
|
||||
return -EINVAL;
|
||||
|
||||
return stat;
|
||||
return (err) ? err : stat;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1601,7 +1539,8 @@ static int is_elm_present(struct omap_nand_info *info,
|
|||
struct device_node *elm_node, enum bch_ecc bch_type)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
info->is_elm_used = false;
|
||||
struct nand_ecc_ctrl *ecc = &info->nand.ecc;
|
||||
int err;
|
||||
/* check whether elm-id is passed via DT */
|
||||
if (!elm_node) {
|
||||
pr_err("nand: error: ELM DT node not found\n");
|
||||
|
@ -1615,10 +1554,10 @@ static int is_elm_present(struct omap_nand_info *info,
|
|||
}
|
||||
/* ELM module available, now configure it */
|
||||
info->elm_dev = &pdev->dev;
|
||||
if (elm_config(info->elm_dev, bch_type))
|
||||
return -ENODEV;
|
||||
info->is_elm_used = true;
|
||||
return 0;
|
||||
err = elm_config(info->elm_dev, bch_type,
|
||||
(info->mtd.writesize / ecc->size), ecc->size, ecc->bytes);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_MTD_NAND_ECC_BCH */
|
||||
|
||||
|
@ -1657,6 +1596,7 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
info->gpmc_cs = pdata->cs;
|
||||
info->reg = pdata->reg;
|
||||
info->of_node = pdata->of_node;
|
||||
info->ecc_opt = pdata->ecc_opt;
|
||||
mtd = &info->mtd;
|
||||
mtd->priv = &info->nand;
|
||||
mtd->name = dev_name(&pdev->dev);
|
||||
|
@ -1666,27 +1606,11 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
nand_chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
err = -EINVAL;
|
||||
dev_err(&pdev->dev, "error getting memory resource\n");
|
||||
goto return_error;
|
||||
}
|
||||
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(nand_chip->IO_ADDR_R))
|
||||
return PTR_ERR(nand_chip->IO_ADDR_R);
|
||||
|
||||
info->phys_base = res->start;
|
||||
info->mem_size = resource_size(res);
|
||||
|
||||
if (!devm_request_mem_region(&pdev->dev, info->phys_base,
|
||||
info->mem_size, pdev->dev.driver->name)) {
|
||||
err = -EBUSY;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base,
|
||||
info->mem_size);
|
||||
if (!nand_chip->IO_ADDR_R) {
|
||||
err = -ENOMEM;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
nand_chip->controller = &info->controller;
|
||||
|
||||
|
@ -1812,7 +1736,7 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
/* populate MTD interface based on ECC scheme */
|
||||
nand_chip->ecc.layout = &omap_oobinfo;
|
||||
ecclayout = &omap_oobinfo;
|
||||
switch (pdata->ecc_opt) {
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_HAM1_CODE_HW:
|
||||
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
|
@ -1844,9 +1768,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.bytes = 7;
|
||||
nand_chip->ecc.strength = 4;
|
||||
nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = nand_bch_correct_data;
|
||||
nand_chip->ecc.calculate = omap3_calculate_ecc_bch4;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
||||
/* define ECC layout */
|
||||
ecclayout->eccbytes = nand_chip->ecc.bytes *
|
||||
(mtd->writesize /
|
||||
|
@ -1884,9 +1808,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
/* 14th bit is kept reserved for ROM-code compatibility */
|
||||
nand_chip->ecc.bytes = 7 + 1;
|
||||
nand_chip->ecc.strength = 4;
|
||||
nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
||||
nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
||||
/* define ECC layout */
|
||||
|
@ -1919,9 +1843,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.bytes = 13;
|
||||
nand_chip->ecc.strength = 8;
|
||||
nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = nand_bch_correct_data;
|
||||
nand_chip->ecc.calculate = omap3_calculate_ecc_bch8;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
||||
/* define ECC layout */
|
||||
ecclayout->eccbytes = nand_chip->ecc.bytes *
|
||||
(mtd->writesize /
|
||||
|
@ -1960,9 +1884,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
/* 14th bit is kept reserved for ROM-code compatibility */
|
||||
nand_chip->ecc.bytes = 13 + 1;
|
||||
nand_chip->ecc.strength = 8;
|
||||
nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
||||
nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
||||
/* This ECC scheme requires ELM H/W block */
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#undef DEBUG
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
||||
|
||||
#define NAND_DEV_READY_TIMEOUT 50
|
||||
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
|
||||
#define NAND_STOP_DELAY (2 * HZ/50)
|
||||
#define PAGE_CHUNK_SIZE (2048)
|
||||
|
@ -1531,7 +1530,7 @@ KEEP_CONFIG:
|
|||
if (!ret) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"ECC strength %d at page size %d is not supported\n",
|
||||
chip->ecc_strength_ds, mtd->writesize);
|
||||
ecc_strength, mtd->writesize);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/io.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/onenand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -3238,20 +3237,17 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
/**
|
||||
* onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
|
||||
* @param mtd MTD device structure
|
||||
* @param buf the databuffer to put/get data
|
||||
* @param len number of bytes to read
|
||||
* @param retlen pointer to variable to store the number of read bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
*
|
||||
* Read factory OTP info.
|
||||
*/
|
||||
static int onenand_get_fact_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret;
|
||||
|
||||
ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);
|
||||
|
||||
return ret ? : retlen;
|
||||
return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
|
||||
MTD_OTP_FACTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3273,20 +3269,17 @@ static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
/**
|
||||
* onenand_get_user_prot_info - [MTD Interface] Read user OTP info
|
||||
* @param mtd MTD device structure
|
||||
* @param buf the databuffer to put/get data
|
||||
* @param retlen pointer to variable to store the number of read bytes
|
||||
* @param len number of bytes to read
|
||||
* @param buf the databuffer to put/get data
|
||||
*
|
||||
* Read user OTP info.
|
||||
*/
|
||||
static int onenand_get_user_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret;
|
||||
|
||||
ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);
|
||||
|
||||
return ret ? : retlen;
|
||||
return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
|
||||
MTD_OTP_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3995,11 +3988,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|||
/* Allocate buffers, if necessary */
|
||||
if (!this->page_buf) {
|
||||
this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
|
||||
if (!this->page_buf) {
|
||||
printk(KERN_ERR "%s: Can't allocate page_buf\n",
|
||||
__func__);
|
||||
if (!this->page_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
|
||||
this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
|
||||
if (!this->verify_buf) {
|
||||
|
@ -4012,8 +4002,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|||
if (!this->oob_buf) {
|
||||
this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
|
||||
if (!this->oob_buf) {
|
||||
printk(KERN_ERR "%s: Can't allocate oob_buf\n",
|
||||
__func__);
|
||||
if (this->options & ONENAND_PAGEBUF_ALLOC) {
|
||||
this->options &= ~ONENAND_PAGEBUF_ALLOC;
|
||||
kfree(this->page_buf);
|
||||
|
|
|
@ -872,10 +872,8 @@ static int s3c_onenand_probe(struct platform_device *pdev)
|
|||
|
||||
size = sizeof(struct mtd_info) + sizeof(struct onenand_chip);
|
||||
mtd = kzalloc(size, GFP_KERNEL);
|
||||
if (!mtd) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory\n");
|
||||
if (!mtd)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL);
|
||||
if (!onenand) {
|
||||
|
|
|
@ -602,8 +602,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
|
|||
if (rc) {
|
||||
printk(KERN_ERR PREFIX "error writing '%s' at "
|
||||
"0x%lx\n", part->mbd.mtd->name, addr);
|
||||
if (rc)
|
||||
goto err;
|
||||
goto err;
|
||||
}
|
||||
if (block == part->current_block)
|
||||
part->header_cache[offset + HEADER_MAP_OFFSET] = del;
|
||||
|
@ -675,8 +674,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
|
|||
if (rc) {
|
||||
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
|
||||
part->mbd.mtd->name, addr);
|
||||
if (rc)
|
||||
goto err;
|
||||
goto err;
|
||||
}
|
||||
|
||||
part->sector_map[sector] = addr;
|
||||
|
@ -695,8 +693,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
|
|||
if (rc) {
|
||||
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
|
||||
part->mbd.mtd->name, addr);
|
||||
if (rc)
|
||||
goto err;
|
||||
goto err;
|
||||
}
|
||||
block->used_sectors++;
|
||||
block->free_sectors--;
|
||||
|
|
|
@ -59,15 +59,12 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
|
|||
struct attribute_group *attr_group;
|
||||
struct attribute **attributes;
|
||||
struct sm_sysfs_attribute *vendor_attribute;
|
||||
char *vendor;
|
||||
|
||||
int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
|
||||
SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET);
|
||||
|
||||
char *vendor = kmalloc(vendor_len, GFP_KERNEL);
|
||||
vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
|
||||
SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL);
|
||||
if (!vendor)
|
||||
goto error1;
|
||||
memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len);
|
||||
vendor[vendor_len] = 0;
|
||||
|
||||
/* Initialize sysfs attributes */
|
||||
vendor_attribute =
|
||||
|
@ -78,7 +75,7 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
|
|||
sysfs_attr_init(&vendor_attribute->dev_attr.attr);
|
||||
|
||||
vendor_attribute->data = vendor;
|
||||
vendor_attribute->len = vendor_len;
|
||||
vendor_attribute->len = strlen(vendor);
|
||||
vendor_attribute->dev_attr.attr.name = "vendor";
|
||||
vendor_attribute->dev_attr.attr.mode = S_IRUGO;
|
||||
vendor_attribute->dev_attr.show = sm_attr_show;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#define pr_fmt(fmt) "mtd_test: " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/printk.h>
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#ifndef __UBI_UBI_H__
|
||||
#define __UBI_UBI_H__
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
|
|
@ -49,6 +49,40 @@ int of_get_nand_ecc_mode(struct device_node *np)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
|
||||
|
||||
/**
|
||||
* of_get_nand_ecc_step_size - Get ECC step size associated to
|
||||
* the required ECC strength (see below).
|
||||
* @np: Pointer to the given device_node
|
||||
*
|
||||
* return the ECC step size, or errno in error case.
|
||||
*/
|
||||
int of_get_nand_ecc_step_size(struct device_node *np)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
|
||||
return ret ? ret : val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size);
|
||||
|
||||
/**
|
||||
* of_get_nand_ecc_strength - Get required ECC strength over the
|
||||
* correspnding step size as defined by 'nand-ecc-size'
|
||||
* @np: Pointer to the given device_node
|
||||
*
|
||||
* return the ECC strength, or errno in error case.
|
||||
*/
|
||||
int of_get_nand_ecc_strength(struct device_node *np)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = of_property_read_u32(np, "nand-ecc-strength", &val);
|
||||
return ret ? ret : val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
|
||||
|
||||
/**
|
||||
* of_get_nand_bus_width - Get nand bus witdh for given device_node
|
||||
* @np: Pointer to the given device_node
|
||||
|
|
|
@ -33,7 +33,7 @@ static int jffs2_rtime_compress(unsigned char *data_in,
|
|||
unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen)
|
||||
{
|
||||
short positions[256];
|
||||
unsigned short positions[256];
|
||||
int outpos = 0;
|
||||
int pos=0;
|
||||
|
||||
|
@ -74,7 +74,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in,
|
|||
unsigned char *cpage_out,
|
||||
uint32_t srclen, uint32_t destlen)
|
||||
{
|
||||
short positions[256];
|
||||
unsigned short positions[256];
|
||||
int outpos = 0;
|
||||
int pos=0;
|
||||
|
||||
|
|
|
@ -457,12 +457,14 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
|
|||
The umask is only applied if there's no default ACL */
|
||||
ret = jffs2_init_acl_pre(dir_i, inode, &mode);
|
||||
if (ret) {
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
return ERR_PTR(ret);
|
||||
mutex_unlock(&f->sem);
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
ret = jffs2_do_new_inode (c, f, mode, ri);
|
||||
if (ret) {
|
||||
mutex_unlock(&f->sem);
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
return ERR_PTR(ret);
|
||||
|
@ -479,6 +481,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
|
|||
inode->i_size = 0;
|
||||
|
||||
if (insert_inode_locked(inode) < 0) {
|
||||
mutex_unlock(&f->sem);
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
|
|
@ -231,7 +231,7 @@ struct jffs2_tmp_dnode_info
|
|||
uint32_t version;
|
||||
uint32_t data_crc;
|
||||
uint32_t partial_crc;
|
||||
uint16_t csize;
|
||||
uint32_t csize;
|
||||
uint16_t overlapped;
|
||||
};
|
||||
|
||||
|
|
|
@ -179,6 +179,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
|||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&c->erase_wait, &wait);
|
||||
} else
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
} else if (ret)
|
||||
|
@ -211,20 +212,25 @@ out:
|
|||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, uint32_t sumsize)
|
||||
{
|
||||
int ret = -EAGAIN;
|
||||
int ret;
|
||||
minsize = PAD(minsize);
|
||||
|
||||
jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
while(ret == -EAGAIN) {
|
||||
while (true) {
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
|
||||
if (ret) {
|
||||
jffs2_dbg(1, "%s(): looping, ret is %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
if (ret == -EAGAIN)
|
||||
cond_resched();
|
||||
else
|
||||
break;
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (!ret)
|
||||
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
|
||||
|
||||
|
|
|
@ -204,12 +204,12 @@ struct mtd_info {
|
|||
struct mtd_oob_ops *ops);
|
||||
int (*_write_oob) (struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops);
|
||||
int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len);
|
||||
int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf);
|
||||
int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf);
|
||||
int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len);
|
||||
int (*_get_user_prot_info) (struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf);
|
||||
int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf);
|
||||
int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
|
||||
|
@ -278,12 +278,12 @@ static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
return mtd->_write_oob(mtd, to, ops);
|
||||
}
|
||||
|
||||
int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len);
|
||||
int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
|
||||
struct otp_info *buf);
|
||||
int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len);
|
||||
int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
|
||||
struct otp_info *buf);
|
||||
int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
|
|
@ -51,14 +51,6 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
|||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
|
||||
/*
|
||||
* This constant declares the max. oobsize / page, which
|
||||
* is supported now. If you add a chip with bigger oobsize/page
|
||||
* adjust this accordingly.
|
||||
*/
|
||||
#define NAND_MAX_OOBSIZE 744
|
||||
#define NAND_MAX_PAGESIZE 8192
|
||||
|
||||
/*
|
||||
* Constants for hardware specific CLE/ALE/NCE function
|
||||
*
|
||||
|
@ -350,6 +342,84 @@ struct nand_onfi_vendor_micron {
|
|||
u8 param_revision;
|
||||
} __packed;
|
||||
|
||||
struct jedec_ecc_info {
|
||||
u8 ecc_bits;
|
||||
u8 codeword_size;
|
||||
__le16 bb_per_lun;
|
||||
__le16 block_endurance;
|
||||
u8 reserved[2];
|
||||
} __packed;
|
||||
|
||||
/* JEDEC features */
|
||||
#define JEDEC_FEATURE_16_BIT_BUS (1 << 0)
|
||||
|
||||
struct nand_jedec_params {
|
||||
/* rev info and features block */
|
||||
/* 'J' 'E' 'S' 'D' */
|
||||
u8 sig[4];
|
||||
__le16 revision;
|
||||
__le16 features;
|
||||
u8 opt_cmd[3];
|
||||
__le16 sec_cmd;
|
||||
u8 num_of_param_pages;
|
||||
u8 reserved0[18];
|
||||
|
||||
/* manufacturer information block */
|
||||
char manufacturer[12];
|
||||
char model[20];
|
||||
u8 jedec_id[6];
|
||||
u8 reserved1[10];
|
||||
|
||||
/* memory organization block */
|
||||
__le32 byte_per_page;
|
||||
__le16 spare_bytes_per_page;
|
||||
u8 reserved2[6];
|
||||
__le32 pages_per_block;
|
||||
__le32 blocks_per_lun;
|
||||
u8 lun_count;
|
||||
u8 addr_cycles;
|
||||
u8 bits_per_cell;
|
||||
u8 programs_per_page;
|
||||
u8 multi_plane_addr;
|
||||
u8 multi_plane_op_attr;
|
||||
u8 reserved3[38];
|
||||
|
||||
/* electrical parameter block */
|
||||
__le16 async_sdr_speed_grade;
|
||||
__le16 toggle_ddr_speed_grade;
|
||||
__le16 sync_ddr_speed_grade;
|
||||
u8 async_sdr_features;
|
||||
u8 toggle_ddr_features;
|
||||
u8 sync_ddr_features;
|
||||
__le16 t_prog;
|
||||
__le16 t_bers;
|
||||
__le16 t_r;
|
||||
__le16 t_r_multi_plane;
|
||||
__le16 t_ccs;
|
||||
__le16 io_pin_capacitance_typ;
|
||||
__le16 input_pin_capacitance_typ;
|
||||
__le16 clk_pin_capacitance_typ;
|
||||
u8 driver_strength_support;
|
||||
__le16 t_ald;
|
||||
u8 reserved4[36];
|
||||
|
||||
/* ECC and endurance block */
|
||||
u8 guaranteed_good_blocks;
|
||||
__le16 guaranteed_block_endurance;
|
||||
struct jedec_ecc_info ecc_info[4];
|
||||
u8 reserved5[29];
|
||||
|
||||
/* reserved */
|
||||
u8 reserved6[148];
|
||||
|
||||
/* vendor */
|
||||
__le16 vendor_rev_num;
|
||||
u8 reserved7[88];
|
||||
|
||||
/* CRC for Parameter Page */
|
||||
__le16 crc;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
|
||||
* @lock: protection lock
|
||||
|
@ -418,7 +488,7 @@ struct nand_ecc_ctrl {
|
|||
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page);
|
||||
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len, uint8_t *buf);
|
||||
uint32_t offs, uint32_t len, uint8_t *buf, int page);
|
||||
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, uint32_t data_len,
|
||||
const uint8_t *data_buf, int oob_required);
|
||||
|
@ -435,17 +505,17 @@ struct nand_ecc_ctrl {
|
|||
|
||||
/**
|
||||
* struct nand_buffers - buffer structure for read/write
|
||||
* @ecccalc: buffer for calculated ECC
|
||||
* @ecccode: buffer for ECC read from flash
|
||||
* @databuf: buffer for data - dynamically sized
|
||||
* @ecccalc: buffer pointer for calculated ECC, size is oobsize.
|
||||
* @ecccode: buffer pointer for ECC read from flash, size is oobsize.
|
||||
* @databuf: buffer pointer for data, size is (page size + oobsize).
|
||||
*
|
||||
* Do not change the order of buffers. databuf and oobrbuf must be in
|
||||
* consecutive order.
|
||||
*/
|
||||
struct nand_buffers {
|
||||
uint8_t ecccalc[NAND_MAX_OOBSIZE];
|
||||
uint8_t ecccode[NAND_MAX_OOBSIZE];
|
||||
uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
|
||||
uint8_t *ecccalc;
|
||||
uint8_t *ecccode;
|
||||
uint8_t *databuf;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -523,8 +593,12 @@ struct nand_buffers {
|
|||
* @subpagesize: [INTERN] holds the subpagesize
|
||||
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
|
||||
* non 0 if ONFI supported.
|
||||
* @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded),
|
||||
* non 0 if JEDEC supported.
|
||||
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
|
||||
* supported, 0 otherwise.
|
||||
* @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is
|
||||
* supported, 0 otherwise.
|
||||
* @read_retries: [INTERN] the number of read retry modes supported
|
||||
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
|
||||
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
|
||||
|
@ -597,7 +671,11 @@ struct nand_chip {
|
|||
int badblockbits;
|
||||
|
||||
int onfi_version;
|
||||
struct nand_onfi_params onfi_params;
|
||||
int jedec_version;
|
||||
union {
|
||||
struct nand_onfi_params onfi_params;
|
||||
struct nand_jedec_params jedec_params;
|
||||
};
|
||||
|
||||
int read_retries;
|
||||
|
||||
|
@ -840,4 +918,29 @@ static inline bool nand_is_slc(struct nand_chip *chip)
|
|||
{
|
||||
return chip->bits_per_cell == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the opcode's address should be sent only on the lower 8 bits
|
||||
* @command: opcode to check
|
||||
*/
|
||||
static inline int nand_opcode_8bits(unsigned int command)
|
||||
{
|
||||
switch (command) {
|
||||
case NAND_CMD_READID:
|
||||
case NAND_CMD_PARAM:
|
||||
case NAND_CMD_GET_FEATURES:
|
||||
case NAND_CMD_SET_FEATURES:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return the supported JEDEC features. */
|
||||
static inline int jedec_feature(struct nand_chip *chip)
|
||||
{
|
||||
return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features)
|
||||
: 0;
|
||||
}
|
||||
#endif /* __LINUX_MTD_NAND_H */
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <linux/of.h>
|
||||
int of_get_nand_ecc_mode(struct device_node *np);
|
||||
int of_get_nand_ecc_step_size(struct device_node *np);
|
||||
int of_get_nand_ecc_strength(struct device_node *np);
|
||||
int of_get_nand_bus_width(struct device_node *np);
|
||||
bool of_get_nand_on_flash_bbt(struct device_node *np);
|
||||
|
||||
|
@ -23,6 +25,16 @@ static inline int of_get_nand_ecc_mode(struct device_node *np)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int of_get_nand_ecc_step_size(struct device_node *np)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int of_get_nand_ecc_strength(struct device_node *np)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int of_get_nand_bus_width(struct device_node *np)
|
||||
{
|
||||
return -ENOSYS;
|
||||
|
|
|
@ -26,13 +26,6 @@ enum bch_ecc {
|
|||
/* ELM support 8 error syndrome process */
|
||||
#define ERROR_VECTOR_MAX 8
|
||||
|
||||
#define BCH8_ECC_OOB_BYTES 13
|
||||
#define BCH4_ECC_OOB_BYTES 7
|
||||
/* RBL requires 14 byte even though BCH8 uses only 13 byte */
|
||||
#define BCH8_SIZE (BCH8_ECC_OOB_BYTES + 1)
|
||||
/* Uses 1 extra byte to handle erased pages */
|
||||
#define BCH4_SIZE (BCH4_ECC_OOB_BYTES + 1)
|
||||
|
||||
/**
|
||||
* struct elm_errorvec - error vector for elm
|
||||
* @error_reported: set true for vectors error is reported
|
||||
|
@ -50,5 +43,6 @@ struct elm_errorvec {
|
|||
|
||||
void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
|
||||
struct elm_errorvec *err_vec);
|
||||
int elm_config(struct device *dev, enum bch_ecc bch_type);
|
||||
int elm_config(struct device *dev, enum bch_ecc bch_type,
|
||||
int ecc_steps, int ecc_step_size, int ecc_syndrome_size);
|
||||
#endif /* __ELM_H */
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* arch/arm/mach-s3c2410/include/mach/nand.h
|
||||
*
|
||||
/*
|
||||
* Copyright (c) 2004 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
|
@ -10,6 +9,9 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MTD_NAND_S3C2410_H
|
||||
#define __MTD_NAND_S3C2410_H
|
||||
|
||||
/**
|
||||
* struct s3c2410_nand_set - define a set of one or more nand chips
|
||||
* @disable_ecc: Entirely disable ECC - Dangerous
|
||||
|
@ -65,3 +67,5 @@ struct s3c2410_platform_nand {
|
|||
* it with the s3c_device_nand. This allows @nand to be __initdata.
|
||||
*/
|
||||
extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand);
|
||||
|
||||
#endif /*__MTD_NAND_S3C2410_H */
|
||||
|
|
Loading…
Reference in New Issue