mirror of
https://github.com/joel16/android_kernel_sony_msm8994_rework.git
synced 2024-12-29 07:44:24 +00:00
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (82 commits) mtd: fix build error in m25p80.c mtd: Remove redundant mutex from mtd_blkdevs.c MTD: Fix wrong check register_blkdev return value Revert "mtd: cleanup Kconfig dependencies" mtd: cfi_cmdset_0002: make sector erase command variable mtd: cfi_cmdset_0002: add CFI detection for SST 38VF640x chips mtd: cfi_util: add support for switching SST 39VF640xB chips into QRY mode mtd: cfi_cmdset_0001: use defined value of P_ID_INTEL_PERFORMANCE instead of hardcoded one block2mtd: dubious assignment P4080/mtd: Fix the freescale lbc issue with 36bit mode P4080/eLBC: Make Freescale elbc interrupt common to elbc devices mtd: phram: use KBUILD_MODNAME mtd: OneNAND: S5PC110: Fix double call suspend & resume function mtd: nand: fix MTD_MODE_RAW writes jffs2: use kmemdup mtd: sm_ftl: cosmetic, use bool when possible mtd: r852: remove useless pci powerup/down from suspend/resume routines mtd: blktrans: fix a race vs kthread_stop mtd: blktrans: kill BKL mtd: allow to unload the mtdtrans module if its block devices aren't open ... Fix up trivial whitespace-introduced conflict in drivers/mtd/mtdchar.c
This commit is contained in:
commit
79346507ad
@ -66,7 +66,7 @@ static DEFINE_SPINLOCK(syscon_resetreg_lock);
|
||||
* AMBA bus
|
||||
* |
|
||||
* +- CPU
|
||||
* +- NANDIF NAND Flash interface
|
||||
* +- FSMC NANDIF NAND Flash interface
|
||||
* +- SEMI Shared Memory interface
|
||||
* +- ISP Image Signal Processor (U335 only)
|
||||
* +- CDS (U335 only)
|
||||
@ -726,7 +726,7 @@ static struct clk cpu_clk = {
|
||||
};
|
||||
|
||||
static struct clk nandif_clk = {
|
||||
.name = "NANDIF",
|
||||
.name = "FSMC",
|
||||
.parent = &amba_clk,
|
||||
.hw_ctrld = false,
|
||||
.reset = true,
|
||||
@ -1259,7 +1259,7 @@ static struct clk_lookup lookups[] = {
|
||||
/* Connected directly to the AMBA bus */
|
||||
DEF_LOOKUP("amba", &amba_clk),
|
||||
DEF_LOOKUP("cpu", &cpu_clk),
|
||||
DEF_LOOKUP("fsmc", &nandif_clk),
|
||||
DEF_LOOKUP("fsmc-nand", &nandif_clk),
|
||||
DEF_LOOKUP("semi", &semi_clk),
|
||||
#ifdef CONFIG_MACH_U300_BS335
|
||||
DEF_LOOKUP("isp", &isp_clk),
|
||||
|
@ -21,7 +21,8 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <mach/coh901318.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/fsmc.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/setup.h>
|
||||
@ -30,6 +31,7 @@
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
#include <mach/coh901318.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/syscon.h>
|
||||
#include <mach/dma_channels.h>
|
||||
@ -285,6 +287,13 @@ static struct resource rtc_resources[] = {
|
||||
*/
|
||||
static struct resource fsmc_resources[] = {
|
||||
{
|
||||
.name = "nand_data",
|
||||
.start = U300_NAND_CS0_PHYS_BASE,
|
||||
.end = U300_NAND_CS0_PHYS_BASE + SZ_16K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.name = "fsmc_regs",
|
||||
.start = U300_NAND_IF_PHYS_BASE,
|
||||
.end = U300_NAND_IF_PHYS_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
@ -1429,11 +1438,39 @@ static struct platform_device rtc_device = {
|
||||
.resource = rtc_resources,
|
||||
};
|
||||
|
||||
static struct platform_device fsmc_device = {
|
||||
.name = "nandif",
|
||||
static struct mtd_partition u300_partitions[] = {
|
||||
{
|
||||
.name = "bootrecords",
|
||||
.offset = 0,
|
||||
.size = SZ_128K,
|
||||
},
|
||||
{
|
||||
.name = "free",
|
||||
.offset = SZ_128K,
|
||||
.size = 8064 * SZ_1K,
|
||||
},
|
||||
{
|
||||
.name = "platform",
|
||||
.offset = 8192 * SZ_1K,
|
||||
.size = 253952 * SZ_1K,
|
||||
},
|
||||
};
|
||||
|
||||
static struct fsmc_nand_platform_data nand_platform_data = {
|
||||
.partitions = u300_partitions,
|
||||
.nr_partitions = ARRAY_SIZE(u300_partitions),
|
||||
.options = NAND_SKIP_BBTSCAN,
|
||||
.width = FSMC_NAND_BW8,
|
||||
};
|
||||
|
||||
static struct platform_device nand_device = {
|
||||
.name = "fsmc-nand",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(fsmc_resources),
|
||||
.resource = fsmc_resources,
|
||||
.num_resources = ARRAY_SIZE(fsmc_resources),
|
||||
.dev = {
|
||||
.platform_data = &nand_platform_data,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device ave_device = {
|
||||
@ -1465,7 +1502,7 @@ static struct platform_device *platform_devs[] __initdata = {
|
||||
&keypad_device,
|
||||
&rtc_device,
|
||||
&gpio_device,
|
||||
&fsmc_device,
|
||||
&nand_device,
|
||||
&wdog_device,
|
||||
&ave_device
|
||||
};
|
||||
|
@ -20,11 +20,9 @@
|
||||
|
||||
/* NAND Flash CS0 */
|
||||
#define U300_NAND_CS0_PHYS_BASE 0x80000000
|
||||
#define U300_NAND_CS0_VIRT_BASE 0xff040000
|
||||
|
||||
/* NFIF */
|
||||
#define U300_NAND_IF_PHYS_BASE 0x9f800000
|
||||
#define U300_NAND_IF_VIRT_BASE 0xff030000
|
||||
|
||||
/* AHB Peripherals */
|
||||
#define U300_AHB_PER_PHYS_BASE 0xa0000000
|
||||
|
@ -30,15 +30,15 @@ struct pxa3xx_nand_cmdset {
|
||||
};
|
||||
|
||||
struct pxa3xx_nand_flash {
|
||||
const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
|
||||
const struct pxa3xx_nand_cmdset *cmdset;
|
||||
uint32_t chip_id;
|
||||
unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */
|
||||
unsigned int page_size; /* Page size in bytes (PAGE_SZ) */
|
||||
unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
|
||||
unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
|
||||
unsigned int num_blocks; /* Number of physical blocks in Flash */
|
||||
|
||||
uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */
|
||||
uint32_t page_size; /* Page size in bytes (PAGE_SZ) */
|
||||
uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */
|
||||
uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */
|
||||
uint32_t num_blocks; /* Number of physical blocks in Flash */
|
||||
uint32_t chip_id;
|
||||
struct pxa3xx_nand_cmdset *cmdset; /* NAND command set */
|
||||
struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
|
||||
};
|
||||
|
||||
struct pxa3xx_nand_platform_data {
|
||||
|
97
arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h
Normal file
97
arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h
Normal file
@ -0,0 +1,97 @@
|
||||
#ifndef __BCM963XX_TAG_H
|
||||
#define __BCM963XX_TAG_H
|
||||
|
||||
#define TAGVER_LEN 4 /* Length of Tag Version */
|
||||
#define TAGLAYOUT_LEN 4 /* Length of FlashLayoutVer */
|
||||
#define SIG1_LEN 20 /* Company Signature 1 Length */
|
||||
#define SIG2_LEN 14 /* Company Signature 2 Lenght */
|
||||
#define BOARDID_LEN 16 /* Length of BoardId */
|
||||
#define ENDIANFLAG_LEN 2 /* Endian Flag Length */
|
||||
#define CHIPID_LEN 6 /* Chip Id Length */
|
||||
#define IMAGE_LEN 10 /* Length of Length Field */
|
||||
#define ADDRESS_LEN 12 /* Length of Address field */
|
||||
#define DUALFLAG_LEN 2 /* Dual Image flag Length */
|
||||
#define INACTIVEFLAG_LEN 2 /* Inactie Flag Length */
|
||||
#define RSASIG_LEN 20 /* Length of RSA Signature in tag */
|
||||
#define TAGINFO1_LEN 30 /* Length of vendor information field1 in tag */
|
||||
#define FLASHLAYOUTVER_LEN 4 /* Length of Flash Layout Version String tag */
|
||||
#define TAGINFO2_LEN 16 /* Length of vendor information field2 in tag */
|
||||
#define CRC_LEN 4 /* Length of CRC in bytes */
|
||||
#define ALTTAGINFO_LEN 54 /* Alternate length for vendor information; Pirelli */
|
||||
|
||||
#define NUM_PIRELLI 2
|
||||
#define IMAGETAG_CRC_START 0xFFFFFFFF
|
||||
|
||||
#define PIRELLI_BOARDS { \
|
||||
"AGPF-S0", \
|
||||
"DWV-S0", \
|
||||
}
|
||||
|
||||
/*
|
||||
* The broadcom firmware assumes the rootfs starts the image,
|
||||
* therefore uses the rootfs start (flash_image_address)
|
||||
* to determine where to flash the image. Since we have the kernel first
|
||||
* we have to give it the kernel address, but the crc uses the length
|
||||
* associated with this address (root_length), which is added to the kernel
|
||||
* length (kernel_length) to determine the length of image to flash and thus
|
||||
* needs to be rootfs + deadcode (jffs2 EOF marker)
|
||||
*/
|
||||
|
||||
struct bcm_tag {
|
||||
/* 0-3: Version of the image tag */
|
||||
char tag_version[TAGVER_LEN];
|
||||
/* 4-23: Company Line 1 */
|
||||
char sig_1[SIG1_LEN];
|
||||
/* 24-37: Company Line 2 */
|
||||
char sig_2[SIG2_LEN];
|
||||
/* 38-43: Chip this image is for */
|
||||
char chip_id[CHIPID_LEN];
|
||||
/* 44-59: Board name */
|
||||
char board_id[BOARDID_LEN];
|
||||
/* 60-61: Map endianness -- 1 BE 0 LE */
|
||||
char big_endian[ENDIANFLAG_LEN];
|
||||
/* 62-71: Total length of image */
|
||||
char total_length[IMAGE_LEN];
|
||||
/* 72-83: Address in memory of CFE */
|
||||
char cfe__address[ADDRESS_LEN];
|
||||
/* 84-93: Size of CFE */
|
||||
char cfe_length[IMAGE_LEN];
|
||||
/* 94-105: Address in memory of image start
|
||||
* (kernel for OpenWRT, rootfs for stock firmware)
|
||||
*/
|
||||
char flash_image_start[ADDRESS_LEN];
|
||||
/* 106-115: Size of rootfs */
|
||||
char root_length[IMAGE_LEN];
|
||||
/* 116-127: Address in memory of kernel */
|
||||
char kernel_address[ADDRESS_LEN];
|
||||
/* 128-137: Size of kernel */
|
||||
char kernel_length[IMAGE_LEN];
|
||||
/* 138-139: Unused at the moment */
|
||||
char dual_image[DUALFLAG_LEN];
|
||||
/* 140-141: Unused at the moment */
|
||||
char inactive_flag[INACTIVEFLAG_LEN];
|
||||
/* 142-161: RSA Signature (not used; some vendors may use this) */
|
||||
char rsa_signature[RSASIG_LEN];
|
||||
/* 162-191: Compilation and related information (not used in OpenWrt) */
|
||||
char information1[TAGINFO1_LEN];
|
||||
/* 192-195: Version flash layout */
|
||||
char flash_layout_ver[FLASHLAYOUTVER_LEN];
|
||||
/* 196-199: kernel+rootfs CRC32 */
|
||||
char fskernel_crc[CRC_LEN];
|
||||
/* 200-215: Unused except on Alice Gate where is is information */
|
||||
char information2[TAGINFO2_LEN];
|
||||
/* 216-219: CRC32 of image less imagetag (kernel for Alice Gate) */
|
||||
char image_crc[CRC_LEN];
|
||||
/* 220-223: CRC32 of rootfs partition */
|
||||
char rootfs_crc[CRC_LEN];
|
||||
/* 224-227: CRC32 of kernel partition */
|
||||
char kernel_crc[CRC_LEN];
|
||||
/* 228-235: Unused at present */
|
||||
char reserved1[8];
|
||||
/* 236-239: CRC32 of header excluding tagVersion */
|
||||
char header_crc[CRC_LEN];
|
||||
/* 240-255: Unused at present */
|
||||
char reserved2[16];
|
||||
};
|
||||
|
||||
#endif /* __BCM63XX_TAG_H */
|
@ -682,9 +682,12 @@ config 4xx_SOC
|
||||
bool
|
||||
|
||||
config FSL_LBC
|
||||
bool
|
||||
bool "Freescale Local Bus support"
|
||||
depends on FSL_SOC
|
||||
help
|
||||
Freescale Localbus support
|
||||
Enables reporting of errors from the Freescale local bus
|
||||
controller. Also contains some common code used by
|
||||
drivers for specific local bus peripherals.
|
||||
|
||||
config FSL_GTM
|
||||
bool
|
||||
|
@ -1,9 +1,10 @@
|
||||
/* Freescale Local Bus Controller
|
||||
*
|
||||
* Copyright (c) 2006-2007 Freescale Semiconductor
|
||||
* Copyright © 2006-2007, 2010 Freescale Semiconductor
|
||||
*
|
||||
* Authors: Nick Spence <nick.spence@freescale.com>,
|
||||
* Scott Wood <scottwood@freescale.com>
|
||||
* Jack Lan <jack.lan@freescale.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,6 +27,8 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct fsl_lbc_bank {
|
||||
__be32 br; /**< Base Register */
|
||||
@ -125,13 +128,23 @@ struct fsl_lbc_regs {
|
||||
#define LTESR_ATMW 0x00800000
|
||||
#define LTESR_ATMR 0x00400000
|
||||
#define LTESR_CS 0x00080000
|
||||
#define LTESR_UPM 0x00000002
|
||||
#define LTESR_CC 0x00000001
|
||||
#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
|
||||
#define LTESR_MASK (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
|
||||
| LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
|
||||
| LTESR_CC)
|
||||
#define LTESR_CLEAR 0xFFFFFFFF
|
||||
#define LTECCR_CLEAR 0xFFFFFFFF
|
||||
#define LTESR_STATUS LTESR_MASK
|
||||
#define LTEIR_ENABLE LTESR_MASK
|
||||
#define LTEDR_ENABLE 0x00000000
|
||||
__be32 ltedr; /**< Transfer Error Disable Register */
|
||||
__be32 lteir; /**< Transfer Error Interrupt Register */
|
||||
__be32 lteatr; /**< Transfer Error Attributes Register */
|
||||
__be32 ltear; /**< Transfer Error Address Register */
|
||||
u8 res6[0xC];
|
||||
__be32 lteccr; /**< Transfer Error ECC Register */
|
||||
u8 res6[0x8];
|
||||
__be32 lbcr; /**< Configuration Register */
|
||||
#define LBCR_LDIS 0x80000000
|
||||
#define LBCR_LDIS_SHIFT 31
|
||||
@ -235,6 +248,7 @@ struct fsl_upm {
|
||||
int width;
|
||||
};
|
||||
|
||||
extern u32 fsl_lbc_addr(phys_addr_t addr_base);
|
||||
extern int fsl_lbc_find(phys_addr_t addr_base);
|
||||
extern int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm);
|
||||
|
||||
@ -265,7 +279,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
/* overview of the fsl lbc controller */
|
||||
|
||||
struct fsl_lbc_ctrl {
|
||||
/* device info */
|
||||
struct device *dev;
|
||||
struct fsl_lbc_regs __iomem *regs;
|
||||
int irq;
|
||||
wait_queue_head_t irq_wait;
|
||||
spinlock_t lock;
|
||||
void *nand;
|
||||
|
||||
/* status read from LTESR by irq handler */
|
||||
unsigned int irq_status;
|
||||
};
|
||||
|
||||
extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
|
||||
u32 mar);
|
||||
extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
|
||||
|
||||
#endif /* __ASM_FSL_LBC_H */
|
||||
|
@ -1,9 +1,12 @@
|
||||
/*
|
||||
* Freescale LBC and UPM routines.
|
||||
*
|
||||
* Copyright (c) 2007-2008 MontaVista Software, Inc.
|
||||
* Copyright © 2007-2008 MontaVista Software, Inc.
|
||||
* Copyright © 2010 Freescale Semiconductor
|
||||
*
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
* Author: Jack Lan <Jack.Lan@freescale.com>
|
||||
* Author: Roy Zang <tie-fei.zang@freescale.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -19,39 +22,37 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/fsl_lbc.h>
|
||||
|
||||
static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
|
||||
static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
|
||||
struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
|
||||
EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
|
||||
|
||||
static char __initdata *compat_lbc[] = {
|
||||
"fsl,pq2-localbus",
|
||||
"fsl,pq2pro-localbus",
|
||||
"fsl,pq3-localbus",
|
||||
"fsl,elbc",
|
||||
};
|
||||
|
||||
static int __init fsl_lbc_init(void)
|
||||
/**
|
||||
* fsl_lbc_addr - convert the base address
|
||||
* @addr_base: base address of the memory bank
|
||||
*
|
||||
* This function converts a base address of lbc into the right format for the
|
||||
* BR register. If the SOC has eLBC then it returns 32bit physical address
|
||||
* else it convers a 34bit local bus physical address to correct format of
|
||||
* 32bit address for BR register (Example: MPC8641).
|
||||
*/
|
||||
u32 fsl_lbc_addr(phys_addr_t addr_base)
|
||||
{
|
||||
struct device_node *lbus;
|
||||
int i;
|
||||
struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node;
|
||||
u32 addr = addr_base & 0xffff8000;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
|
||||
lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
|
||||
if (lbus)
|
||||
goto found;
|
||||
}
|
||||
return -ENODEV;
|
||||
if (of_device_is_compatible(np, "fsl,elbc"))
|
||||
return addr;
|
||||
|
||||
found:
|
||||
fsl_lbc_regs = of_iomap(lbus, 0);
|
||||
of_node_put(lbus);
|
||||
if (!fsl_lbc_regs)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
return addr | ((addr_base & 0x300000000ull) >> 19);
|
||||
}
|
||||
arch_initcall(fsl_lbc_init);
|
||||
EXPORT_SYMBOL(fsl_lbc_addr);
|
||||
|
||||
/**
|
||||
* fsl_lbc_find - find Localbus bank
|
||||
@ -65,15 +66,17 @@ arch_initcall(fsl_lbc_init);
|
||||
int fsl_lbc_find(phys_addr_t addr_base)
|
||||
{
|
||||
int i;
|
||||
struct fsl_lbc_regs __iomem *lbc;
|
||||
|
||||
if (!fsl_lbc_regs)
|
||||
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
|
||||
__be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
|
||||
__be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
|
||||
lbc = fsl_lbc_ctrl_dev->regs;
|
||||
for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
|
||||
__be32 br = in_be32(&lbc->bank[i].br);
|
||||
__be32 or = in_be32(&lbc->bank[i].or);
|
||||
|
||||
if (br & BR_V && (br & or & BR_BA) == addr_base)
|
||||
if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base))
|
||||
return i;
|
||||
}
|
||||
|
||||
@ -94,22 +97,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
|
||||
{
|
||||
int bank;
|
||||
__be32 br;
|
||||
struct fsl_lbc_regs __iomem *lbc;
|
||||
|
||||
bank = fsl_lbc_find(addr_base);
|
||||
if (bank < 0)
|
||||
return bank;
|
||||
|
||||
br = in_be32(&fsl_lbc_regs->bank[bank].br);
|
||||
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
|
||||
return -ENODEV;
|
||||
|
||||
lbc = fsl_lbc_ctrl_dev->regs;
|
||||
br = in_be32(&lbc->bank[bank].br);
|
||||
|
||||
switch (br & BR_MSEL) {
|
||||
case BR_MS_UPMA:
|
||||
upm->mxmr = &fsl_lbc_regs->mamr;
|
||||
upm->mxmr = &lbc->mamr;
|
||||
break;
|
||||
case BR_MS_UPMB:
|
||||
upm->mxmr = &fsl_lbc_regs->mbmr;
|
||||
upm->mxmr = &lbc->mbmr;
|
||||
break;
|
||||
case BR_MS_UPMC:
|
||||
upm->mxmr = &fsl_lbc_regs->mcmr;
|
||||
upm->mxmr = &lbc->mcmr;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -148,9 +156,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&fsl_lbc_lock, flags);
|
||||
|
||||
out_be32(&fsl_lbc_regs->mar, mar);
|
||||
out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
|
||||
|
||||
switch (upm->width) {
|
||||
case 8:
|
||||
@ -172,3 +183,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fsl_upm_run_pattern);
|
||||
|
||||
static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
|
||||
{
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
|
||||
/* clear event registers */
|
||||
setbits32(&lbc->ltesr, LTESR_CLEAR);
|
||||
out_be32(&lbc->lteatr, 0);
|
||||
out_be32(&lbc->ltear, 0);
|
||||
out_be32(&lbc->lteccr, LTECCR_CLEAR);
|
||||
out_be32(&lbc->ltedr, LTEDR_ENABLE);
|
||||
|
||||
/* Enable interrupts for any detected events */
|
||||
out_be32(&lbc->lteir, LTEIR_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: This interrupt is used to report localbus events of various kinds,
|
||||
* such as transaction errors on the chipselects.
|
||||
*/
|
||||
|
||||
static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
|
||||
{
|
||||
struct fsl_lbc_ctrl *ctrl = data;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
u32 status;
|
||||
|
||||
status = in_be32(&lbc->ltesr);
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
out_be32(&lbc->ltesr, LTESR_CLEAR);
|
||||
out_be32(&lbc->lteatr, 0);
|
||||
out_be32(&lbc->ltear, 0);
|
||||
ctrl->irq_status = status;
|
||||
|
||||
if (status & LTESR_BM)
|
||||
dev_err(ctrl->dev, "Local bus monitor time-out: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
if (status & LTESR_WP)
|
||||
dev_err(ctrl->dev, "Write protect error: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
if (status & LTESR_ATMW)
|
||||
dev_err(ctrl->dev, "Atomic write error: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
if (status & LTESR_ATMR)
|
||||
dev_err(ctrl->dev, "Atomic read error: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
if (status & LTESR_CS)
|
||||
dev_err(ctrl->dev, "Chip select error: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
if (status & LTESR_UPM)
|
||||
;
|
||||
if (status & LTESR_FCT) {
|
||||
dev_err(ctrl->dev, "FCM command time-out: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
smp_wmb();
|
||||
wake_up(&ctrl->irq_wait);
|
||||
}
|
||||
if (status & LTESR_PAR) {
|
||||
dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
smp_wmb();
|
||||
wake_up(&ctrl->irq_wait);
|
||||
}
|
||||
if (status & LTESR_CC) {
|
||||
smp_wmb();
|
||||
wake_up(&ctrl->irq_wait);
|
||||
}
|
||||
if (status & ~LTESR_MASK)
|
||||
dev_err(ctrl->dev, "Unknown error: "
|
||||
"LTESR 0x%08X\n", status);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* fsl_lbc_ctrl_probe
|
||||
*
|
||||
* called by device layer when it finds a device matching
|
||||
* one our driver can handled. This code allocates all of
|
||||
* the resources needed for the controller only. The
|
||||
* resources for the NAND banks themselves are allocated
|
||||
* in the chip probe function.
|
||||
*/
|
||||
|
||||
static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dev->dev.of_node) {
|
||||
dev_err(&dev->dev, "Device OF-Node is NULL");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
|
||||
if (!fsl_lbc_ctrl_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
|
||||
|
||||
spin_lock_init(&fsl_lbc_ctrl_dev->lock);
|
||||
init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
|
||||
|
||||
fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
|
||||
if (!fsl_lbc_ctrl_dev->regs) {
|
||||
dev_err(&dev->dev, "failed to get memory region\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
|
||||
if (fsl_lbc_ctrl_dev->irq == NO_IRQ) {
|
||||
dev_err(&dev->dev, "failed to get irq resource\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
fsl_lbc_ctrl_dev->dev = &dev->dev;
|
||||
|
||||
ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0,
|
||||
"fsl-lbc", fsl_lbc_ctrl_dev);
|
||||
if (ret != 0) {
|
||||
dev_err(&dev->dev, "failed to install irq (%d)\n",
|
||||
fsl_lbc_ctrl_dev->irq);
|
||||
ret = fsl_lbc_ctrl_dev->irq;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
iounmap(fsl_lbc_ctrl_dev->regs);
|
||||
kfree(fsl_lbc_ctrl_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_lbc_match[] = {
|
||||
{ .compatible = "fsl,elbc", },
|
||||
{ .compatible = "fsl,pq3-localbus", },
|
||||
{ .compatible = "fsl,pq2-localbus", },
|
||||
{ .compatible = "fsl,pq2pro-localbus", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_lbc_ctrl_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-lbc",
|
||||
.of_match_table = fsl_lbc_match,
|
||||
},
|
||||
.probe = fsl_lbc_ctrl_probe,
|
||||
};
|
||||
|
||||
static int __init fsl_lbc_init(void)
|
||||
{
|
||||
return platform_driver_register(&fsl_lbc_ctrl_driver);
|
||||
}
|
||||
module_init(fsl_lbc_init);
|
||||
|
@ -1496,7 +1496,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
||||
|
||||
switch (mode) {
|
||||
case FL_WRITING:
|
||||
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41);
|
||||
write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0x40) : CMD(0x41);
|
||||
break;
|
||||
case FL_OTP_WRITE:
|
||||
write_cmd = CMD(0xc0);
|
||||
@ -1661,7 +1661,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
cmd_adr = adr & ~(wbufsize-1);
|
||||
|
||||
/* Let's determine this according to the interleave only once */
|
||||
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9);
|
||||
write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
ret = get_chip(map, chip, cmd_adr, FL_WRITING);
|
||||
|
@ -291,6 +291,23 @@ static void fixup_sst39vf_rev_b(struct mtd_info *mtd, void *param)
|
||||
|
||||
cfi->addr_unlock1 = 0x555;
|
||||
cfi->addr_unlock2 = 0x2AA;
|
||||
|
||||
cfi->sector_erase_cmd = CMD(0x50);
|
||||
}
|
||||
|
||||
static void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd, void *param)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
|
||||
fixup_sst39vf_rev_b(mtd, param);
|
||||
|
||||
/*
|
||||
* CFI reports 1024 sectors (0x03ff+1) of 64KBytes (0x0100*256) where
|
||||
* it should report a size of 8KBytes (0x0020*256).
|
||||
*/
|
||||
cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
|
||||
pr_warning("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n", mtd->name);
|
||||
}
|
||||
|
||||
static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param)
|
||||
@ -317,14 +334,14 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
|
||||
|
||||
/* Used to fix CFI-Tables of chips without Extended Query Tables */
|
||||
static struct cfi_fixup cfi_nopri_fixup_table[] = {
|
||||
{ CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, // SST39VF1602
|
||||
{ CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601
|
||||
{ CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202
|
||||
{ CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201
|
||||
{ CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, // SST39VF3202B
|
||||
{ CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, // SST39VF3201B
|
||||
{ CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, // SST39VF6402B
|
||||
{ CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, // SST39VF6401B
|
||||
{ CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, /* SST39VF1602 */
|
||||
{ CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, /* SST39VF1601 */
|
||||
{ CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, /* SST39VF3202 */
|
||||
{ CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, /* SST39VF3201 */
|
||||
{ CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3202B */
|
||||
{ CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3201B */
|
||||
{ CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6402B */
|
||||
{ CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6401B */
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
@ -344,6 +361,10 @@ static struct cfi_fixup cfi_fixup_table[] = {
|
||||
{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
|
||||
{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
|
||||
{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
|
||||
{ CFI_MFR_SST, 0x536A, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6402 */
|
||||
{ CFI_MFR_SST, 0x536B, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6401 */
|
||||
{ CFI_MFR_SST, 0x536C, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6404 */
|
||||
{ CFI_MFR_SST, 0x536D, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6403 */
|
||||
#if !FORCE_WORD_WRITE
|
||||
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
|
||||
#endif
|
||||
@ -374,6 +395,13 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi,
|
||||
if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e &&
|
||||
extp->MajorVersion == '0')
|
||||
extp->MajorVersion = '1';
|
||||
/*
|
||||
* SST 38VF640x chips report major=0xFF / minor=0xFF.
|
||||
*/
|
||||
if (cfi->mfr == CFI_MFR_SST && (cfi->id >> 4) == 0x0536) {
|
||||
extp->MajorVersion = '1';
|
||||
extp->MinorVersion = '0';
|
||||
}
|
||||
}
|
||||
|
||||
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
||||
@ -545,15 +573,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
|
||||
printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
|
||||
goto setup_err;
|
||||
}
|
||||
#if 0
|
||||
// debug
|
||||
for (i=0; i<mtd->numeraseregions;i++){
|
||||
printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
|
||||
i,mtd->eraseregions[i].offset,
|
||||
mtd->eraseregions[i].erasesize,
|
||||
mtd->eraseregions[i].numblocks);
|
||||
}
|
||||
#endif
|
||||
|
||||
__module_get(THIS_MODULE);
|
||||
register_reboot_notifier(&mtd->reboot_notifier);
|
||||
@ -674,7 +693,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
||||
* there was an error (so leave the erase
|
||||
* routine to recover from it) or we trying to
|
||||
* use the erase-in-progress sector. */
|
||||
map_write(map, CMD(0x30), chip->in_progress_block_addr);
|
||||
map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
|
||||
chip->state = FL_ERASING;
|
||||
chip->oldstate = FL_READY;
|
||||
printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
|
||||
@ -727,7 +746,7 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
|
||||
switch(chip->oldstate) {
|
||||
case FL_ERASING:
|
||||
chip->state = chip->oldstate;
|
||||
map_write(map, CMD(0x30), chip->in_progress_block_addr);
|
||||
map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
|
||||
chip->oldstate = FL_READY;
|
||||
chip->state = FL_ERASING;
|
||||
break;
|
||||
@ -870,7 +889,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
||||
local_irq_disable();
|
||||
|
||||
/* Resume the write or erase operation */
|
||||
map_write(map, CMD(0x30), adr);
|
||||
map_write(map, cfi->sector_erase_cmd, adr);
|
||||
chip->state = oldstate;
|
||||
start = xip_currtime();
|
||||
} else if (usec >= 1000000/HZ) {
|
||||
@ -1025,9 +1044,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
|
||||
mutex_lock(&chip->mutex);
|
||||
|
||||
if (chip->state != FL_READY){
|
||||
#if 0
|
||||
printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
|
||||
#endif
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
@ -1035,10 +1051,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
#if 0
|
||||
if(signal_pending(current))
|
||||
return -EINTR;
|
||||
#endif
|
||||
timeo = jiffies + HZ;
|
||||
|
||||
goto retry;
|
||||
@ -1246,9 +1258,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
mutex_lock(&cfi->chips[chipnum].mutex);
|
||||
|
||||
if (cfi->chips[chipnum].state != FL_READY) {
|
||||
#if 0
|
||||
printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
|
||||
#endif
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
|
||||
@ -1256,10 +1265,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
#if 0
|
||||
if(signal_pending(current))
|
||||
return -EINTR;
|
||||
#endif
|
||||
goto retry;
|
||||
}
|
||||
|
||||
@ -1324,9 +1329,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
mutex_lock(&cfi->chips[chipnum].mutex);
|
||||
|
||||
if (cfi->chips[chipnum].state != FL_READY) {
|
||||
#if 0
|
||||
printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
|
||||
#endif
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
|
||||
@ -1334,10 +1336,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
#if 0
|
||||
if(signal_pending(current))
|
||||
return -EINTR;
|
||||
#endif
|
||||
goto retry1;
|
||||
}
|
||||
|
||||
@ -1396,7 +1394,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
|
||||
/* Write Buffer Load */
|
||||
map_write(map, CMD(0x25), cmd_adr);
|
||||
@ -1675,7 +1672,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
map_write(map, CMD(0x30), adr);
|
||||
map_write(map, cfi->sector_erase_cmd, adr);
|
||||
|
||||
chip->state = FL_ERASING;
|
||||
chip->erase_suspended = 0;
|
||||
|
@ -177,6 +177,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
|
||||
|
||||
cfi->cfi_mode = CFI_MODE_CFI;
|
||||
|
||||
cfi->sector_erase_cmd = CMD(0x30);
|
||||
|
||||
/* Read the CFI info structure */
|
||||
xip_disable_qry(base, map, cfi);
|
||||
for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)
|
||||
|
@ -75,6 +75,13 @@ int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
|
||||
cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
if (cfi_qry_present(map, base, cfi))
|
||||
return 1;
|
||||
/* SST 39VF640xB */
|
||||
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0xAA, 0x555, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, 0x2AA, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
|
||||
if (cfi_qry_present(map, base, cfi))
|
||||
return 1;
|
||||
/* QRY not found */
|
||||
|
@ -91,7 +91,6 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
} else
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
mtd_erase_callback(instr);
|
||||
return err;
|
||||
}
|
||||
|
@ -661,11 +661,14 @@ static const struct spi_device_id m25p_ids[] = {
|
||||
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
|
||||
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
|
||||
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
|
||||
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SECT_4K) },
|
||||
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
|
||||
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
||||
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
||||
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
|
||||
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
|
||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) },
|
||||
@ -714,6 +717,7 @@ static const struct spi_device_id m25p_ids[] = {
|
||||
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
|
||||
/* Catalyst / On Semiconductor -- non-JEDEC */
|
||||
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
|
||||
@ -924,7 +928,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
nr_parts = data->nr_parts;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
#ifdef CONFIG_MTD_OF_PARTS
|
||||
if (nr_parts <= 0 && spi->dev.of_node) {
|
||||
nr_parts = of_mtd_parse_partitions(&spi->dev,
|
||||
spi->dev.of_node, &parts);
|
||||
|
@ -15,7 +15,7 @@
|
||||
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "phram: " fmt
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -251,6 +251,15 @@ config MTD_NETtel
|
||||
help
|
||||
Support for flash chips on NETtel/SecureEdge/SnapGear boards.
|
||||
|
||||
config MTD_BCM963XX
|
||||
tristate "Map driver for Broadcom BCM963xx boards"
|
||||
depends on BCM63XX
|
||||
select MTD_MAP_BANK_WIDTH_2
|
||||
select MTD_CFI_I1
|
||||
help
|
||||
Support for parsing CFE image tag and creating MTD partitions on
|
||||
Broadcom BCM63xx boards.
|
||||
|
||||
config MTD_DILNETPC
|
||||
tristate "CFI Flash device mapped on DIL/Net PC"
|
||||
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
|
||||
|
@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
|
||||
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
|
||||
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
|
||||
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
|
||||
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o
|
||||
|
271
drivers/mtd/maps/bcm963xx-flash.c
Normal file
271
drivers/mtd/maps/bcm963xx-flash.c
Normal file
@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
|
||||
* Mike Albon <malbon@openwrt.org>
|
||||
* Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
|
||||
|
||||
#define BCM63XX_BUSWIDTH 2 /* Buswidth */
|
||||
#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
|
||||
|
||||
#define PFX KBUILD_MODNAME ": "
|
||||
|
||||
static struct mtd_partition *parsed_parts;
|
||||
|
||||
static struct mtd_info *bcm963xx_mtd_info;
|
||||
|
||||
static struct map_info bcm963xx_map = {
|
||||
.name = "bcm963xx",
|
||||
.bankwidth = BCM63XX_BUSWIDTH,
|
||||
};
|
||||
|
||||
static int parse_cfe_partitions(struct mtd_info *master,
|
||||
struct mtd_partition **pparts)
|
||||
{
|
||||
/* CFE, NVRAM and global Linux are always present */
|
||||
int nrparts = 3, curpart = 0;
|
||||
struct bcm_tag *buf;
|
||||
struct mtd_partition *parts;
|
||||
int ret;
|
||||
size_t retlen;
|
||||
unsigned int rootfsaddr, kerneladdr, spareaddr;
|
||||
unsigned int rootfslen, kernellen, sparelen, totallen;
|
||||
int namelen = 0;
|
||||
int i;
|
||||
char *boardid;
|
||||
char *tagversion;
|
||||
|
||||
/* Allocate memory for buffer */
|
||||
buf = vmalloc(sizeof(struct bcm_tag));
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get the tag */
|
||||
ret = master->read(master, master->erasesize, sizeof(struct bcm_tag),
|
||||
&retlen, (void *)buf);
|
||||
if (retlen != sizeof(struct bcm_tag)) {
|
||||
vfree(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sscanf(buf->kernel_address, "%u", &kerneladdr);
|
||||
sscanf(buf->kernel_length, "%u", &kernellen);
|
||||
sscanf(buf->total_length, "%u", &totallen);
|
||||
tagversion = &(buf->tag_version[0]);
|
||||
boardid = &(buf->board_id[0]);
|
||||
|
||||
printk(KERN_INFO PFX "CFE boot tag found with version %s "
|
||||
"and board type %s\n", tagversion, boardid);
|
||||
|
||||
kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
|
||||
rootfsaddr = kerneladdr + kernellen;
|
||||
spareaddr = roundup(totallen, master->erasesize) + master->erasesize;
|
||||
sparelen = master->size - spareaddr - master->erasesize;
|
||||
rootfslen = spareaddr - rootfsaddr;
|
||||
|
||||
/* Determine number of partitions */
|
||||
namelen = 8;
|
||||
if (rootfslen > 0) {
|
||||
nrparts++;
|
||||
namelen += 6;
|
||||
};
|
||||
if (kernellen > 0) {
|
||||
nrparts++;
|
||||
namelen += 6;
|
||||
};
|
||||
|
||||
/* Ask kernel for more memory */
|
||||
parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
|
||||
if (!parts) {
|
||||
vfree(buf);
|
||||
return -ENOMEM;
|
||||
};
|
||||
|
||||
/* Start building partition list */
|
||||
parts[curpart].name = "CFE";
|
||||
parts[curpart].offset = 0;
|
||||
parts[curpart].size = master->erasesize;
|
||||
curpart++;
|
||||
|
||||
if (kernellen > 0) {
|
||||
parts[curpart].name = "kernel";
|
||||
parts[curpart].offset = kerneladdr;
|
||||
parts[curpart].size = kernellen;
|
||||
curpart++;
|
||||
};
|
||||
|
||||
if (rootfslen > 0) {
|
||||
parts[curpart].name = "rootfs";
|
||||
parts[curpart].offset = rootfsaddr;
|
||||
parts[curpart].size = rootfslen;
|
||||
if (sparelen > 0)
|
||||
parts[curpart].size += sparelen;
|
||||
curpart++;
|
||||
};
|
||||
|
||||
parts[curpart].name = "nvram";
|
||||
parts[curpart].offset = master->size - master->erasesize;
|
||||
parts[curpart].size = master->erasesize;
|
||||
|
||||
/* Global partition "linux" to make easy firmware upgrade */
|
||||
curpart++;
|
||||
parts[curpart].name = "linux";
|
||||
parts[curpart].offset = parts[0].size;
|
||||
parts[curpart].size = master->size - parts[0].size - parts[3].size;
|
||||
|
||||
for (i = 0; i < nrparts; i++)
|
||||
printk(KERN_INFO PFX "Partition %d is %s offset %lx and "
|
||||
"length %lx\n", i, parts[i].name,
|
||||
(long unsigned int)(parts[i].offset),
|
||||
(long unsigned int)(parts[i].size));
|
||||
|
||||
printk(KERN_INFO PFX "Spare partition is %x offset and length %x\n",
|
||||
spareaddr, sparelen);
|
||||
*pparts = parts;
|
||||
vfree(buf);
|
||||
|
||||
return nrparts;
|
||||
};
|
||||
|
||||
static int bcm963xx_detect_cfe(struct mtd_info *master)
|
||||
{
|
||||
int idoffset = 0x4e0;
|
||||
static char idstring[8] = "CFE1CFE1";
|
||||
char buf[9];
|
||||
int ret;
|
||||
size_t retlen;
|
||||
|
||||
ret = master->read(master, idoffset, 8, &retlen, (void *)buf);
|
||||
buf[retlen] = 0;
|
||||
printk(KERN_INFO PFX "Read Signature value of %s\n", buf);
|
||||
|
||||
return strncmp(idstring, buf, 8);
|
||||
}
|
||||
|
||||
static int bcm963xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err = 0;
|
||||
int parsed_nr_parts = 0;
|
||||
char *part_type;
|
||||
struct resource *r;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "no resource supplied\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bcm963xx_map.phys = r->start;
|
||||
bcm963xx_map.size = resource_size(r);
|
||||
bcm963xx_map.virt = ioremap(r->start, resource_size(r));
|
||||
if (!bcm963xx_map.virt) {
|
||||
dev_err(&pdev->dev, "failed to ioremap\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "0x%08lx at 0x%08x\n",
|
||||
bcm963xx_map.size, bcm963xx_map.phys);
|
||||
|
||||
simple_map_init(&bcm963xx_map);
|
||||
|
||||
bcm963xx_mtd_info = do_map_probe("cfi_probe", &bcm963xx_map);
|
||||
if (!bcm963xx_mtd_info) {
|
||||
dev_err(&pdev->dev, "failed to probe using CFI\n");
|
||||
err = -EIO;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
bcm963xx_mtd_info->owner = THIS_MODULE;
|
||||
|
||||
/* This is mutually exclusive */
|
||||
if (bcm963xx_detect_cfe(bcm963xx_mtd_info) == 0) {
|
||||
dev_info(&pdev->dev, "CFE bootloader detected\n");
|
||||
if (parsed_nr_parts == 0) {
|
||||
int ret = parse_cfe_partitions(bcm963xx_mtd_info,
|
||||
&parsed_parts);
|
||||
if (ret > 0) {
|
||||
part_type = "CFE";
|
||||
parsed_nr_parts = ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dev_info(&pdev->dev, "unsupported bootloader\n");
|
||||
err = -ENODEV;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
return add_mtd_partitions(bcm963xx_mtd_info, parsed_parts,
|
||||
parsed_nr_parts);
|
||||
|
||||
err_probe:
|
||||
iounmap(bcm963xx_map.virt);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bcm963xx_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (bcm963xx_mtd_info) {
|
||||
del_mtd_partitions(bcm963xx_mtd_info);
|
||||
map_destroy(bcm963xx_mtd_info);
|
||||
}
|
||||
|
||||
if (bcm963xx_map.virt) {
|
||||
iounmap(bcm963xx_map.virt);
|
||||
bcm963xx_map.virt = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bcm63xx_mtd_dev = {
|
||||
.probe = bcm963xx_probe,
|
||||
.remove = bcm963xx_remove,
|
||||
.driver = {
|
||||
.name = "bcm963xx-flash",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init bcm963xx_mtd_init(void)
|
||||
{
|
||||
return platform_driver_register(&bcm63xx_mtd_dev);
|
||||
}
|
||||
|
||||
static void __exit bcm963xx_mtd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bcm63xx_mtd_dev);
|
||||
}
|
||||
|
||||
module_init(bcm963xx_mtd_init);
|
||||
module_exit(bcm963xx_mtd_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot");
|
||||
MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
|
@ -208,10 +208,14 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* We cast start/end to known types in the boards file, so cast
|
||||
* away their pointer types here to the known types (gpios->xxx).
|
||||
*/
|
||||
state->gpio_count = gpios->end;
|
||||
state->gpio_addrs = (void *)gpios->start;
|
||||
state->gpio_addrs = (void *)(unsigned long)gpios->start;
|
||||
state->gpio_values = (void *)(state + 1);
|
||||
state->win_size = memory->end - memory->start + 1;
|
||||
state->win_size = resource_size(memory);
|
||||
memset(state->gpio_values, 0xff, arr_size);
|
||||
|
||||
state->map.name = DRIVER_NAME;
|
||||
@ -221,7 +225,7 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
|
||||
state->map.copy_to = gf_copy_to;
|
||||
state->map.bankwidth = pdata->width;
|
||||
state->map.size = state->win_size * (1 << state->gpio_count);
|
||||
state->map.virt = (void __iomem *)memory->start;
|
||||
state->map.virt = ioremap_nocache(memory->start, state->map.size);
|
||||
state->map.phys = NO_XIP;
|
||||
state->map.map_priv_1 = (unsigned long)state;
|
||||
|
||||
|
@ -640,10 +640,6 @@ static int pcmciamtd_config(struct pcmcia_device *link)
|
||||
}
|
||||
dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name);
|
||||
return 0;
|
||||
|
||||
dev_err(&dev->p_dev->dev, "CS Error, exiting\n");
|
||||
pcmciamtd_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ static int parse_obsolete_partitions(struct platform_device *dev,
|
||||
{
|
||||
int i, plen, nr_parts;
|
||||
const struct {
|
||||
u32 offset, len;
|
||||
__be32 offset, len;
|
||||
} *part;
|
||||
const char *names;
|
||||
|
||||
@ -69,9 +69,9 @@ static int parse_obsolete_partitions(struct platform_device *dev,
|
||||
names = of_get_property(dp, "partition-names", &plen);
|
||||
|
||||
for (i = 0; i < nr_parts; i++) {
|
||||
info->parts[i].offset = part->offset;
|
||||
info->parts[i].size = part->len & ~1;
|
||||
if (part->len & 1) /* bit 0 set signifies read only partition */
|
||||
info->parts[i].offset = be32_to_cpu(part->offset);
|
||||
info->parts[i].size = be32_to_cpu(part->len) & ~1;
|
||||
if (be32_to_cpu(part->len) & 1) /* bit 0 set signifies read only partition */
|
||||
info->parts[i].mask_flags = MTD_WRITEABLE;
|
||||
|
||||
if (names && (plen > 0)) {
|
||||
@ -226,11 +226,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
|
||||
struct resource res;
|
||||
struct of_flash *info;
|
||||
const char *probe_type = match->data;
|
||||
const u32 *width;
|
||||
const __be32 *width;
|
||||
int err;
|
||||
int i;
|
||||
int count;
|
||||
const u32 *p;
|
||||
const __be32 *p;
|
||||
int reg_tuple_size;
|
||||
struct mtd_info **mtd_list = NULL;
|
||||
resource_size_t res_size;
|
||||
@ -267,9 +267,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
|
||||
for (i = 0; i < count; i++) {
|
||||
err = -ENXIO;
|
||||
if (of_address_to_resource(dp, i, &res)) {
|
||||
dev_err(&dev->dev, "Can't get IO address from device"
|
||||
" tree\n");
|
||||
goto err_out;
|
||||
/*
|
||||
* Continue with next register tuple if this
|
||||
* one is not mappable
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
|
||||
|
@ -37,7 +37,6 @@
|
||||
|
||||
#include "mtdcore.h"
|
||||
|
||||
static DEFINE_MUTEX(mtd_blkdevs_mutex);
|
||||
static LIST_HEAD(blktrans_majors);
|
||||
static DEFINE_MUTEX(blktrans_ref_mutex);
|
||||
|
||||
@ -133,6 +132,10 @@ static int mtd_blktrans_thread(void *arg)
|
||||
|
||||
if (!req && !(req = blk_fetch_request(rq))) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
if (kthread_should_stop())
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
spin_unlock_irq(rq->queue_lock);
|
||||
schedule();
|
||||
spin_lock_irq(rq->queue_lock);
|
||||
@ -176,54 +179,53 @@ static void mtd_blktrans_request(struct request_queue *rq)
|
||||
static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||
{
|
||||
struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev)
|
||||
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
|
||||
|
||||
mutex_lock(&mtd_blkdevs_mutex);
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
if (!dev->mtd) {
|
||||
ret = -ENXIO;
|
||||
if (dev->open++)
|
||||
goto unlock;
|
||||
|
||||
kref_get(&dev->ref);
|
||||
__module_get(dev->tr->owner);
|
||||
|
||||
if (dev->mtd) {
|
||||
ret = dev->tr->open ? dev->tr->open(dev) : 0;
|
||||
__get_mtd_device(dev->mtd);
|
||||
}
|
||||
|
||||
ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
|
||||
|
||||
/* Take another reference on the device so it won't go away till
|
||||
last release */
|
||||
if (!ret)
|
||||
kref_get(&dev->ref);
|
||||
unlock:
|
||||
mutex_unlock(&dev->lock);
|
||||
blktrans_dev_put(dev);
|
||||
mutex_unlock(&mtd_blkdevs_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int blktrans_release(struct gendisk *disk, fmode_t mode)
|
||||
{
|
||||
struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
|
||||
int ret = -ENXIO;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&mtd_blkdevs_mutex);
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
/* Release one reference, we sure its not the last one here*/
|
||||
kref_put(&dev->ref, blktrans_dev_release);
|
||||
|
||||
if (!dev->mtd)
|
||||
if (--dev->open)
|
||||
goto unlock;
|
||||
|
||||
ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
|
||||
kref_put(&dev->ref, blktrans_dev_release);
|
||||
module_put(dev->tr->owner);
|
||||
|
||||
if (dev->mtd) {
|
||||
ret = dev->tr->release ? dev->tr->release(dev) : 0;
|
||||
__put_mtd_device(dev->mtd);
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&dev->lock);
|
||||
blktrans_dev_put(dev);
|
||||
mutex_unlock(&mtd_blkdevs_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -256,7 +258,6 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
if (!dev)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&mtd_blkdevs_mutex);
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
if (!dev->mtd)
|
||||
@ -271,7 +272,6 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&dev->lock);
|
||||
mutex_unlock(&mtd_blkdevs_mutex);
|
||||
blktrans_dev_put(dev);
|
||||
return ret;
|
||||
}
|
||||
@ -385,9 +385,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
||||
|
||||
gd->queue = new->rq;
|
||||
|
||||
__get_mtd_device(new->mtd);
|
||||
__module_get(tr->owner);
|
||||
|
||||
/* Create processing thread */
|
||||
/* TODO: workqueue ? */
|
||||
new->thread = kthread_run(mtd_blktrans_thread, new,
|
||||
@ -410,8 +407,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
||||
}
|
||||
return 0;
|
||||
error4:
|
||||
module_put(tr->owner);
|
||||
__put_mtd_device(new->mtd);
|
||||
blk_cleanup_queue(new->rq);
|
||||
error3:
|
||||
put_disk(new->disk);
|
||||
@ -448,17 +443,15 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
|
||||
blk_start_queue(old->rq);
|
||||
spin_unlock_irqrestore(&old->queue_lock, flags);
|
||||
|
||||
/* Ask trans driver for release to the mtd device */
|
||||
/* If the device is currently open, tell trans driver to close it,
|
||||
then put mtd device, and don't touch it again */
|
||||
mutex_lock(&old->lock);
|
||||
if (old->open && old->tr->release) {
|
||||
old->tr->release(old);
|
||||
old->open = 0;
|
||||
if (old->open) {
|
||||
if (old->tr->release)
|
||||
old->tr->release(old);
|
||||
__put_mtd_device(old->mtd);
|
||||
}
|
||||
|
||||
__put_mtd_device(old->mtd);
|
||||
module_put(old->tr->owner);
|
||||
|
||||
/* At that point, we don't touch the mtd anymore */
|
||||
old->mtd = NULL;
|
||||
|
||||
mutex_unlock(&old->lock);
|
||||
@ -508,13 +501,16 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
|
||||
mutex_lock(&mtd_table_mutex);
|
||||
|
||||
ret = register_blkdev(tr->major, tr->name);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
|
||||
tr->name, tr->major, ret);
|
||||
mutex_unlock(&mtd_table_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
tr->major = ret;
|
||||
|
||||
tr->blkshift = ffs(tr->blksize) - 1;
|
||||
|
||||
INIT_LIST_HEAD(&tr->devs);
|
||||
|
@ -30,8 +30,9 @@
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/mount.h>
|
||||
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/map.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
@ -478,6 +479,78 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies (and truncates, if necessary) data from the larger struct,
|
||||
* nand_ecclayout, to the smaller, deprecated layout struct,
|
||||
* nand_ecclayout_user. This is necessary only to suppport the deprecated
|
||||
* API ioctl ECCGETLAYOUT while allowing all new functionality to use
|
||||
* nand_ecclayout flexibly (i.e. the struct may change size in new
|
||||
* releases without requiring major rewrites).
|
||||
*/
|
||||
static int shrink_ecclayout(const struct nand_ecclayout *from,
|
||||
struct nand_ecclayout_user *to)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!from || !to)
|
||||
return -EINVAL;
|
||||
|
||||
memset(to, 0, sizeof(*to));
|
||||
|
||||
to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
|
||||
for (i = 0; i < to->eccbytes; i++)
|
||||
to->eccpos[i] = from->eccpos[i];
|
||||
|
||||
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
|
||||
if (from->oobfree[i].length == 0 &&
|
||||
from->oobfree[i].offset == 0)
|
||||
break;
|
||||
to->oobavail += from->oobfree[i].length;
|
||||
to->oobfree[i] = from->oobfree[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static int mtd_blkpg_ioctl(struct mtd_info *mtd,
|
||||
struct blkpg_ioctl_arg __user *arg)
|
||||
{
|
||||
struct blkpg_ioctl_arg a;
|
||||
struct blkpg_partition p;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* Only master mtd device must be used to control partitions */
|
||||
if (!mtd_is_master(mtd))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (a.op) {
|
||||
case BLKPG_ADD_PARTITION:
|
||||
|
||||
return mtd_add_partition(mtd, p.devname, p.start, p.length);
|
||||
|
||||
case BLKPG_DEL_PARTITION:
|
||||
|
||||
if (p.pno < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return mtd_del_partition(mtd, p.pno);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||
{
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
@ -514,6 +587,9 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||
if (get_user(ur_idx, &(ur->regionindex)))
|
||||
return -EFAULT;
|
||||
|
||||
if (ur_idx >= mtd->numeraseregions)
|
||||
return -EINVAL;
|
||||
|
||||
kr = &(mtd->eraseregions[ur_idx]);
|
||||
|
||||
if (put_user(kr->offset, &(ur->offset))
|
||||
@ -813,14 +889,23 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This ioctl is being deprecated - it truncates the ecc layout */
|
||||
case ECCGETLAYOUT:
|
||||
{
|
||||
struct nand_ecclayout_user *usrlay;
|
||||
|
||||
if (!mtd->ecclayout)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (copy_to_user(argp, mtd->ecclayout,
|
||||
sizeof(struct nand_ecclayout)))
|
||||
return -EFAULT;
|
||||
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
|
||||
if (!usrlay)
|
||||
return -ENOMEM;
|
||||
|
||||
shrink_ecclayout(mtd->ecclayout, usrlay);
|
||||
|
||||
if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
|
||||
ret = -EFAULT;
|
||||
kfree(usrlay);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -856,6 +941,22 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
case BLKPG:
|
||||
{
|
||||
ret = mtd_blkpg_ioctl(mtd,
|
||||
(struct blkpg_ioctl_arg __user *)arg);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLKRRPART:
|
||||
{
|
||||
/* No reread partition feature. Just return ok */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
@ -1033,7 +1134,7 @@ static const struct file_operations mtd_fops = {
|
||||
static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC);
|
||||
return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC);
|
||||
}
|
||||
|
||||
static struct file_system_type mtd_inodefs_type = {
|
||||
|
@ -29,9 +29,11 @@
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/* Our partition linked list */
|
||||
static LIST_HEAD(mtd_partitions);
|
||||
static DEFINE_MUTEX(mtd_partitions_mutex);
|
||||
|
||||
/* Our partition node structure */
|
||||
struct mtd_part {
|
||||
@ -326,6 +328,12 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void free_partition(struct mtd_part *p)
|
||||
{
|
||||
kfree(p->mtd.name);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function unregisters and destroy all slave MTD objects which are
|
||||
* attached to the given master MTD object.
|
||||
@ -334,33 +342,42 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
int del_mtd_partitions(struct mtd_info *master)
|
||||
{
|
||||
struct mtd_part *slave, *next;
|
||||
int ret, err = 0;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
|
||||
if (slave->master == master) {
|
||||
ret = del_mtd_device(&slave->mtd);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
continue;
|
||||
}
|
||||
list_del(&slave->list);
|
||||
del_mtd_device(&slave->mtd);
|
||||
kfree(slave);
|
||||
free_partition(slave);
|
||||
}
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(del_mtd_partitions);
|
||||
|
||||
static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
const struct mtd_partition *part, int partno,
|
||||
uint64_t cur_offset)
|
||||
static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||
const struct mtd_partition *part, int partno,
|
||||
uint64_t cur_offset)
|
||||
{
|
||||
struct mtd_part *slave;
|
||||
char *name;
|
||||
|
||||
/* allocate the partition structure */
|
||||
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
|
||||
if (!slave) {
|
||||
name = kstrdup(part->name, GFP_KERNEL);
|
||||
if (!name || !slave) {
|
||||
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
|
||||
master->name);
|
||||
del_mtd_partitions(master);
|
||||
return NULL;
|
||||
master->name);
|
||||
kfree(name);
|
||||
kfree(slave);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
list_add(&slave->list, &mtd_partitions);
|
||||
|
||||
/* set up the MTD object for this partition */
|
||||
slave->mtd.type = master->type;
|
||||
@ -371,7 +388,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
slave->mtd.oobavail = master->oobavail;
|
||||
slave->mtd.subpage_sft = master->subpage_sft;
|
||||
|
||||
slave->mtd.name = part->name;
|
||||
slave->mtd.name = name;
|
||||
slave->mtd.owner = master->owner;
|
||||
slave->mtd.backing_dev_info = master->backing_dev_info;
|
||||
|
||||
@ -518,12 +535,89 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
||||
}
|
||||
|
||||
out_register:
|
||||
/* register our partition */
|
||||
add_mtd_device(&slave->mtd);
|
||||
|
||||
return slave;
|
||||
}
|
||||
|
||||
int mtd_add_partition(struct mtd_info *master, char *name,
|
||||
long long offset, long long length)
|
||||
{
|
||||
struct mtd_partition part;
|
||||
struct mtd_part *p, *new;
|
||||
uint64_t start, end;
|
||||
int ret = 0;
|
||||
|
||||
/* the direct offset is expected */
|
||||
if (offset == MTDPART_OFS_APPEND ||
|
||||
offset == MTDPART_OFS_NXTBLK)
|
||||
return -EINVAL;
|
||||
|
||||
if (length == MTDPART_SIZ_FULL)
|
||||
length = master->size - offset;
|
||||
|
||||
if (length <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
part.name = name;
|
||||
part.size = length;
|
||||
part.offset = offset;
|
||||
part.mask_flags = 0;
|
||||
part.ecclayout = NULL;
|
||||
|
||||
new = allocate_partition(master, &part, -1, offset);
|
||||
if (IS_ERR(new))
|
||||
return PTR_ERR(new);
|
||||
|
||||
start = offset;
|
||||
end = offset + length;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry(p, &mtd_partitions, list)
|
||||
if (p->master == master) {
|
||||
if ((start >= p->offset) &&
|
||||
(start < (p->offset + p->mtd.size)))
|
||||
goto err_inv;
|
||||
|
||||
if ((end >= p->offset) &&
|
||||
(end < (p->offset + p->mtd.size)))
|
||||
goto err_inv;
|
||||
}
|
||||
|
||||
list_add(&new->list, &mtd_partitions);
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
add_mtd_device(&new->mtd);
|
||||
|
||||
return ret;
|
||||
err_inv:
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
free_partition(new);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_add_partition);
|
||||
|
||||
int mtd_del_partition(struct mtd_info *master, int partno)
|
||||
{
|
||||
struct mtd_part *slave, *next;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
|
||||
if ((slave->master == master) &&
|
||||
(slave->mtd.index == partno)) {
|
||||
ret = del_mtd_device(&slave->mtd);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
list_del(&slave->list);
|
||||
free_partition(slave);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_del_partition);
|
||||
|
||||
/*
|
||||
* This function, given a master MTD object and a partition table, creates
|
||||
* and registers slave MTD objects which are bound to the master according to
|
||||
@ -544,9 +638,16 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
|
||||
|
||||
for (i = 0; i < nbparts; i++) {
|
||||
slave = add_one_partition(master, parts + i, i, cur_offset);
|
||||
if (!slave)
|
||||
return -ENOMEM;
|
||||
slave = allocate_partition(master, parts + i, i, cur_offset);
|
||||
if (IS_ERR(slave))
|
||||
return PTR_ERR(slave);
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_add(&slave->list, &mtd_partitions);
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
add_mtd_device(&slave->mtd);
|
||||
|
||||
cur_offset = slave->offset + slave->mtd.size;
|
||||
}
|
||||
|
||||
@ -618,3 +719,20 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(parse_mtd_partitions);
|
||||
|
||||
int mtd_is_master(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part;
|
||||
int nopart = 0;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry(part, &mtd_partitions, list)
|
||||
if (&part->mtd == mtd) {
|
||||
nopart = 1;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
return nopart;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_is_master);
|
||||
|
@ -400,13 +400,6 @@ config MTD_NAND_PXA3xx
|
||||
This enables the driver for the NAND flash device found on
|
||||
PXA3xx processors
|
||||
|
||||
config MTD_NAND_PXA3xx_BUILTIN
|
||||
bool "Use builtin definitions for some NAND chips (deprecated)"
|
||||
depends on MTD_NAND_PXA3xx
|
||||
help
|
||||
This enables builtin definitions for some NAND chips. This
|
||||
is deprecated in favor of platform specific data.
|
||||
|
||||
config MTD_NAND_CM_X270
|
||||
tristate "Support for NAND Flash on CM-X270 modules"
|
||||
depends on MACH_ARMCORE
|
||||
@ -458,6 +451,7 @@ config MTD_NAND_ORION
|
||||
config MTD_NAND_FSL_ELBC
|
||||
tristate "NAND support for Freescale eLBC controllers"
|
||||
depends on PPC_OF
|
||||
select FSL_LBC
|
||||
help
|
||||
Various Freescale chips, including the 8313, include a NAND Flash
|
||||
Controller Module with built-in hardware ECC capabilities.
|
||||
@ -531,4 +525,11 @@ config MTD_NAND_JZ4740
|
||||
help
|
||||
Enables support for NAND Flash on JZ4740 SoC based boards.
|
||||
|
||||
config MTD_NAND_FSMC
|
||||
tristate "Support for NAND on ST Micros FSMC"
|
||||
depends on PLAT_SPEAR || PLAT_NOMADIK || MACH_U300
|
||||
help
|
||||
Enables support for NAND Flash chips on the ST Microelectronics
|
||||
Flexible Static Memory Controller (FSMC)
|
||||
|
||||
endif # MTD_NAND
|
||||
|
@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
|
||||
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
||||
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
||||
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
|
||||
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
|
||||
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
|
||||
|
@ -110,15 +110,6 @@ static const unsigned short bfin_nfc_pin_req[] =
|
||||
0};
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
|
||||
static uint8_t bbt_pattern[] = { 0xff };
|
||||
|
||||
static struct nand_bbt_descr bootrom_bbt = {
|
||||
.options = 0,
|
||||
.offs = 63,
|
||||
.len = 1,
|
||||
.pattern = bbt_pattern,
|
||||
};
|
||||
|
||||
static struct nand_ecclayout bootrom_ecclayout = {
|
||||
.eccbytes = 24,
|
||||
.eccpos = {
|
||||
@ -809,7 +800,6 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
|
||||
/* setup hardware ECC data struct */
|
||||
if (hardware_ecc) {
|
||||
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
|
||||
chip->badblock_pattern = &bootrom_bbt;
|
||||
chip->ecc.layout = &bootrom_ecclayout;
|
||||
#endif
|
||||
chip->read_buf = bf5xx_nand_dma_read_buf;
|
||||
@ -830,6 +820,10 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
|
||||
goto out_err_nand_scan;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
|
||||
chip->badblockpos = 63;
|
||||
#endif
|
||||
|
||||
/* add NAND partition */
|
||||
bf5xx_nand_add_partition(info);
|
||||
|
||||
|
@ -316,7 +316,7 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd,
|
||||
u32 syndrome[4];
|
||||
u32 ecc_state;
|
||||
unsigned num_errors, corrected;
|
||||
unsigned long timeo = jiffies + msecs_to_jiffies(100);
|
||||
unsigned long timeo;
|
||||
|
||||
/* All bytes 0xff? It's an erased page; ignore its ECC. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
@ -372,9 +372,11 @@ compare:
|
||||
* after setting the 4BITECC_ADD_CALC_START bit. So if you immediately
|
||||
* begin trying to poll for the state, you may fall right out of your
|
||||
* loop without any of the correction calculations having taken place.
|
||||
* The recommendation from the hardware team is to wait till ECC_STATE
|
||||
* reads less than 4, which means ECC HW has entered correction state.
|
||||
* The recommendation from the hardware team is to initially delay as
|
||||
* long as ECC_STATE reads less than 4. After that, ECC HW has entered
|
||||
* correction state.
|
||||
*/
|
||||
timeo = jiffies + usecs_to_jiffies(100);
|
||||
do {
|
||||
ecc_state = (davinci_nand_readl(info,
|
||||
NANDFSR_OFFSET) >> 8) & 0x0f;
|
||||
@ -733,6 +735,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
* breaks userspace ioctl interface with mtd-utils. Once we
|
||||
* resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used
|
||||
* for the 4KiB page chips.
|
||||
*
|
||||
* TODO: Note that nand_ecclayout has now been expanded and can
|
||||
* hold plenty of OOB entries.
|
||||
*/
|
||||
dev_warn(&pdev->dev, "no 4-bit ECC support yet "
|
||||
"for 4KiB-page NAND\n");
|
||||
|
@ -1292,6 +1292,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
||||
read_status(denali);
|
||||
break;
|
||||
case NAND_CMD_READID:
|
||||
case NAND_CMD_PARAM:
|
||||
reset_buf(denali);
|
||||
/*sometimes ManufactureId read from register is not right
|
||||
* e.g. some of Micron MT29F32G08QAA MLC NAND chips
|
||||
|
@ -1,9 +1,11 @@
|
||||
/* Freescale Enhanced Local Bus Controller NAND driver
|
||||
*
|
||||
* Copyright (c) 2006-2007 Freescale Semiconductor
|
||||
* Copyright © 2006-2007, 2010 Freescale Semiconductor
|
||||
*
|
||||
* Authors: Nick Spence <nick.spence@freescale.com>,
|
||||
* Scott Wood <scottwood@freescale.com>
|
||||
* Jack Lan <jack.lan@freescale.com>
|
||||
* Roy Zang <tie-fei.zang@freescale.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -27,6 +29,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
@ -42,14 +45,12 @@
|
||||
#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
|
||||
#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
|
||||
|
||||
struct fsl_elbc_ctrl;
|
||||
|
||||
/* mtd information per set */
|
||||
|
||||
struct fsl_elbc_mtd {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip chip;
|
||||
struct fsl_elbc_ctrl *ctrl;
|
||||
struct fsl_lbc_ctrl *ctrl;
|
||||
|
||||
struct device *dev;
|
||||
int bank; /* Chip select bank number */
|
||||
@ -58,18 +59,12 @@ struct fsl_elbc_mtd {
|
||||
unsigned int fmr; /* FCM Flash Mode Register value */
|
||||
};
|
||||
|
||||
/* overview of the fsl elbc controller */
|
||||
/* Freescale eLBC FCM controller infomation */
|
||||
|
||||
struct fsl_elbc_ctrl {
|
||||
struct fsl_elbc_fcm_ctrl {
|
||||
struct nand_hw_control controller;
|
||||
struct fsl_elbc_mtd *chips[MAX_BANKS];
|
||||
|
||||
/* device info */
|
||||
struct device *dev;
|
||||
struct fsl_lbc_regs __iomem *regs;
|
||||
int irq;
|
||||
wait_queue_head_t irq_wait;
|
||||
unsigned int irq_status; /* status read from LTESR by irq handler */
|
||||
u8 __iomem *addr; /* Address of assigned FCM buffer */
|
||||
unsigned int page; /* Last page written to / read from */
|
||||
unsigned int read_bytes; /* Number of bytes read during command */
|
||||
@ -79,6 +74,7 @@ struct fsl_elbc_ctrl {
|
||||
unsigned int mdr; /* UPM/FCM Data Register value */
|
||||
unsigned int use_mdr; /* Non zero if the MDR is to be set */
|
||||
unsigned int oob; /* Non zero if operating on OOB data */
|
||||
unsigned int counter; /* counter for the initializations */
|
||||
char *oob_poi; /* Place to write ECC after read back */
|
||||
};
|
||||
|
||||
@ -164,11 +160,12 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
|
||||
int buf_num;
|
||||
|
||||
ctrl->page = page_addr;
|
||||
elbc_fcm_ctrl->page = page_addr;
|
||||
|
||||
out_be32(&lbc->fbar,
|
||||
page_addr >> (chip->phys_erase_shift - chip->page_shift));
|
||||
@ -185,16 +182,18 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
|
||||
buf_num = page_addr & 7;
|
||||
}
|
||||
|
||||
ctrl->addr = priv->vbase + buf_num * 1024;
|
||||
ctrl->index = column;
|
||||
elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
|
||||
elbc_fcm_ctrl->index = column;
|
||||
|
||||
/* for OOB data point to the second half of the buffer */
|
||||
if (oob)
|
||||
ctrl->index += priv->page_size ? 2048 : 512;
|
||||
elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
|
||||
|
||||
dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
|
||||
dev_vdbg(priv->dev, "set_addr: bank=%d, "
|
||||
"elbc_fcm_ctrl->addr=0x%p (0x%p), "
|
||||
"index %x, pes %d ps %d\n",
|
||||
buf_num, ctrl->addr, priv->vbase, ctrl->index,
|
||||
buf_num, elbc_fcm_ctrl->addr, priv->vbase,
|
||||
elbc_fcm_ctrl->index,
|
||||
chip->phys_erase_shift, chip->page_shift);
|
||||
}
|
||||
|
||||
@ -205,18 +204,19 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
|
||||
/* Setup the FMR[OP] to execute without write protection */
|
||||
out_be32(&lbc->fmr, priv->fmr | 3);
|
||||
if (ctrl->use_mdr)
|
||||
out_be32(&lbc->mdr, ctrl->mdr);
|
||||
if (elbc_fcm_ctrl->use_mdr)
|
||||
out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
|
||||
|
||||
dev_vdbg(ctrl->dev,
|
||||
dev_vdbg(priv->dev,
|
||||
"fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
|
||||
in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
|
||||
dev_vdbg(ctrl->dev,
|
||||
dev_vdbg(priv->dev,
|
||||
"fsl_elbc_run_command: fbar=%08x fpar=%08x "
|
||||
"fbcr=%08x bank=%d\n",
|
||||
in_be32(&lbc->fbar), in_be32(&lbc->fpar),
|
||||
@ -229,19 +229,18 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
|
||||
/* wait for FCM complete flag or timeout */
|
||||
wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
|
||||
FCM_TIMEOUT_MSECS * HZ/1000);
|
||||
ctrl->status = ctrl->irq_status;
|
||||
|
||||
elbc_fcm_ctrl->status = ctrl->irq_status;
|
||||
/* store mdr value in case it was needed */
|
||||
if (ctrl->use_mdr)
|
||||
ctrl->mdr = in_be32(&lbc->mdr);
|
||||
if (elbc_fcm_ctrl->use_mdr)
|
||||
elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
|
||||
|
||||
ctrl->use_mdr = 0;
|
||||
elbc_fcm_ctrl->use_mdr = 0;
|
||||
|
||||
if (ctrl->status != LTESR_CC) {
|
||||
dev_info(ctrl->dev,
|
||||
if (elbc_fcm_ctrl->status != LTESR_CC) {
|
||||
dev_info(priv->dev,
|
||||
"command failed: fir %x fcr %x status %x mdr %x\n",
|
||||
in_be32(&lbc->fir), in_be32(&lbc->fcr),
|
||||
ctrl->status, ctrl->mdr);
|
||||
elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -251,7 +250,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
|
||||
static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
|
||||
{
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
|
||||
if (priv->page_size) {
|
||||
@ -284,15 +283,16 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
|
||||
ctrl->use_mdr = 0;
|
||||
elbc_fcm_ctrl->use_mdr = 0;
|
||||
|
||||
/* clear the read buffer */
|
||||
ctrl->read_bytes = 0;
|
||||
elbc_fcm_ctrl->read_bytes = 0;
|
||||
if (command != NAND_CMD_PAGEPROG)
|
||||
ctrl->index = 0;
|
||||
elbc_fcm_ctrl->index = 0;
|
||||
|
||||
switch (command) {
|
||||
/* READ0 and READ1 read the entire buffer to use hardware ECC. */
|
||||
@ -301,7 +301,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
|
||||
/* fall-through */
|
||||
case NAND_CMD_READ0:
|
||||
dev_dbg(ctrl->dev,
|
||||
dev_dbg(priv->dev,
|
||||
"fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
|
||||
" 0x%x, column: 0x%x.\n", page_addr, column);
|
||||
|
||||
@ -309,8 +309,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
|
||||
set_addr(mtd, 0, page_addr, 0);
|
||||
|
||||
ctrl->read_bytes = mtd->writesize + mtd->oobsize;
|
||||
ctrl->index += column;
|
||||
elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
|
||||
elbc_fcm_ctrl->index += column;
|
||||
|
||||
fsl_elbc_do_read(chip, 0);
|
||||
fsl_elbc_run_command(mtd);
|
||||
@ -318,14 +318,14 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
|
||||
/* READOOB reads only the OOB because no ECC is performed. */
|
||||
case NAND_CMD_READOOB:
|
||||
dev_vdbg(ctrl->dev,
|
||||
dev_vdbg(priv->dev,
|
||||
"fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
|
||||
" 0x%x, column: 0x%x.\n", page_addr, column);
|
||||
|
||||
out_be32(&lbc->fbcr, mtd->oobsize - column);
|
||||
set_addr(mtd, column, page_addr, 1);
|
||||
|
||||
ctrl->read_bytes = mtd->writesize + mtd->oobsize;
|
||||
elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
|
||||
|
||||
fsl_elbc_do_read(chip, 1);
|
||||
fsl_elbc_run_command(mtd);
|
||||
@ -333,7 +333,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
|
||||
/* READID must read all 5 possible bytes while CEB is active */
|
||||
case NAND_CMD_READID:
|
||||
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
|
||||
dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
|
||||
|
||||
out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_UA << FIR_OP1_SHIFT) |
|
||||
@ -341,9 +341,9 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
|
||||
/* 5 bytes for manuf, device and exts */
|
||||
out_be32(&lbc->fbcr, 5);
|
||||
ctrl->read_bytes = 5;
|
||||
ctrl->use_mdr = 1;
|
||||
ctrl->mdr = 0;
|
||||
elbc_fcm_ctrl->read_bytes = 5;
|
||||
elbc_fcm_ctrl->use_mdr = 1;
|
||||
elbc_fcm_ctrl->mdr = 0;
|
||||
|
||||
set_addr(mtd, 0, 0, 0);
|
||||
fsl_elbc_run_command(mtd);
|
||||
@ -351,7 +351,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
|
||||
/* ERASE1 stores the block and page address */
|
||||
case NAND_CMD_ERASE1:
|
||||
dev_vdbg(ctrl->dev,
|
||||
dev_vdbg(priv->dev,
|
||||
"fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
|
||||
"page_addr: 0x%x.\n", page_addr);
|
||||
set_addr(mtd, 0, page_addr, 0);
|
||||
@ -359,7 +359,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
|
||||
/* ERASE2 uses the block and page address from ERASE1 */
|
||||
case NAND_CMD_ERASE2:
|
||||
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
|
||||
dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
|
||||
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||
@ -374,8 +374,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
(NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
|
||||
|
||||
out_be32(&lbc->fbcr, 0);
|
||||
ctrl->read_bytes = 0;
|
||||
ctrl->use_mdr = 1;
|
||||
elbc_fcm_ctrl->read_bytes = 0;
|
||||
elbc_fcm_ctrl->use_mdr = 1;
|
||||
|
||||
fsl_elbc_run_command(mtd);
|
||||
return;
|
||||
@ -383,14 +383,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
/* SEQIN sets up the addr buffer and all registers except the length */
|
||||
case NAND_CMD_SEQIN: {
|
||||
__be32 fcr;
|
||||
dev_vdbg(ctrl->dev,
|
||||
"fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
|
||||
dev_vdbg(priv->dev,
|
||||
"fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
|
||||
"page_addr: 0x%x, column: 0x%x.\n",
|
||||
page_addr, column);
|
||||
|
||||
ctrl->column = column;
|
||||
ctrl->oob = 0;
|
||||
ctrl->use_mdr = 1;
|
||||
elbc_fcm_ctrl->use_mdr = 1;
|
||||
|
||||
fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
|
||||
(NAND_CMD_SEQIN << FCR_CMD2_SHIFT) |
|
||||
@ -420,7 +418,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
/* OOB area --> READOOB */
|
||||
column -= mtd->writesize;
|
||||
fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
|
||||
ctrl->oob = 1;
|
||||
elbc_fcm_ctrl->oob = 1;
|
||||
} else {
|
||||
WARN_ON(column != 0);
|
||||
/* First 256 bytes --> READ0 */
|
||||
@ -429,24 +427,24 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
}
|
||||
|
||||
out_be32(&lbc->fcr, fcr);
|
||||
set_addr(mtd, column, page_addr, ctrl->oob);
|
||||
set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
|
||||
return;
|
||||
}
|
||||
|
||||
/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
|
||||
case NAND_CMD_PAGEPROG: {
|
||||
int full_page;
|
||||
dev_vdbg(ctrl->dev,
|
||||
dev_vdbg(priv->dev,
|
||||
"fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
|
||||
"writing %d bytes.\n", ctrl->index);
|
||||
"writing %d bytes.\n", elbc_fcm_ctrl->index);
|
||||
|
||||
/* if the write did not start at 0 or is not a full page
|
||||
* then set the exact length, otherwise use a full page
|
||||
* write so the HW generates the ECC.
|
||||
*/
|
||||
if (ctrl->oob || ctrl->column != 0 ||
|
||||
ctrl->index != mtd->writesize + mtd->oobsize) {
|
||||
out_be32(&lbc->fbcr, ctrl->index);
|
||||
if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
|
||||
elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) {
|
||||
out_be32(&lbc->fbcr, elbc_fcm_ctrl->index);
|
||||
full_page = 0;
|
||||
} else {
|
||||
out_be32(&lbc->fbcr, 0);
|
||||
@ -458,21 +456,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
/* Read back the page in order to fill in the ECC for the
|
||||
* caller. Is this really needed?
|
||||
*/
|
||||
if (full_page && ctrl->oob_poi) {
|
||||
if (full_page && elbc_fcm_ctrl->oob_poi) {
|
||||
out_be32(&lbc->fbcr, 3);
|
||||
set_addr(mtd, 6, page_addr, 1);
|
||||
|
||||
ctrl->read_bytes = mtd->writesize + 9;
|
||||
elbc_fcm_ctrl->read_bytes = mtd->writesize + 9;
|
||||
|
||||
fsl_elbc_do_read(chip, 1);
|
||||
fsl_elbc_run_command(mtd);
|
||||
|
||||
memcpy_fromio(ctrl->oob_poi + 6,
|
||||
&ctrl->addr[ctrl->index], 3);
|
||||
ctrl->index += 3;
|
||||
memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6,
|
||||
&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3);
|
||||
elbc_fcm_ctrl->index += 3;
|
||||
}
|
||||
|
||||
ctrl->oob_poi = NULL;
|
||||
elbc_fcm_ctrl->oob_poi = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -485,26 +483,26 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
|
||||
out_be32(&lbc->fbcr, 1);
|
||||
set_addr(mtd, 0, 0, 0);
|
||||
ctrl->read_bytes = 1;
|
||||
elbc_fcm_ctrl->read_bytes = 1;
|
||||
|
||||
fsl_elbc_run_command(mtd);
|
||||
|
||||
/* The chip always seems to report that it is
|
||||
* write-protected, even when it is not.
|
||||
*/
|
||||
setbits8(ctrl->addr, NAND_STATUS_WP);
|
||||
setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
|
||||
return;
|
||||
|
||||
/* RESET without waiting for the ready line */
|
||||
case NAND_CMD_RESET:
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
|
||||
dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
|
||||
out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
|
||||
out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
|
||||
fsl_elbc_run_command(mtd);
|
||||
return;
|
||||
|
||||
default:
|
||||
dev_err(ctrl->dev,
|
||||
dev_err(priv->dev,
|
||||
"fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
|
||||
command);
|
||||
}
|
||||
@ -524,24 +522,24 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
unsigned int bufsize = mtd->writesize + mtd->oobsize;
|
||||
|
||||
if (len <= 0) {
|
||||
dev_err(ctrl->dev, "write_buf of %d bytes", len);
|
||||
ctrl->status = 0;
|
||||
dev_err(priv->dev, "write_buf of %d bytes", len);
|
||||
elbc_fcm_ctrl->status = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((unsigned int)len > bufsize - ctrl->index) {
|
||||
dev_err(ctrl->dev,
|
||||
if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
|
||||
dev_err(priv->dev,
|
||||
"write_buf beyond end of buffer "
|
||||
"(%d requested, %u available)\n",
|
||||
len, bufsize - ctrl->index);
|
||||
len = bufsize - ctrl->index;
|
||||
len, bufsize - elbc_fcm_ctrl->index);
|
||||
len = bufsize - elbc_fcm_ctrl->index;
|
||||
}
|
||||
|
||||
memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
|
||||
memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
|
||||
/*
|
||||
* This is workaround for the weird elbc hangs during nand write,
|
||||
* Scott Wood says: "...perhaps difference in how long it takes a
|
||||
@ -549,9 +547,9 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
|
||||
* is causing problems, and sync isn't helping for some reason."
|
||||
* Reading back the last byte helps though.
|
||||
*/
|
||||
in_8(&ctrl->addr[ctrl->index] + len - 1);
|
||||
in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
|
||||
|
||||
ctrl->index += len;
|
||||
elbc_fcm_ctrl->index += len;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -562,13 +560,13 @@ static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
|
||||
/* If there are still bytes in the FCM, then use the next byte. */
|
||||
if (ctrl->index < ctrl->read_bytes)
|
||||
return in_8(&ctrl->addr[ctrl->index++]);
|
||||
if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
|
||||
return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
|
||||
|
||||
dev_err(ctrl->dev, "read_byte beyond end of buffer\n");
|
||||
dev_err(priv->dev, "read_byte beyond end of buffer\n");
|
||||
return ERR_BYTE;
|
||||
}
|
||||
|
||||
@ -579,18 +577,19 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
int avail;
|
||||
|
||||
if (len < 0)
|
||||
return;
|
||||
|
||||
avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
|
||||
memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
|
||||
ctrl->index += avail;
|
||||
avail = min((unsigned int)len,
|
||||
elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
|
||||
memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
|
||||
elbc_fcm_ctrl->index += avail;
|
||||
|
||||
if (len > avail)
|
||||
dev_err(ctrl->dev,
|
||||
dev_err(priv->dev,
|
||||
"read_buf beyond end of buffer "
|
||||
"(%d requested, %d available)\n",
|
||||
len, avail);
|
||||
@ -603,30 +602,32 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
int i;
|
||||
|
||||
if (len < 0) {
|
||||
dev_err(ctrl->dev, "write_buf of %d bytes", len);
|
||||
dev_err(priv->dev, "write_buf of %d bytes", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((unsigned int)len > ctrl->read_bytes - ctrl->index) {
|
||||
dev_err(ctrl->dev,
|
||||
"verify_buf beyond end of buffer "
|
||||
"(%d requested, %u available)\n",
|
||||
len, ctrl->read_bytes - ctrl->index);
|
||||
if ((unsigned int)len >
|
||||
elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) {
|
||||
dev_err(priv->dev,
|
||||
"verify_buf beyond end of buffer "
|
||||
"(%d requested, %u available)\n",
|
||||
len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
|
||||
|
||||
ctrl->index = ctrl->read_bytes;
|
||||
elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
|
||||
if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i])
|
||||
!= buf[i])
|
||||
break;
|
||||
|
||||
ctrl->index += len;
|
||||
return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
|
||||
elbc_fcm_ctrl->index += len;
|
||||
return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO;
|
||||
}
|
||||
|
||||
/* This function is called after Program and Erase Operations to
|
||||
@ -635,22 +636,22 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
{
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
|
||||
if (ctrl->status != LTESR_CC)
|
||||
if (elbc_fcm_ctrl->status != LTESR_CC)
|
||||
return NAND_STATUS_FAIL;
|
||||
|
||||
/* The chip always seems to report that it is
|
||||
* write-protected, even when it is not.
|
||||
*/
|
||||
return (ctrl->mdr & 0xff) | NAND_STATUS_WP;
|
||||
return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
|
||||
}
|
||||
|
||||
static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
unsigned int al;
|
||||
|
||||
@ -665,41 +666,41 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
||||
priv->fmr |= (12 << FMR_CWTO_SHIFT) | /* Timeout > 12 ms */
|
||||
(al << FMR_AL_SHIFT);
|
||||
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
|
||||
chip->numchips);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
|
||||
chip->chipsize);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
|
||||
chip->pagemask);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
|
||||
chip->chip_delay);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
|
||||
chip->badblockpos);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
|
||||
chip->chip_shift);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
|
||||
chip->page_shift);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
|
||||
chip->phys_erase_shift);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
|
||||
chip->ecclayout);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
|
||||
chip->ecc.mode);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
|
||||
chip->ecc.steps);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
|
||||
chip->ecc.bytes);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
|
||||
chip->ecc.total);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
|
||||
chip->ecc.layout);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
|
||||
mtd->erasesize);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
|
||||
mtd->writesize);
|
||||
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
|
||||
dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
|
||||
mtd->oobsize);
|
||||
|
||||
/* adjust Option Register and ECC to match Flash page size */
|
||||
@ -719,7 +720,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
||||
chip->badblock_pattern = &largepage_memorybased;
|
||||
}
|
||||
} else {
|
||||
dev_err(ctrl->dev,
|
||||
dev_err(priv->dev,
|
||||
"fsl_elbc_init: page size %d is not supported\n",
|
||||
mtd->writesize);
|
||||
return -1;
|
||||
@ -750,18 +751,19 @@ static void fsl_elbc_write_page(struct mtd_info *mtd,
|
||||
const uint8_t *buf)
|
||||
{
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
|
||||
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
||||
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
ctrl->oob_poi = chip->oob_poi;
|
||||
elbc_fcm_ctrl->oob_poi = chip->oob_poi;
|
||||
}
|
||||
|
||||
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||
{
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
|
||||
struct nand_chip *chip = &priv->chip;
|
||||
|
||||
dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
|
||||
@ -790,7 +792,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||
chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
|
||||
NAND_USE_FLASH_BBT;
|
||||
|
||||
chip->controller = &ctrl->controller;
|
||||
chip->controller = &elbc_fcm_ctrl->controller;
|
||||
chip->priv = priv;
|
||||
|
||||
chip->ecc.read_page = fsl_elbc_read_page;
|
||||
@ -815,8 +817,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||
|
||||
static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
|
||||
{
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
|
||||
nand_release(&priv->mtd);
|
||||
|
||||
kfree(priv->mtd.name);
|
||||
@ -824,18 +825,21 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
|
||||
if (priv->vbase)
|
||||
iounmap(priv->vbase);
|
||||
|
||||
ctrl->chips[priv->bank] = NULL;
|
||||
elbc_fcm_ctrl->chips[priv->bank] = NULL;
|
||||
kfree(priv);
|
||||
|
||||
kfree(elbc_fcm_ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
|
||||
struct device_node *node)
|
||||
static DEFINE_MUTEX(fsl_elbc_nand_mutex);
|
||||
|
||||
static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
struct fsl_lbc_regs __iomem *lbc;
|
||||
struct fsl_elbc_mtd *priv;
|
||||
struct resource res;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *part_probe_types[]
|
||||
= { "cmdlinepart", "RedBoot", NULL };
|
||||
@ -843,11 +847,18 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
|
||||
#endif
|
||||
int ret;
|
||||
int bank;
|
||||
struct device *dev;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
|
||||
return -ENODEV;
|
||||
lbc = fsl_lbc_ctrl_dev->regs;
|
||||
dev = fsl_lbc_ctrl_dev->dev;
|
||||
|
||||
/* get, allocate and map the memory resource */
|
||||
ret = of_address_to_resource(node, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(ctrl->dev, "failed to get resource\n");
|
||||
dev_err(dev, "failed to get resource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -857,11 +868,11 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
|
||||
(in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
|
||||
(in_be32(&lbc->bank[bank].br) &
|
||||
in_be32(&lbc->bank[bank].or) & BR_BA)
|
||||
== res.start)
|
||||
== fsl_lbc_addr(res.start))
|
||||
break;
|
||||
|
||||
if (bank >= MAX_BANKS) {
|
||||
dev_err(ctrl->dev, "address did not match any chip selects\n");
|
||||
dev_err(dev, "address did not match any chip selects\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -869,14 +880,33 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
ctrl->chips[bank] = priv;
|
||||
mutex_lock(&fsl_elbc_nand_mutex);
|
||||
if (!fsl_lbc_ctrl_dev->nand) {
|
||||
elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
|
||||
if (!elbc_fcm_ctrl) {
|
||||
dev_err(dev, "failed to allocate memory\n");
|
||||
mutex_unlock(&fsl_elbc_nand_mutex);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
elbc_fcm_ctrl->counter++;
|
||||
|
||||
spin_lock_init(&elbc_fcm_ctrl->controller.lock);
|
||||
init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);
|
||||
fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
|
||||
} else {
|
||||
elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
|
||||
}
|
||||
mutex_unlock(&fsl_elbc_nand_mutex);
|
||||
|
||||
elbc_fcm_ctrl->chips[bank] = priv;
|
||||
priv->bank = bank;
|
||||
priv->ctrl = ctrl;
|
||||
priv->dev = ctrl->dev;
|
||||
priv->ctrl = fsl_lbc_ctrl_dev;
|
||||
priv->dev = dev;
|
||||
|
||||
priv->vbase = ioremap(res.start, resource_size(&res));
|
||||
if (!priv->vbase) {
|
||||
dev_err(ctrl->dev, "failed to map chip region\n");
|
||||
dev_err(dev, "failed to map chip region\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
@ -933,171 +963,53 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
|
||||
static int fsl_elbc_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
|
||||
/*
|
||||
* NAND transactions can tie up the bus for a long time, so set the
|
||||
* bus timeout to max by clearing LBCR[BMT] (highest base counter
|
||||
* value) and setting LBCR[BMTPS] to the highest prescaler value.
|
||||
*/
|
||||
clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15);
|
||||
|
||||
/* clear event registers */
|
||||
setbits32(&lbc->ltesr, LTESR_NAND_MASK);
|
||||
out_be32(&lbc->lteatr, 0);
|
||||
|
||||
/* Enable interrupts for any detected events */
|
||||
out_be32(&lbc->lteir, LTESR_NAND_MASK);
|
||||
|
||||
ctrl->read_bytes = 0;
|
||||
ctrl->index = 0;
|
||||
ctrl->addr = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_elbc_ctrl_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
|
||||
int i;
|
||||
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
|
||||
for (i = 0; i < MAX_BANKS; i++)
|
||||
if (ctrl->chips[i])
|
||||
fsl_elbc_chip_remove(ctrl->chips[i]);
|
||||
if (elbc_fcm_ctrl->chips[i])
|
||||
fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]);
|
||||
|
||||
if (ctrl->irq)
|
||||
free_irq(ctrl->irq, ctrl);
|
||||
|
||||
if (ctrl->regs)
|
||||
iounmap(ctrl->regs);
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
kfree(ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: This interrupt is also used to report other localbus events,
|
||||
* such as transaction errors on other chipselects. If we want to
|
||||
* capture those, we'll need to move the IRQ code into a shared
|
||||
* LBC driver.
|
||||
*/
|
||||
|
||||
static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
|
||||
{
|
||||
struct fsl_elbc_ctrl *ctrl = data;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
__be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK;
|
||||
|
||||
if (status) {
|
||||
out_be32(&lbc->ltesr, status);
|
||||
out_be32(&lbc->lteatr, 0);
|
||||
|
||||
ctrl->irq_status = status;
|
||||
smp_wmb();
|
||||
wake_up(&ctrl->irq_wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
mutex_lock(&fsl_elbc_nand_mutex);
|
||||
elbc_fcm_ctrl->counter--;
|
||||
if (!elbc_fcm_ctrl->counter) {
|
||||
fsl_lbc_ctrl_dev->nand = NULL;
|
||||
kfree(elbc_fcm_ctrl);
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* fsl_elbc_ctrl_probe
|
||||
*
|
||||
* called by device layer when it finds a device matching
|
||||
* one our driver can handled. This code allocates all of
|
||||
* the resources needed for the controller only. The
|
||||
* resources for the NAND banks themselves are allocated
|
||||
* in the chip probe function.
|
||||
*/
|
||||
|
||||
static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct fsl_elbc_ctrl *ctrl;
|
||||
int ret;
|
||||
|
||||
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, ctrl);
|
||||
|
||||
spin_lock_init(&ctrl->controller.lock);
|
||||
init_waitqueue_head(&ctrl->controller.wq);
|
||||
init_waitqueue_head(&ctrl->irq_wait);
|
||||
|
||||
ctrl->regs = of_iomap(ofdev->dev.of_node, 0);
|
||||
if (!ctrl->regs) {
|
||||
dev_err(&ofdev->dev, "failed to get memory region\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ctrl->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
|
||||
if (ctrl->irq == NO_IRQ) {
|
||||
dev_err(&ofdev->dev, "failed to get irq resource\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ctrl->dev = &ofdev->dev;
|
||||
|
||||
ret = fsl_elbc_ctrl_init(ctrl);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl);
|
||||
if (ret != 0) {
|
||||
dev_err(&ofdev->dev, "failed to install irq (%d)\n",
|
||||
ctrl->irq);
|
||||
ret = ctrl->irq;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for_each_child_of_node(ofdev->dev.of_node, child)
|
||||
if (of_device_is_compatible(child, "fsl,elbc-fcm-nand"))
|
||||
fsl_elbc_chip_probe(ctrl, child);
|
||||
mutex_unlock(&fsl_elbc_nand_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
fsl_elbc_ctrl_remove(ofdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_elbc_match[] = {
|
||||
{
|
||||
.compatible = "fsl,elbc",
|
||||
},
|
||||
static const struct of_device_id fsl_elbc_nand_match[] = {
|
||||
{ .compatible = "fsl,elbc-fcm-nand", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct of_platform_driver fsl_elbc_ctrl_driver = {
|
||||
static struct platform_driver fsl_elbc_nand_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-elbc",
|
||||
.name = "fsl,elbc-fcm-nand",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = fsl_elbc_match,
|
||||
.of_match_table = fsl_elbc_nand_match,
|
||||
},
|
||||
.probe = fsl_elbc_ctrl_probe,
|
||||
.remove = fsl_elbc_ctrl_remove,
|
||||
.probe = fsl_elbc_nand_probe,
|
||||
.remove = fsl_elbc_nand_remove,
|
||||
};
|
||||
|
||||
static int __init fsl_elbc_init(void)
|
||||
static int __init fsl_elbc_nand_init(void)
|
||||
{
|
||||
return of_register_platform_driver(&fsl_elbc_ctrl_driver);
|
||||
return platform_driver_register(&fsl_elbc_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit fsl_elbc_exit(void)
|
||||
static void __exit fsl_elbc_nand_exit(void)
|
||||
{
|
||||
of_unregister_platform_driver(&fsl_elbc_ctrl_driver);
|
||||
platform_driver_unregister(&fsl_elbc_nand_driver);
|
||||
}
|
||||
|
||||
module_init(fsl_elbc_init);
|
||||
module_exit(fsl_elbc_exit);
|
||||
module_init(fsl_elbc_nand_init);
|
||||
module_exit(fsl_elbc_nand_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Freescale");
|
||||
|
@ -186,7 +186,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
|
||||
if (!flash_np)
|
||||
return -ENODEV;
|
||||
|
||||
fun->mtd.name = kasprintf(GFP_KERNEL, "%x.%s", io_res->start,
|
||||
fun->mtd.name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start,
|
||||
flash_np->name);
|
||||
if (!fun->mtd.name) {
|
||||
ret = -ENOMEM;
|
||||
@ -222,7 +222,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
|
||||
{
|
||||
struct fsl_upm_nand *fun;
|
||||
struct resource io_res;
|
||||
const uint32_t *prop;
|
||||
const __be32 *prop;
|
||||
int rnb_gpio;
|
||||
int ret;
|
||||
int size;
|
||||
@ -270,7 +270,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
|
||||
goto err1;
|
||||
}
|
||||
for (i = 0; i < fun->mchip_count; i++)
|
||||
fun->mchip_offsets[i] = prop[i];
|
||||
fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
|
||||
} else {
|
||||
fun->mchip_count = 1;
|
||||
}
|
||||
@ -295,13 +295,13 @@ static int __devinit fun_probe(struct platform_device *ofdev,
|
||||
|
||||
prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL);
|
||||
if (prop)
|
||||
fun->chip_delay = *prop;
|
||||
fun->chip_delay = be32_to_cpup(prop);
|
||||
else
|
||||
fun->chip_delay = 50;
|
||||
|
||||
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size);
|
||||
if (prop && size == sizeof(uint32_t))
|
||||
fun->wait_flags = *prop;
|
||||
fun->wait_flags = be32_to_cpup(prop);
|
||||
else
|
||||
fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
|
||||
FSL_UPM_WAIT_WRITE_BYTE;
|
||||
|
866
drivers/mtd/nand/fsmc_nand.c
Normal file
866
drivers/mtd/nand/fsmc_nand.c
Normal file
@ -0,0 +1,866 @@
|
||||
/*
|
||||
* drivers/mtd/nand/fsmc_nand.c
|
||||
*
|
||||
* ST Microelectronics
|
||||
* Flexible Static Memory Controller (FSMC)
|
||||
* Driver for NAND portions
|
||||
*
|
||||
* Copyright © 2010 ST Microelectronics
|
||||
* Vipin Kumar <vipin.kumar@st.com>
|
||||
* Ashish Priyadarshi
|
||||
*
|
||||
* Based on drivers/mtd/nand/nomadik_nand.c
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/fsmc.h>
|
||||
#include <mtd/mtd-abi.h>
|
||||
|
||||
static struct nand_ecclayout fsmc_ecc1_layout = {
|
||||
.eccbytes = 24,
|
||||
.eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
|
||||
66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
|
||||
.oobfree = {
|
||||
{.offset = 8, .length = 8},
|
||||
{.offset = 24, .length = 8},
|
||||
{.offset = 40, .length = 8},
|
||||
{.offset = 56, .length = 8},
|
||||
{.offset = 72, .length = 8},
|
||||
{.offset = 88, .length = 8},
|
||||
{.offset = 104, .length = 8},
|
||||
{.offset = 120, .length = 8}
|
||||
}
|
||||
};
|
||||
|
||||
static struct nand_ecclayout fsmc_ecc4_lp_layout = {
|
||||
.eccbytes = 104,
|
||||
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14,
|
||||
18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30,
|
||||
34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46,
|
||||
50, 51, 52, 53, 54, 55, 56,
|
||||
57, 58, 59, 60, 61, 62,
|
||||
66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78,
|
||||
82, 83, 84, 85, 86, 87, 88,
|
||||
89, 90, 91, 92, 93, 94,
|
||||
98, 99, 100, 101, 102, 103, 104,
|
||||
105, 106, 107, 108, 109, 110,
|
||||
114, 115, 116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125, 126
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 15, .length = 3},
|
||||
{.offset = 31, .length = 3},
|
||||
{.offset = 47, .length = 3},
|
||||
{.offset = 63, .length = 3},
|
||||
{.offset = 79, .length = 3},
|
||||
{.offset = 95, .length = 3},
|
||||
{.offset = 111, .length = 3},
|
||||
{.offset = 127, .length = 1}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ECC placement definitions in oobfree type format.
|
||||
* There are 13 bytes of ecc for every 512 byte block and it has to be read
|
||||
* consecutively and immediately after the 512 byte data block for hardware to
|
||||
* generate the error bit offsets in 512 byte data.
|
||||
* Managing the ecc bytes in the following way makes it easier for software to
|
||||
* read ecc bytes consecutive to data bytes. This way is similar to
|
||||
* oobfree structure maintained already in generic nand driver
|
||||
*/
|
||||
static struct fsmc_eccplace fsmc_ecc4_lp_place = {
|
||||
.eccplace = {
|
||||
{.offset = 2, .length = 13},
|
||||
{.offset = 18, .length = 13},
|
||||
{.offset = 34, .length = 13},
|
||||
{.offset = 50, .length = 13},
|
||||
{.offset = 66, .length = 13},
|
||||
{.offset = 82, .length = 13},
|
||||
{.offset = 98, .length = 13},
|
||||
{.offset = 114, .length = 13}
|
||||
}
|
||||
};
|
||||
|
||||
static struct nand_ecclayout fsmc_ecc4_sp_layout = {
|
||||
.eccbytes = 13,
|
||||
.eccpos = { 0, 1, 2, 3, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 15, .length = 1},
|
||||
}
|
||||
};
|
||||
|
||||
static struct fsmc_eccplace fsmc_ecc4_sp_place = {
|
||||
.eccplace = {
|
||||
{.offset = 0, .length = 4},
|
||||
{.offset = 6, .length = 9}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Default partition tables to be used if the partition information not
|
||||
* provided through platform data
|
||||
*/
|
||||
#define PARTITION(n, off, sz) {.name = n, .offset = off, .size = sz}
|
||||
|
||||
/*
|
||||
* Default partition layout for small page(= 512 bytes) devices
|
||||
* Size for "Root file system" is updated in driver based on actual device size
|
||||
*/
|
||||
static struct mtd_partition partition_info_16KB_blk[] = {
|
||||
PARTITION("X-loader", 0, 4 * 0x4000),
|
||||
PARTITION("U-Boot", 0x10000, 20 * 0x4000),
|
||||
PARTITION("Kernel", 0x60000, 256 * 0x4000),
|
||||
PARTITION("Root File System", 0x460000, 0),
|
||||
};
|
||||
|
||||
/*
|
||||
* Default partition layout for large page(> 512 bytes) devices
|
||||
* Size for "Root file system" is updated in driver based on actual device size
|
||||
*/
|
||||
static struct mtd_partition partition_info_128KB_blk[] = {
|
||||
PARTITION("X-loader", 0, 4 * 0x20000),
|
||||
PARTITION("U-Boot", 0x80000, 12 * 0x20000),
|
||||
PARTITION("Kernel", 0x200000, 48 * 0x20000),
|
||||
PARTITION("Root File System", 0x800000, 0),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct fsmc_nand_data - atructure for FSMC NAND device state
|
||||
*
|
||||
* @mtd: MTD info for a NAND flash.
|
||||
* @nand: Chip related info for a NAND flash.
|
||||
* @partitions: Partition info for a NAND Flash.
|
||||
* @nr_partitions: Total number of partition of a NAND flash.
|
||||
*
|
||||
* @ecc_place: ECC placing locations in oobfree type format.
|
||||
* @bank: Bank number for probed device.
|
||||
* @clk: Clock structure for FSMC.
|
||||
*
|
||||
* @data_va: NAND port for Data.
|
||||
* @cmd_va: NAND port for Command.
|
||||
* @addr_va: NAND port for Address.
|
||||
* @regs_va: FSMC regs base address.
|
||||
*/
|
||||
struct fsmc_nand_data {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip nand;
|
||||
struct mtd_partition *partitions;
|
||||
unsigned int nr_partitions;
|
||||
|
||||
struct fsmc_eccplace *ecc_place;
|
||||
unsigned int bank;
|
||||
struct clk *clk;
|
||||
|
||||
struct resource *resregs;
|
||||
struct resource *rescmd;
|
||||
struct resource *resaddr;
|
||||
struct resource *resdata;
|
||||
|
||||
void __iomem *data_va;
|
||||
void __iomem *cmd_va;
|
||||
void __iomem *addr_va;
|
||||
void __iomem *regs_va;
|
||||
|
||||
void (*select_chip)(uint32_t bank, uint32_t busw);
|
||||
};
|
||||
|
||||
/* Assert CS signal based on chipnr */
|
||||
static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsmc_nand_data *host;
|
||||
|
||||
host = container_of(mtd, struct fsmc_nand_data, mtd);
|
||||
|
||||
switch (chipnr) {
|
||||
case -1:
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (host->select_chip)
|
||||
host->select_chip(chipnr,
|
||||
chip->options & NAND_BUSWIDTH_16);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_cmd_ctrl - For facilitaing Hardware access
|
||||
* This routine allows hardware specific access to control-lines(ALE,CLE)
|
||||
*/
|
||||
static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct fsmc_nand_data *host = container_of(mtd,
|
||||
struct fsmc_nand_data, mtd);
|
||||
struct fsmc_regs *regs = host->regs_va;
|
||||
unsigned int bank = host->bank;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
if (ctrl & NAND_CLE) {
|
||||
this->IO_ADDR_R = (void __iomem *)host->cmd_va;
|
||||
this->IO_ADDR_W = (void __iomem *)host->cmd_va;
|
||||
} else if (ctrl & NAND_ALE) {
|
||||
this->IO_ADDR_R = (void __iomem *)host->addr_va;
|
||||
this->IO_ADDR_W = (void __iomem *)host->addr_va;
|
||||
} else {
|
||||
this->IO_ADDR_R = (void __iomem *)host->data_va;
|
||||
this->IO_ADDR_W = (void __iomem *)host->data_va;
|
||||
}
|
||||
|
||||
if (ctrl & NAND_NCE) {
|
||||
writel(readl(®s->bank_regs[bank].pc) | FSMC_ENABLE,
|
||||
®s->bank_regs[bank].pc);
|
||||
} else {
|
||||
writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ENABLE,
|
||||
®s->bank_regs[bank].pc);
|
||||
}
|
||||
}
|
||||
|
||||
mb();
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, this->IO_ADDR_W);
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
|
||||
*
|
||||
* This routine initializes timing parameters related to NAND memory access in
|
||||
* FSMC registers
|
||||
*/
|
||||
static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank,
|
||||
uint32_t busw)
|
||||
{
|
||||
uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
|
||||
|
||||
if (busw)
|
||||
writel(value | FSMC_DEVWID_16, ®s->bank_regs[bank].pc);
|
||||
else
|
||||
writel(value | FSMC_DEVWID_8, ®s->bank_regs[bank].pc);
|
||||
|
||||
writel(readl(®s->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1,
|
||||
®s->bank_regs[bank].pc);
|
||||
writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
|
||||
®s->bank_regs[bank].comm);
|
||||
writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
|
||||
®s->bank_regs[bank].attrib);
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
|
||||
*/
|
||||
static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct fsmc_nand_data *host = container_of(mtd,
|
||||
struct fsmc_nand_data, mtd);
|
||||
struct fsmc_regs *regs = host->regs_va;
|
||||
uint32_t bank = host->bank;
|
||||
|
||||
writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256,
|
||||
®s->bank_regs[bank].pc);
|
||||
writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCEN,
|
||||
®s->bank_regs[bank].pc);
|
||||
writel(readl(®s->bank_regs[bank].pc) | FSMC_ECCEN,
|
||||
®s->bank_regs[bank].pc);
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by
|
||||
* FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction upto
|
||||
* max of 8-bits)
|
||||
*/
|
||||
static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
|
||||
uint8_t *ecc)
|
||||
{
|
||||
struct fsmc_nand_data *host = container_of(mtd,
|
||||
struct fsmc_nand_data, mtd);
|
||||
struct fsmc_regs *regs = host->regs_va;
|
||||
uint32_t bank = host->bank;
|
||||
uint32_t ecc_tmp;
|
||||
unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
|
||||
|
||||
do {
|
||||
if (readl(®s->bank_regs[bank].sts) & FSMC_CODE_RDY)
|
||||
break;
|
||||
else
|
||||
cond_resched();
|
||||
} while (!time_after_eq(jiffies, deadline));
|
||||
|
||||
ecc_tmp = readl(®s->bank_regs[bank].ecc1);
|
||||
ecc[0] = (uint8_t) (ecc_tmp >> 0);
|
||||
ecc[1] = (uint8_t) (ecc_tmp >> 8);
|
||||
ecc[2] = (uint8_t) (ecc_tmp >> 16);
|
||||
ecc[3] = (uint8_t) (ecc_tmp >> 24);
|
||||
|
||||
ecc_tmp = readl(®s->bank_regs[bank].ecc2);
|
||||
ecc[4] = (uint8_t) (ecc_tmp >> 0);
|
||||
ecc[5] = (uint8_t) (ecc_tmp >> 8);
|
||||
ecc[6] = (uint8_t) (ecc_tmp >> 16);
|
||||
ecc[7] = (uint8_t) (ecc_tmp >> 24);
|
||||
|
||||
ecc_tmp = readl(®s->bank_regs[bank].ecc3);
|
||||
ecc[8] = (uint8_t) (ecc_tmp >> 0);
|
||||
ecc[9] = (uint8_t) (ecc_tmp >> 8);
|
||||
ecc[10] = (uint8_t) (ecc_tmp >> 16);
|
||||
ecc[11] = (uint8_t) (ecc_tmp >> 24);
|
||||
|
||||
ecc_tmp = readl(®s->bank_regs[bank].sts);
|
||||
ecc[12] = (uint8_t) (ecc_tmp >> 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by
|
||||
* FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction upto
|
||||
* max of 1-bit)
|
||||
*/
|
||||
static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
|
||||
uint8_t *ecc)
|
||||
{
|
||||
struct fsmc_nand_data *host = container_of(mtd,
|
||||
struct fsmc_nand_data, mtd);
|
||||
struct fsmc_regs *regs = host->regs_va;
|
||||
uint32_t bank = host->bank;
|
||||
uint32_t ecc_tmp;
|
||||
|
||||
ecc_tmp = readl(®s->bank_regs[bank].ecc1);
|
||||
ecc[0] = (uint8_t) (ecc_tmp >> 0);
|
||||
ecc[1] = (uint8_t) (ecc_tmp >> 8);
|
||||
ecc[2] = (uint8_t) (ecc_tmp >> 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_read_page_hwecc
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @page: page number to read
|
||||
*
|
||||
* This routine is needed for fsmc verison 8 as reading from NAND chip has to be
|
||||
* performed in a strict sequence as follows:
|
||||
* data(512 byte) -> ecc(13 byte)
|
||||
* After this read, fsmc hardware generates and reports error data bits(upto a
|
||||
* max of 8 bits)
|
||||
*/
|
||||
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
{
|
||||
struct fsmc_nand_data *host = container_of(mtd,
|
||||
struct fsmc_nand_data, mtd);
|
||||
struct fsmc_eccplace *ecc_place = host->ecc_place;
|
||||
int i, j, s, stat, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
int off, len, group = 0;
|
||||
/*
|
||||
* ecc_oob is intentionally taken as uint16_t. In 16bit devices, we
|
||||
* end up reading 14 bytes (7 words) from oob. The local array is
|
||||
* to maintain word alignment
|
||||
*/
|
||||
uint16_t ecc_oob[7];
|
||||
uint8_t *oob = (uint8_t *)&ecc_oob[0];
|
||||
|
||||
for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
chip->read_buf(mtd, p, eccsize);
|
||||
|
||||
for (j = 0; j < eccbytes;) {
|
||||
off = ecc_place->eccplace[group].offset;
|
||||
len = ecc_place->eccplace[group].length;
|
||||
group++;
|
||||
|
||||
/*
|
||||
* length is intentionally kept a higher multiple of 2
|
||||
* to read at least 13 bytes even in case of 16 bit NAND
|
||||
* devices
|
||||
*/
|
||||
len = roundup(len, 2);
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page);
|
||||
chip->read_buf(mtd, oob + j, len);
|
||||
j += len;
|
||||
}
|
||||
|
||||
memcpy(&ecc_code[i], oob, 13);
|
||||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
if (stat < 0)
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_correct_data
|
||||
* @mtd: mtd info structure
|
||||
* @dat: buffer of read data
|
||||
* @read_ecc: ecc read from device spare area
|
||||
* @calc_ecc: ecc calculated from read data
|
||||
*
|
||||
* calc_ecc is a 104 bit information containing maximum of 8 error
|
||||
* offset informations of 13 bits each in 512 bytes of read data.
|
||||
*/
|
||||
static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
|
||||
uint8_t *read_ecc, uint8_t *calc_ecc)
|
||||
{
|
||||
struct fsmc_nand_data *host = container_of(mtd,
|
||||
struct fsmc_nand_data, mtd);
|
||||
struct fsmc_regs *regs = host->regs_va;
|
||||
unsigned int bank = host->bank;
|
||||
uint16_t err_idx[8];
|
||||
uint64_t ecc_data[2];
|
||||
uint32_t num_err, i;
|
||||
|
||||
/* The calculated ecc is actually the correction index in data */
|
||||
memcpy(ecc_data, calc_ecc, 13);
|
||||
|
||||
/*
|
||||
* ------------------- calc_ecc[] bit wise -----------|--13 bits--|
|
||||
* |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--|
|
||||
*
|
||||
* calc_ecc is a 104 bit information containing maximum of 8 error
|
||||
* offset informations of 13 bits each. calc_ecc is copied into a
|
||||
* uint64_t array and error offset indexes are populated in err_idx
|
||||
* array
|
||||
*/
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i == 4) {
|
||||
err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0];
|
||||
ecc_data[1] >>= 1;
|
||||
continue;
|
||||
}
|
||||
err_idx[i] = (ecc_data[i/4] & 0x1FFF);
|
||||
ecc_data[i/4] >>= 13;
|
||||
}
|
||||
|
||||
num_err = (readl(®s->bank_regs[bank].sts) >> 10) & 0xF;
|
||||
|
||||
if (num_err == 0xF)
|
||||
return -EBADMSG;
|
||||
|
||||
i = 0;
|
||||
while (num_err--) {
|
||||
change_bit(0, (unsigned long *)&err_idx[i]);
|
||||
change_bit(1, (unsigned long *)&err_idx[i]);
|
||||
|
||||
if (err_idx[i] <= 512 * 8) {
|
||||
change_bit(err_idx[i], (unsigned long *)dat);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_nand_probe - Probe function
|
||||
* @pdev: platform device structure
|
||||
*/
|
||||
static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct fsmc_nand_data *host;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *nand;
|
||||
struct fsmc_regs *regs;
|
||||
struct resource *res;
|
||||
int nr_parts, ret = 0;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "platform data is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Allocate memory for the device structure (and zero it) */
|
||||
host = kzalloc(sizeof(*host), GFP_KERNEL);
|
||||
if (!host) {
|
||||
dev_err(&pdev->dev, "failed to allocate device structure\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
|
||||
if (!res) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->resdata = request_mem_region(res->start, resource_size(res),
|
||||
pdev->name);
|
||||
if (!host->resdata) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->data_va = ioremap(res->start, resource_size(res));
|
||||
if (!host->data_va) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE,
|
||||
resource_size(res), pdev->name);
|
||||
if (!host->resaddr) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res));
|
||||
if (!host->addr_va) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE,
|
||||
resource_size(res), pdev->name);
|
||||
if (!host->rescmd) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res));
|
||||
if (!host->cmd_va) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
|
||||
if (!res) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->resregs = request_mem_region(res->start, resource_size(res),
|
||||
pdev->name);
|
||||
if (!host->resregs) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->regs_va = ioremap(res->start, resource_size(res));
|
||||
if (!host->regs_va) {
|
||||
ret = -EIO;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
host->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(host->clk)) {
|
||||
dev_err(&pdev->dev, "failed to fetch block clock\n");
|
||||
ret = PTR_ERR(host->clk);
|
||||
host->clk = NULL;
|
||||
goto err_probe1;
|
||||
}
|
||||
|
||||
ret = clk_enable(host->clk);
|
||||
if (ret)
|
||||
goto err_probe1;
|
||||
|
||||
host->bank = pdata->bank;
|
||||
host->select_chip = pdata->select_bank;
|
||||
regs = host->regs_va;
|
||||
|
||||
/* Link all private pointers */
|
||||
mtd = &host->mtd;
|
||||
nand = &host->nand;
|
||||
mtd->priv = nand;
|
||||
nand->priv = host;
|
||||
|
||||
host->mtd.owner = THIS_MODULE;
|
||||
nand->IO_ADDR_R = host->data_va;
|
||||
nand->IO_ADDR_W = host->data_va;
|
||||
nand->cmd_ctrl = fsmc_cmd_ctrl;
|
||||
nand->chip_delay = 30;
|
||||
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.hwctl = fsmc_enable_hwecc;
|
||||
nand->ecc.size = 512;
|
||||
nand->options = pdata->options;
|
||||
nand->select_chip = fsmc_select_chip;
|
||||
|
||||
if (pdata->width == FSMC_NAND_BW16)
|
||||
nand->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16);
|
||||
|
||||
if (get_fsmc_version(host->regs_va) == FSMC_VER8) {
|
||||
nand->ecc.read_page = fsmc_read_page_hwecc;
|
||||
nand->ecc.calculate = fsmc_read_hwecc_ecc4;
|
||||
nand->ecc.correct = fsmc_correct_data;
|
||||
nand->ecc.bytes = 13;
|
||||
} else {
|
||||
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
|
||||
nand->ecc.correct = nand_correct_data;
|
||||
nand->ecc.bytes = 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan to find existance of the device
|
||||
*/
|
||||
if (nand_scan_ident(&host->mtd, 1, NULL)) {
|
||||
ret = -ENXIO;
|
||||
dev_err(&pdev->dev, "No NAND Device found!\n");
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
if (get_fsmc_version(host->regs_va) == FSMC_VER8) {
|
||||
if (host->mtd.writesize == 512) {
|
||||
nand->ecc.layout = &fsmc_ecc4_sp_layout;
|
||||
host->ecc_place = &fsmc_ecc4_sp_place;
|
||||
} else {
|
||||
nand->ecc.layout = &fsmc_ecc4_lp_layout;
|
||||
host->ecc_place = &fsmc_ecc4_lp_place;
|
||||
}
|
||||
} else {
|
||||
nand->ecc.layout = &fsmc_ecc1_layout;
|
||||
}
|
||||
|
||||
/* Second stage of scan to fill MTD data-structures */
|
||||
if (nand_scan_tail(&host->mtd)) {
|
||||
ret = -ENXIO;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
/*
|
||||
* The partition information can is accessed by (in the same precedence)
|
||||
*
|
||||
* command line through Bootloader,
|
||||
* platform data,
|
||||
* default partition information present in driver.
|
||||
*/
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
/*
|
||||
* Check if partition info passed via command line
|
||||
*/
|
||||
host->mtd.name = "nand";
|
||||
nr_parts = parse_mtd_partitions(&host->mtd, part_probes,
|
||||
&host->partitions, 0);
|
||||
if (nr_parts > 0) {
|
||||
host->nr_partitions = nr_parts;
|
||||
} else {
|
||||
#endif
|
||||
/*
|
||||
* Check if partition info passed via command line
|
||||
*/
|
||||
if (pdata->partitions) {
|
||||
host->partitions = pdata->partitions;
|
||||
host->nr_partitions = pdata->nr_partitions;
|
||||
} else {
|
||||
struct mtd_partition *partition;
|
||||
int i;
|
||||
|
||||
/* Select the default partitions info */
|
||||
switch (host->mtd.size) {
|
||||
case 0x01000000:
|
||||
case 0x02000000:
|
||||
case 0x04000000:
|
||||
host->partitions = partition_info_16KB_blk;
|
||||
host->nr_partitions =
|
||||
sizeof(partition_info_16KB_blk) /
|
||||
sizeof(struct mtd_partition);
|
||||
break;
|
||||
case 0x08000000:
|
||||
case 0x10000000:
|
||||
case 0x20000000:
|
||||
case 0x40000000:
|
||||
host->partitions = partition_info_128KB_blk;
|
||||
host->nr_partitions =
|
||||
sizeof(partition_info_128KB_blk) /
|
||||
sizeof(struct mtd_partition);
|
||||
break;
|
||||
default:
|
||||
ret = -ENXIO;
|
||||
pr_err("Unsupported NAND size\n");
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
partition = host->partitions;
|
||||
for (i = 0; i < host->nr_partitions; i++, partition++) {
|
||||
if (partition->size == 0) {
|
||||
partition->size = host->mtd.size -
|
||||
partition->offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
}
|
||||
#endif
|
||||
|
||||
if (host->partitions) {
|
||||
ret = add_mtd_partitions(&host->mtd, host->partitions,
|
||||
host->nr_partitions);
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
}
|
||||
#else
|
||||
dev_info(&pdev->dev, "Registering %s as whole device\n", mtd->name);
|
||||
if (!add_mtd_device(mtd)) {
|
||||
ret = -ENXIO;
|
||||
goto err_probe;
|
||||
}
|
||||
#endif
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
|
||||
return 0;
|
||||
|
||||
err_probe:
|
||||
clk_disable(host->clk);
|
||||
err_probe1:
|
||||
if (host->clk)
|
||||
clk_put(host->clk);
|
||||
if (host->regs_va)
|
||||
iounmap(host->regs_va);
|
||||
if (host->resregs)
|
||||
release_mem_region(host->resregs->start,
|
||||
resource_size(host->resregs));
|
||||
if (host->cmd_va)
|
||||
iounmap(host->cmd_va);
|
||||
if (host->rescmd)
|
||||
release_mem_region(host->rescmd->start,
|
||||
resource_size(host->rescmd));
|
||||
if (host->addr_va)
|
||||
iounmap(host->addr_va);
|
||||
if (host->resaddr)
|
||||
release_mem_region(host->resaddr->start,
|
||||
resource_size(host->resaddr));
|
||||
if (host->data_va)
|
||||
iounmap(host->data_va);
|
||||
if (host->resdata)
|
||||
release_mem_region(host->resdata->start,
|
||||
resource_size(host->resdata));
|
||||
|
||||
kfree(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static int fsmc_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsmc_nand_data *host = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (host) {
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
del_mtd_partitions(&host->mtd);
|
||||
#else
|
||||
del_mtd_device(&host->mtd);
|
||||
#endif
|
||||
clk_disable(host->clk);
|
||||
clk_put(host->clk);
|
||||
|
||||
iounmap(host->regs_va);
|
||||
release_mem_region(host->resregs->start,
|
||||
resource_size(host->resregs));
|
||||
iounmap(host->cmd_va);
|
||||
release_mem_region(host->rescmd->start,
|
||||
resource_size(host->rescmd));
|
||||
iounmap(host->addr_va);
|
||||
release_mem_region(host->resaddr->start,
|
||||
resource_size(host->resaddr));
|
||||
iounmap(host->data_va);
|
||||
release_mem_region(host->resdata->start,
|
||||
resource_size(host->resdata));
|
||||
|
||||
kfree(host);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsmc_nand_suspend(struct device *dev)
|
||||
{
|
||||
struct fsmc_nand_data *host = dev_get_drvdata(dev);
|
||||
if (host)
|
||||
clk_disable(host->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsmc_nand_resume(struct device *dev)
|
||||
{
|
||||
struct fsmc_nand_data *host = dev_get_drvdata(dev);
|
||||
if (host)
|
||||
clk_enable(host->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops fsmc_nand_pm_ops = {
|
||||
.suspend = fsmc_nand_suspend,
|
||||
.resume = fsmc_nand_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver fsmc_nand_driver = {
|
||||
.remove = fsmc_nand_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fsmc-nand",
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &fsmc_nand_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init fsmc_nand_init(void)
|
||||
{
|
||||
return platform_driver_probe(&fsmc_nand_driver,
|
||||
fsmc_nand_probe);
|
||||
}
|
||||
module_init(fsmc_nand_init);
|
||||
|
||||
static void __exit fsmc_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&fsmc_nand_driver);
|
||||
}
|
||||
module_exit(fsmc_nand_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
|
||||
MODULE_DESCRIPTION("NAND driver for SPEAr Platforms");
|
@ -568,6 +568,7 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
|
||||
uint rcw_width;
|
||||
uint rcwh;
|
||||
uint romloc, ps;
|
||||
int ret = 0;
|
||||
|
||||
rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");
|
||||
if (!rmnode) {
|
||||
@ -579,7 +580,8 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
|
||||
rm = of_iomap(rmnode, 0);
|
||||
if (!rm) {
|
||||
dev_err(prv->dev, "Error mapping reset module node!\n");
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rcwh = in_be32(&rm->rcwhr);
|
||||
@ -628,8 +630,9 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
|
||||
rcw_width * 8, rcw_pagesize,
|
||||
rcw_sparesize);
|
||||
iounmap(rm);
|
||||
out:
|
||||
of_node_put(rmnode);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Free driver resources */
|
||||
@ -660,7 +663,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
|
||||
#endif
|
||||
struct nand_chip *chip;
|
||||
unsigned long regs_paddr, regs_size;
|
||||
const uint *chips_no;
|
||||
const __be32 *chips_no;
|
||||
int resettime = 0;
|
||||
int retval = 0;
|
||||
int rev, len;
|
||||
@ -803,7 +806,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
|
||||
}
|
||||
|
||||
/* Detect NAND chips */
|
||||
if (nand_scan(mtd, *chips_no)) {
|
||||
if (nand_scan(mtd, be32_to_cpup(chips_no))) {
|
||||
dev_err(dev, "NAND Flash not found !\n");
|
||||
devm_free_irq(dev, prv->irq, mtd);
|
||||
retval = -ENXIO;
|
||||
|
@ -45,7 +45,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/leds.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
#include <linux/mtd/partitions.h>
|
||||
@ -59,7 +59,7 @@ static struct nand_ecclayout nand_oob_8 = {
|
||||
{.offset = 3,
|
||||
.length = 2},
|
||||
{.offset = 6,
|
||||
.length = 2}}
|
||||
.length = 2} }
|
||||
};
|
||||
|
||||
static struct nand_ecclayout nand_oob_16 = {
|
||||
@ -67,7 +67,7 @@ static struct nand_ecclayout nand_oob_16 = {
|
||||
.eccpos = {0, 1, 2, 3, 6, 7},
|
||||
.oobfree = {
|
||||
{.offset = 8,
|
||||
. length = 8}}
|
||||
. length = 8} }
|
||||
};
|
||||
|
||||
static struct nand_ecclayout nand_oob_64 = {
|
||||
@ -78,7 +78,7 @@ static struct nand_ecclayout nand_oob_64 = {
|
||||
56, 57, 58, 59, 60, 61, 62, 63},
|
||||
.oobfree = {
|
||||
{.offset = 2,
|
||||
.length = 38}}
|
||||
.length = 38} }
|
||||
};
|
||||
|
||||
static struct nand_ecclayout nand_oob_128 = {
|
||||
@ -92,7 +92,7 @@ static struct nand_ecclayout nand_oob_128 = {
|
||||
120, 121, 122, 123, 124, 125, 126, 127},
|
||||
.oobfree = {
|
||||
{.offset = 2,
|
||||
.length = 78}}
|
||||
.length = 78} }
|
||||
};
|
||||
|
||||
static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
|
||||
@ -612,7 +612,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
|
||||
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
||||
chip->cmd_ctrl(mtd,
|
||||
NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
|
||||
while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
|
||||
;
|
||||
return;
|
||||
|
||||
/* This applies to read commands */
|
||||
@ -718,7 +719,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
||||
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
|
||||
NAND_NCE | NAND_CTRL_CHANGE);
|
||||
while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
|
||||
while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
|
||||
;
|
||||
return;
|
||||
|
||||
case NAND_CMD_RNDOUT:
|
||||
@ -784,7 +786,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
|
||||
spinlock_t *lock = &chip->controller->lock;
|
||||
wait_queue_head_t *wq = &chip->controller->wq;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
retry:
|
||||
retry:
|
||||
spin_lock(lock);
|
||||
|
||||
/* Hardware controller shared among independent devices */
|
||||
@ -834,7 +836,7 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
break;
|
||||
}
|
||||
mdelay(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -980,6 +982,7 @@ out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_unlock);
|
||||
|
||||
/**
|
||||
* nand_lock - [REPLACEABLE] locks all blocks present in the device
|
||||
@ -1049,6 +1052,7 @@ out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_lock);
|
||||
|
||||
/**
|
||||
* nand_read_page_raw - [Intern] read raw page data without ecc
|
||||
@ -1076,8 +1080,9 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
*
|
||||
* We need a special oob layout and handling even when OOB isn't used.
|
||||
*/
|
||||
static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
{
|
||||
int eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
@ -1158,7 +1163,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
* @readlen: data length
|
||||
* @bufpoi: buffer to store read data
|
||||
*/
|
||||
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
|
||||
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
|
||||
{
|
||||
int start_step, end_step, num_steps;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
@ -1166,6 +1172,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
|
||||
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;
|
||||
|
||||
/* Column address wihin the page aligned to ECC size (256bytes). */
|
||||
start_step = data_offs / chip->ecc.size;
|
||||
@ -1204,26 +1211,30 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
|
||||
} else {
|
||||
/* send the command to read the particular ecc bytes */
|
||||
/* take care about buswidth alignment in read_buf */
|
||||
aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
|
||||
index = start_step * chip->ecc.bytes;
|
||||
|
||||
aligned_pos = eccpos[index] & ~(busw - 1);
|
||||
aligned_len = eccfrag_len;
|
||||
if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
|
||||
if (eccpos[index] & (busw - 1))
|
||||
aligned_len++;
|
||||
if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
|
||||
if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
|
||||
aligned_len++;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
|
||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
||||
mtd->writesize + aligned_pos, -1);
|
||||
chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
||||
}
|
||||
|
||||
for (i = 0; i < eccfrag_len; i++)
|
||||
chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
|
||||
chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
|
||||
|
||||
p = bufpoi + data_col_addr;
|
||||
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
|
||||
int stat;
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
|
||||
if (stat == -1)
|
||||
stat = chip->ecc.correct(mtd, p,
|
||||
&chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
|
||||
if (stat < 0)
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
@ -1390,7 +1401,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
|
||||
struct mtd_oob_ops *ops, size_t len)
|
||||
{
|
||||
switch(ops->mode) {
|
||||
switch (ops->mode) {
|
||||
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_RAW:
|
||||
@ -1402,7 +1413,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
|
||||
uint32_t boffs = 0, roffs = ops->ooboffs;
|
||||
size_t bytes = 0;
|
||||
|
||||
for(; free->length && len; free++, len -= bytes) {
|
||||
for (; free->length && len; free++, len -= bytes) {
|
||||
/* Read request not from offset 0 ? */
|
||||
if (unlikely(roffs)) {
|
||||
if (roffs >= free->length) {
|
||||
@ -1466,7 +1477,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
buf = ops->datbuf;
|
||||
oob = ops->oobbuf;
|
||||
|
||||
while(1) {
|
||||
while (1) {
|
||||
bytes = min(mtd->writesize - col, readlen);
|
||||
aligned = (bytes == mtd->writesize);
|
||||
|
||||
@ -1484,7 +1495,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
ret = chip->ecc.read_page_raw(mtd, chip,
|
||||
bufpoi, page);
|
||||
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
|
||||
ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
|
||||
ret = chip->ecc.read_subpage(mtd, chip,
|
||||
col, bytes, bufpoi);
|
||||
else
|
||||
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
||||
page);
|
||||
@ -1493,7 +1505,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
|
||||
/* Transfer not aligned data */
|
||||
if (!aligned) {
|
||||
if (!NAND_SUBPAGE_READ(chip) && !oob)
|
||||
if (!NAND_SUBPAGE_READ(chip) && !oob &&
|
||||
!(mtd->ecc_stats.failed - stats.failed))
|
||||
chip->pagebuf = realpage;
|
||||
memcpy(buf, chip->buffers->databuf + col, bytes);
|
||||
}
|
||||
@ -1791,7 +1804,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
realpage = (int)(from >> chip->page_shift);
|
||||
page = realpage & chip->pagemask;
|
||||
|
||||
while(1) {
|
||||
while (1) {
|
||||
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
|
||||
|
||||
len = min(len, readlen);
|
||||
@ -1861,7 +1874,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
|
||||
nand_get_device(chip, mtd, FL_READING);
|
||||
|
||||
switch(ops->mode) {
|
||||
switch (ops->mode) {
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_AUTO:
|
||||
case MTD_OOB_RAW:
|
||||
@ -1876,7 +1889,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
else
|
||||
ret = nand_do_read_ops(mtd, from, ops);
|
||||
|
||||
out:
|
||||
out:
|
||||
nand_release_device(mtd);
|
||||
return ret;
|
||||
}
|
||||
@ -1905,8 +1918,9 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
*
|
||||
* We need a special oob layout and handling even when ECC isn't checked.
|
||||
*/
|
||||
static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
{
|
||||
int eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
@ -2099,7 +2113,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
switch(ops->mode) {
|
||||
switch (ops->mode) {
|
||||
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_RAW:
|
||||
@ -2111,7 +2125,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
|
||||
uint32_t boffs = 0, woffs = ops->ooboffs;
|
||||
size_t bytes = 0;
|
||||
|
||||
for(; free->length && len; free++, len -= bytes) {
|
||||
for (; free->length && len; free++, len -= bytes) {
|
||||
/* Write request not from offset 0 ? */
|
||||
if (unlikely(woffs)) {
|
||||
if (woffs >= free->length) {
|
||||
@ -2137,7 +2151,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
|
||||
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
|
||||
|
||||
/**
|
||||
* nand_do_write_ops - [Internal] NAND write with ECC
|
||||
@ -2200,10 +2214,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
||||
|
||||
/* Don't allow multipage oob writes with offset */
|
||||
if (ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
|
||||
if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
|
||||
return -EINVAL;
|
||||
|
||||
while(1) {
|
||||
while (1) {
|
||||
int bytes = mtd->writesize;
|
||||
int cached = writelen > bytes && page != blockmask;
|
||||
uint8_t *wbuf = buf;
|
||||
@ -2431,7 +2445,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
|
||||
nand_get_device(chip, mtd, FL_WRITING);
|
||||
|
||||
switch(ops->mode) {
|
||||
switch (ops->mode) {
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_AUTO:
|
||||
case MTD_OOB_RAW:
|
||||
@ -2446,7 +2460,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
else
|
||||
ret = nand_do_write_ops(mtd, to, ops);
|
||||
|
||||
out:
|
||||
out:
|
||||
nand_release_device(mtd);
|
||||
return ret;
|
||||
}
|
||||
@ -2511,7 +2525,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
{
|
||||
int page, status, pages_per_block, ret, chipnr;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
loff_t rewrite_bbt[NAND_MAX_CHIPS]={0};
|
||||
loff_t rewrite_bbt[NAND_MAX_CHIPS] = {0};
|
||||
unsigned int bbt_masked_page = 0xffffffff;
|
||||
loff_t len;
|
||||
|
||||
@ -2632,7 +2646,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
}
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
||||
erase_exit:
|
||||
erase_exit:
|
||||
|
||||
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
|
||||
|
||||
@ -2706,7 +2720,8 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
int ret;
|
||||
|
||||
if ((ret = nand_block_isbad(mtd, ofs))) {
|
||||
ret = nand_block_isbad(mtd, ofs);
|
||||
if (ret) {
|
||||
/* If it was bad already, return success and do nothing. */
|
||||
if (ret > 0)
|
||||
return 0;
|
||||
@ -2786,16 +2801,116 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* sanitize ONFI strings so we can safely print them
|
||||
*/
|
||||
static void sanitize_string(uint8_t *s, size_t len)
|
||||
{
|
||||
ssize_t i;
|
||||
|
||||
/* null terminate */
|
||||
s[len - 1] = 0;
|
||||
|
||||
/* remove non printable chars */
|
||||
for (i = 0; i < len - 1; i++) {
|
||||
if (s[i] < ' ' || s[i] > 127)
|
||||
s[i] = '?';
|
||||
}
|
||||
|
||||
/* remove trailing spaces */
|
||||
strim(s);
|
||||
}
|
||||
|
||||
static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
|
||||
{
|
||||
int i;
|
||||
while (len--) {
|
||||
crc ^= *p++ << 8;
|
||||
for (i = 0; i < 8; i++)
|
||||
crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
|
||||
*/
|
||||
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 val;
|
||||
|
||||
/* try ONFI for unknow chip or LP */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
|
||||
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
|
||||
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
|
||||
return 0;
|
||||
|
||||
printk(KERN_INFO "ONFI flash detected\n");
|
||||
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
|
||||
for (i = 0; i < 3; i++) {
|
||||
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
|
||||
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
|
||||
le16_to_cpu(p->crc)) {
|
||||
printk(KERN_INFO "ONFI param page %d valid\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 3)
|
||||
return 0;
|
||||
|
||||
/* check version */
|
||||
val = le16_to_cpu(p->revision);
|
||||
if (val == 1 || val > (1 << 4)) {
|
||||
printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
|
||||
__func__, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (val & (1 << 4))
|
||||
chip->onfi_version = 22;
|
||||
else if (val & (1 << 3))
|
||||
chip->onfi_version = 21;
|
||||
else if (val & (1 << 2))
|
||||
chip->onfi_version = 20;
|
||||
else
|
||||
chip->onfi_version = 10;
|
||||
|
||||
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);
|
||||
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
|
||||
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
|
||||
busw = 0;
|
||||
if (le16_to_cpu(p->features) & 1)
|
||||
busw = NAND_BUSWIDTH_16;
|
||||
|
||||
chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
||||
chip->options |= (NAND_NO_READRDY |
|
||||
NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the flash and manufacturer id and lookup if the type is supported
|
||||
*/
|
||||
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
int busw, int *maf_id,
|
||||
int busw,
|
||||
int *maf_id, int *dev_id,
|
||||
struct nand_flash_dev *type)
|
||||
{
|
||||
int i, dev_id, maf_idx;
|
||||
int i, maf_idx;
|
||||
u8 id_data[8];
|
||||
int ret;
|
||||
|
||||
/* Select the device */
|
||||
chip->select_chip(mtd, 0);
|
||||
@ -2811,7 +2926,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
|
||||
/* Read manufacturer and device IDs */
|
||||
*maf_id = chip->read_byte(mtd);
|
||||
dev_id = chip->read_byte(mtd);
|
||||
*dev_id = chip->read_byte(mtd);
|
||||
|
||||
/* Try again to make sure, as some systems the bus-hold or other
|
||||
* interface concerns can cause random data which looks like a
|
||||
@ -2821,15 +2936,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read entire ID string */
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
for (i = 0; i < 2; i++)
|
||||
id_data[i] = chip->read_byte(mtd);
|
||||
|
||||
if (id_data[0] != *maf_id || id_data[1] != dev_id) {
|
||||
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
|
||||
printk(KERN_INFO "%s: second ID read did not match "
|
||||
"%02x,%02x against %02x,%02x\n", __func__,
|
||||
*maf_id, dev_id, id_data[0], id_data[1]);
|
||||
*maf_id, *dev_id, id_data[0], id_data[1]);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
@ -2837,8 +2950,23 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
type = nand_flash_ids;
|
||||
|
||||
for (; type->name != NULL; type++)
|
||||
if (dev_id == type->id)
|
||||
break;
|
||||
if (*dev_id == type->id)
|
||||
break;
|
||||
|
||||
chip->onfi_version = 0;
|
||||
if (!type->name || !type->pagesize) {
|
||||
/* Check is chip is ONFI compliant */
|
||||
ret = nand_flash_detect_onfi(mtd, chip, busw);
|
||||
if (ret)
|
||||
goto ident_done;
|
||||
}
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read entire ID string */
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
id_data[i] = chip->read_byte(mtd);
|
||||
|
||||
if (!type->name)
|
||||
return ERR_PTR(-ENODEV);
|
||||
@ -2848,8 +2976,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
|
||||
/* Newer devices have all the information in additional id bytes */
|
||||
if (!type->pagesize) {
|
||||
if (!type->pagesize && chip->init_size) {
|
||||
/* set the pagesize, oobsize, erasesize by the driver*/
|
||||
busw = chip->init_size(mtd, chip, id_data);
|
||||
} else if (!type->pagesize) {
|
||||
int extid;
|
||||
/* The 3rd id byte holds MLC / multichip data */
|
||||
chip->cellinfo = id_data[2];
|
||||
@ -2859,7 +2989,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
/*
|
||||
* Field definitions are in the following datasheets:
|
||||
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
|
||||
* New style (6 byte ID): Samsung K9GAG08U0D (p.40)
|
||||
* New style (6 byte ID): Samsung K9GBG08U0M (p.40)
|
||||
*
|
||||
* Check for wraparound + Samsung ID + nonzero 6th byte
|
||||
* to decide what to do.
|
||||
@ -2872,7 +3002,20 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
mtd->writesize = 2048 << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Calc oobsize */
|
||||
mtd->oobsize = (extid & 0x03) == 0x01 ? 128 : 218;
|
||||
switch (extid & 0x03) {
|
||||
case 1:
|
||||
mtd->oobsize = 128;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 218;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 400;
|
||||
break;
|
||||
default:
|
||||
mtd->oobsize = 436;
|
||||
break;
|
||||
}
|
||||
extid >>= 2;
|
||||
/* Calc blocksize */
|
||||
mtd->erasesize = (128 * 1024) <<
|
||||
@ -2900,7 +3043,35 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
mtd->writesize = type->pagesize;
|
||||
mtd->oobsize = mtd->writesize / 32;
|
||||
busw = type->options & NAND_BUSWIDTH_16;
|
||||
|
||||
/*
|
||||
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
|
||||
* some Spansion chips have erasesize that conflicts with size
|
||||
* listed in nand_ids table
|
||||
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
|
||||
*/
|
||||
if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
|
||||
id_data[5] == 0x00 && id_data[6] == 0x00 &&
|
||||
id_data[7] == 0x00 && mtd->writesize == 512) {
|
||||
mtd->erasesize = 128 * 1024;
|
||||
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
|
||||
}
|
||||
}
|
||||
/* Get chip options, preserve non chip based options */
|
||||
chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
||||
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
|
||||
|
||||
/* Check if chip is a not a samsung device. Do not clear the
|
||||
* options for chips which are not having an extended id.
|
||||
*/
|
||||
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
|
||||
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
|
||||
ident_done:
|
||||
|
||||
/*
|
||||
* Set chip as a default. Board drivers can override it, if necessary
|
||||
*/
|
||||
chip->options |= NAND_NO_AUTOINCR;
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
|
||||
@ -2915,7 +3086,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
if (busw != (chip->options & NAND_BUSWIDTH_16)) {
|
||||
printk(KERN_INFO "NAND device: Manufacturer ID:"
|
||||
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
|
||||
dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
|
||||
*dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
|
||||
printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
|
||||
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
||||
busw ? 16 : 8);
|
||||
@ -2931,8 +3102,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
ffs(mtd->erasesize) - 1;
|
||||
if (chip->chipsize & 0xffffffff)
|
||||
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
|
||||
else
|
||||
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1;
|
||||
else {
|
||||
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
|
||||
chip->chip_shift += 32 - 1;
|
||||
}
|
||||
|
||||
/* Set the bad block position */
|
||||
if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16))
|
||||
@ -2940,27 +3113,12 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
else
|
||||
chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
|
||||
|
||||
/* Get chip options, preserve non chip based options */
|
||||
chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
||||
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
|
||||
|
||||
/*
|
||||
* Set chip as a default. Board drivers can override it, if necessary
|
||||
*/
|
||||
chip->options |= NAND_NO_AUTOINCR;
|
||||
|
||||
/* Check if chip is a not a samsung device. Do not clear the
|
||||
* options for chips which are not having an extended id.
|
||||
*/
|
||||
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
|
||||
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
|
||||
|
||||
/*
|
||||
* Bad block marker is stored in the last page of each block
|
||||
* on Samsung and Hynix MLC devices; stored in first two pages
|
||||
* of each block on Micron devices with 2KiB pages and on
|
||||
* SLC Samsung, Hynix, and AMD/Spansion. All others scan only
|
||||
* the first page.
|
||||
* SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan
|
||||
* only the first page.
|
||||
*/
|
||||
if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
|
||||
(*maf_id == NAND_MFR_SAMSUNG ||
|
||||
@ -2969,6 +3127,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
|
||||
(*maf_id == NAND_MFR_SAMSUNG ||
|
||||
*maf_id == NAND_MFR_HYNIX ||
|
||||
*maf_id == NAND_MFR_TOSHIBA ||
|
||||
*maf_id == NAND_MFR_AMD)) ||
|
||||
(mtd->writesize == 2048 &&
|
||||
*maf_id == NAND_MFR_MICRON))
|
||||
@ -2994,9 +3153,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
||||
chip->cmdfunc = nand_command_lp;
|
||||
|
||||
/* TODO onfi flash name */
|
||||
printk(KERN_INFO "NAND device: Manufacturer ID:"
|
||||
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
|
||||
nand_manuf_ids[maf_idx].name, type->name);
|
||||
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
|
||||
nand_manuf_ids[maf_idx].name,
|
||||
chip->onfi_version ? type->name : chip->onfi_params.model);
|
||||
|
||||
return type;
|
||||
}
|
||||
@ -3015,7 +3176,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
struct nand_flash_dev *table)
|
||||
{
|
||||
int i, busw, nand_maf_id;
|
||||
int i, busw, nand_maf_id, nand_dev_id;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct nand_flash_dev *type;
|
||||
|
||||
@ -3025,7 +3186,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
nand_set_defaults(chip, busw);
|
||||
|
||||
/* Read the flash type */
|
||||
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table);
|
||||
type = nand_get_flash_type(mtd, chip, busw,
|
||||
&nand_maf_id, &nand_dev_id, table);
|
||||
|
||||
if (IS_ERR(type)) {
|
||||
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
|
||||
@ -3043,7 +3205,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
/* Read manufacturer and device IDs */
|
||||
if (nand_maf_id != chip->read_byte(mtd) ||
|
||||
type->id != chip->read_byte(mtd))
|
||||
nand_dev_id != chip->read_byte(mtd))
|
||||
break;
|
||||
}
|
||||
if (i > 1)
|
||||
@ -3055,6 +3217,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan_ident);
|
||||
|
||||
|
||||
/**
|
||||
@ -3219,7 +3382,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
* mode
|
||||
*/
|
||||
chip->ecc.steps = mtd->writesize / chip->ecc.size;
|
||||
if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
|
||||
if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
|
||||
printk(KERN_WARNING "Invalid ecc parameters\n");
|
||||
BUG();
|
||||
}
|
||||
@ -3231,7 +3394,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
*/
|
||||
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
|
||||
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
|
||||
switch(chip->ecc.steps) {
|
||||
switch (chip->ecc.steps) {
|
||||
case 2:
|
||||
mtd->subpage_sft = 1;
|
||||
break;
|
||||
@ -3283,10 +3446,11 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
/* Build bad block table */
|
||||
return chip->scan_bbt(mtd);
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan_tail);
|
||||
|
||||
/* is_module_text_address() isn't exported, and it's mostly a pointless
|
||||
test if this is a module _anyway_ -- they'd have to try _really_ hard
|
||||
to call us from in-kernel code if the core NAND support is modular. */
|
||||
* test if this is a module _anyway_ -- they'd have to try _really_ hard
|
||||
* to call us from in-kernel code if the core NAND support is modular. */
|
||||
#ifdef MODULE
|
||||
#define caller_is_module() (1)
|
||||
#else
|
||||
@ -3322,6 +3486,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
|
||||
ret = nand_scan_tail(mtd);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan);
|
||||
|
||||
/**
|
||||
* nand_release - [NAND Interface] Free resources held by the NAND device
|
||||
@ -3348,12 +3513,6 @@ void nand_release(struct mtd_info *mtd)
|
||||
& NAND_BBT_DYNAMICSTRUCT)
|
||||
kfree(chip->badblock_pattern);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(nand_lock);
|
||||
EXPORT_SYMBOL_GPL(nand_unlock);
|
||||
EXPORT_SYMBOL_GPL(nand_scan);
|
||||
EXPORT_SYMBOL_GPL(nand_scan_ident);
|
||||
EXPORT_SYMBOL_GPL(nand_scan_tail);
|
||||
EXPORT_SYMBOL_GPL(nand_release);
|
||||
|
||||
static int __init nand_base_init(void)
|
||||
@ -3371,5 +3530,6 @@ module_init(nand_base_init);
|
||||
module_exit(nand_base_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION("Generic NAND flash driver code");
|
||||
|
@ -13,28 +13,37 @@
|
||||
* Description:
|
||||
*
|
||||
* When nand_scan_bbt is called, then it tries to find the bad block table
|
||||
* depending on the options in the bbt descriptor(s). If a bbt is found
|
||||
* then the contents are read and the memory based bbt is created. If a
|
||||
* mirrored bbt is selected then the mirror is searched too and the
|
||||
* versions are compared. If the mirror has a greater version number
|
||||
* than the mirror bbt is used to build the memory based bbt.
|
||||
* depending on the options in the BBT descriptor(s). If no flash based BBT
|
||||
* (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory
|
||||
* marked good / bad blocks. This information is used to create a memory BBT.
|
||||
* Once a new bad block is discovered then the "factory" information is updated
|
||||
* on the device.
|
||||
* If a flash based BBT is specified then the function first tries to find the
|
||||
* BBT on flash. If a BBT is found then the contents are read and the memory
|
||||
* based BBT is created. If a mirrored BBT is selected then the mirror is
|
||||
* searched too and the versions are compared. If the mirror has a greater
|
||||
* version number than the mirror BBT is used to build the memory based BBT.
|
||||
* If the tables are not versioned, then we "or" the bad block information.
|
||||
* If one of the bbt's is out of date or does not exist it is (re)created.
|
||||
* If no bbt exists at all then the device is scanned for factory marked
|
||||
* If one of the BBTs is out of date or does not exist it is (re)created.
|
||||
* If no BBT exists at all then the device is scanned for factory marked
|
||||
* good / bad blocks and the bad block tables are created.
|
||||
*
|
||||
* For manufacturer created bbts like the one found on M-SYS DOC devices
|
||||
* the bbt is searched and read but never created
|
||||
* For manufacturer created BBTs like the one found on M-SYS DOC devices
|
||||
* the BBT is searched and read but never created
|
||||
*
|
||||
* The autogenerated bad block table is located in the last good blocks
|
||||
* The auto generated bad block table is located in the last good blocks
|
||||
* of the device. The table is mirrored, so it can be updated eventually.
|
||||
* The table is marked in the oob area with an ident pattern and a version
|
||||
* number which indicates which of both tables is more up to date.
|
||||
* The table is marked in the OOB area with an ident pattern and a version
|
||||
* number which indicates which of both tables is more up to date. If the NAND
|
||||
* controller needs the complete OOB area for the ECC information then the
|
||||
* option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern
|
||||
* and the version byte into the data area and the OOB area will remain
|
||||
* untouched.
|
||||
*
|
||||
* The table uses 2 bits per block
|
||||
* 11b: block is good
|
||||
* 00b: block is factory marked bad
|
||||
* 01b, 10b: block is marked bad due to wear
|
||||
* 11b: block is good
|
||||
* 00b: block is factory marked bad
|
||||
* 01b, 10b: block is marked bad due to wear
|
||||
*
|
||||
* The memory bad block table uses the following scheme:
|
||||
* 00b: block is good
|
||||
@ -59,6 +68,16 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = memcmp(buf, td->pattern, td->len);
|
||||
if (!ret)
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_pattern - [GENERIC] check if a pattern is in the buffer
|
||||
* @buf: the buffer to search
|
||||
@ -77,6 +96,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
|
||||
int i, end = 0;
|
||||
uint8_t *p = buf;
|
||||
|
||||
if (td->options & NAND_BBT_NO_OOB)
|
||||
return check_pattern_no_oob(buf, td);
|
||||
|
||||
end = paglen + td->offs;
|
||||
if (td->options & NAND_BBT_SCANEMPTY) {
|
||||
for (i = 0; i < end; i++) {
|
||||
@ -155,33 +177,64 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_marker_len - compute the length of the marker in data area
|
||||
* @td: BBT descriptor used for computation
|
||||
*
|
||||
* The length will be 0 if the markeris located in OOB area.
|
||||
*/
|
||||
static u32 add_marker_len(struct nand_bbt_descr *td)
|
||||
{
|
||||
u32 len;
|
||||
|
||||
if (!(td->options & NAND_BBT_NO_OOB))
|
||||
return 0;
|
||||
|
||||
len = td->len;
|
||||
if (td->options & NAND_BBT_VERSION)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_bbt - [GENERIC] Read the bad block table starting from page
|
||||
* @mtd: MTD device structure
|
||||
* @buf: temporary buffer
|
||||
* @page: the starting page
|
||||
* @num: the number of bbt descriptors to read
|
||||
* @bits: number of bits per block
|
||||
* @td: the bbt describtion table
|
||||
* @offs: offset in the memory table
|
||||
* @reserved_block_code: Pattern to identify reserved blocks
|
||||
*
|
||||
* Read the bad block table starting from page.
|
||||
*
|
||||
*/
|
||||
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||
int bits, int offs, int reserved_block_code)
|
||||
struct nand_bbt_descr *td, int offs)
|
||||
{
|
||||
int res, i, j, act = 0;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
size_t retlen, len, totlen;
|
||||
loff_t from;
|
||||
int bits = td->options & NAND_BBT_NRBITS_MSK;
|
||||
uint8_t msk = (uint8_t) ((1 << bits) - 1);
|
||||
u32 marker_len;
|
||||
int reserved_block_code = td->reserved_block_code;
|
||||
|
||||
totlen = (num * bits) >> 3;
|
||||
marker_len = add_marker_len(td);
|
||||
from = ((loff_t) page) << this->page_shift;
|
||||
|
||||
while (totlen) {
|
||||
len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
|
||||
if (marker_len) {
|
||||
/*
|
||||
* In case the BBT marker is not in the OOB area it
|
||||
* will be just in the first page.
|
||||
*/
|
||||
len -= marker_len;
|
||||
from += marker_len;
|
||||
marker_len = 0;
|
||||
}
|
||||
res = mtd->read(mtd, from, len, &retlen, buf);
|
||||
if (res < 0) {
|
||||
if (retlen != len) {
|
||||
@ -238,30 +291,47 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int res = 0, i;
|
||||
int bits;
|
||||
|
||||
bits = td->options & NAND_BBT_NRBITS_MSK;
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
int offs = 0;
|
||||
for (i = 0; i < this->numchips; i++) {
|
||||
if (chip == -1 || chip == i)
|
||||
res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
|
||||
res = read_bbt(mtd, buf, td->pages[i],
|
||||
this->chipsize >> this->bbt_erase_shift,
|
||||
td, offs);
|
||||
if (res)
|
||||
return res;
|
||||
offs += this->chipsize >> (this->bbt_erase_shift + 2);
|
||||
}
|
||||
} else {
|
||||
res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
|
||||
res = read_bbt(mtd, buf, td->pages[0],
|
||||
mtd->size >> this->bbt_erase_shift, td, 0);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* BBT marker is in the first page, no OOB.
|
||||
*/
|
||||
static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
||||
struct nand_bbt_descr *td)
|
||||
{
|
||||
size_t retlen;
|
||||
size_t len;
|
||||
|
||||
len = td->len;
|
||||
if (td->options & NAND_BBT_VERSION)
|
||||
len++;
|
||||
|
||||
return mtd->read(mtd, offs, len, &retlen, buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan read raw data from flash
|
||||
*/
|
||||
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
||||
static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
||||
size_t len)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
@ -294,6 +364,15 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
||||
size_t len, struct nand_bbt_descr *td)
|
||||
{
|
||||
if (td->options & NAND_BBT_NO_OOB)
|
||||
return scan_read_raw_data(mtd, buf, offs, td);
|
||||
else
|
||||
return scan_read_raw_oob(mtd, buf, offs, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan write data with oob to flash
|
||||
*/
|
||||
@ -312,6 +391,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
return mtd->write_oob(mtd, offs, &ops);
|
||||
}
|
||||
|
||||
static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
{
|
||||
u32 ver_offs = td->veroffs;
|
||||
|
||||
if (!(td->options & NAND_BBT_NO_OOB))
|
||||
ver_offs += mtd->writesize;
|
||||
return ver_offs;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
|
||||
* @mtd: MTD device structure
|
||||
@ -331,8 +419,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
|
||||
/* Read the primary version, if available */
|
||||
if (td->options & NAND_BBT_VERSION) {
|
||||
scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
|
||||
mtd->writesize);
|
||||
td->version[0] = buf[mtd->writesize + td->veroffs];
|
||||
mtd->writesize, td);
|
||||
td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
|
||||
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
|
||||
td->pages[0], td->version[0]);
|
||||
}
|
||||
@ -340,8 +428,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
|
||||
/* Read the mirror version, if available */
|
||||
if (md && (md->options & NAND_BBT_VERSION)) {
|
||||
scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
|
||||
mtd->writesize);
|
||||
md->version[0] = buf[mtd->writesize + md->veroffs];
|
||||
mtd->writesize, td);
|
||||
md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
|
||||
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
|
||||
md->pages[0], md->version[0]);
|
||||
}
|
||||
@ -357,7 +445,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
|
||||
{
|
||||
int ret, j;
|
||||
|
||||
ret = scan_read_raw(mtd, buf, offs, readlen);
|
||||
ret = scan_read_raw_oob(mtd, buf, offs, readlen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -464,6 +552,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
for (i = startblock; i < numblocks;) {
|
||||
int ret;
|
||||
|
||||
BUG_ON(bd->options & NAND_BBT_NO_OOB);
|
||||
|
||||
if (bd->options & NAND_BBT_SCANALLPAGES)
|
||||
ret = scan_block_full(mtd, bd, from, buf, readlen,
|
||||
scanlen, len);
|
||||
@ -545,11 +635,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
|
||||
|
||||
/* Read first page */
|
||||
scan_read_raw(mtd, buf, offs, mtd->writesize);
|
||||
scan_read_raw(mtd, buf, offs, mtd->writesize, td);
|
||||
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
|
||||
td->pages[i] = actblock << blocktopage;
|
||||
if (td->options & NAND_BBT_VERSION) {
|
||||
td->version[i] = buf[mtd->writesize + td->veroffs];
|
||||
offs = bbt_get_ver_offs(mtd, td);
|
||||
td->version[i] = buf[offs];
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -733,12 +824,26 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
|
||||
ooboffs = len + (pageoffs * mtd->oobsize);
|
||||
|
||||
} else if (td->options & NAND_BBT_NO_OOB) {
|
||||
ooboffs = 0;
|
||||
offs = td->len;
|
||||
/* the version byte */
|
||||
if (td->options & NAND_BBT_VERSION)
|
||||
offs++;
|
||||
/* Calc length */
|
||||
len = (size_t) (numblocks >> sft);
|
||||
len += offs;
|
||||
/* Make it page aligned ! */
|
||||
len = ALIGN(len, mtd->writesize);
|
||||
/* Preset the buffer with 0xff */
|
||||
memset(buf, 0xff, len);
|
||||
/* Pattern is located at the begin of first page */
|
||||
memcpy(buf, td->pattern, td->len);
|
||||
} else {
|
||||
/* Calc length */
|
||||
len = (size_t) (numblocks >> sft);
|
||||
/* Make it page aligned ! */
|
||||
len = (len + (mtd->writesize - 1)) &
|
||||
~(mtd->writesize - 1);
|
||||
len = ALIGN(len, mtd->writesize);
|
||||
/* Preset the buffer with 0xff */
|
||||
memset(buf, 0xff, len +
|
||||
(len >> this->page_shift)* mtd->oobsize);
|
||||
@ -772,7 +877,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
if (res < 0)
|
||||
goto outerr;
|
||||
|
||||
res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
|
||||
res = scan_write_bbt(mtd, to, len, buf,
|
||||
td->options & NAND_BBT_NO_OOB ? NULL :
|
||||
&buf[len]);
|
||||
if (res < 0)
|
||||
goto outerr;
|
||||
|
||||
@ -892,7 +999,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
||||
continue;
|
||||
|
||||
/* Create the table in memory by scanning the chip(s) */
|
||||
create_bbt(mtd, buf, bd, chipsel);
|
||||
if (!(this->options & NAND_CREATE_EMPTY_BBT))
|
||||
create_bbt(mtd, buf, bd, chipsel);
|
||||
|
||||
td->version[i] = 1;
|
||||
if (md)
|
||||
@ -982,6 +1090,49 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* verify_bbt_descr - verify the bad block description
|
||||
* @bd: the table to verify
|
||||
*
|
||||
* This functions performs a few sanity checks on the bad block description
|
||||
* table.
|
||||
*/
|
||||
static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
u32 pattern_len = bd->len;
|
||||
u32 bits = bd->options & NAND_BBT_NRBITS_MSK;
|
||||
u32 table_size;
|
||||
|
||||
if (!bd)
|
||||
return;
|
||||
BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
|
||||
!(this->options & NAND_USE_FLASH_BBT));
|
||||
BUG_ON(!bits);
|
||||
|
||||
if (bd->options & NAND_BBT_VERSION)
|
||||
pattern_len++;
|
||||
|
||||
if (bd->options & NAND_BBT_NO_OOB) {
|
||||
BUG_ON(!(this->options & NAND_USE_FLASH_BBT));
|
||||
BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB));
|
||||
BUG_ON(bd->offs);
|
||||
if (bd->options & NAND_BBT_VERSION)
|
||||
BUG_ON(bd->veroffs != bd->len);
|
||||
BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
|
||||
}
|
||||
|
||||
if (bd->options & NAND_BBT_PERCHIP)
|
||||
table_size = this->chipsize >> this->bbt_erase_shift;
|
||||
else
|
||||
table_size = mtd->size >> this->bbt_erase_shift;
|
||||
table_size >>= 3;
|
||||
table_size *= bits;
|
||||
if (bd->options & NAND_BBT_NO_OOB)
|
||||
table_size += pattern_len;
|
||||
BUG_ON(table_size > (1 << this->bbt_erase_shift));
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
|
||||
* @mtd: MTD device structure
|
||||
@ -1023,6 +1174,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
verify_bbt_descr(mtd, td);
|
||||
verify_bbt_descr(mtd, md);
|
||||
|
||||
/* Allocate a temporary buffer for one eraseblock incl. oob */
|
||||
len = (1 << this->bbt_erase_shift);
|
||||
@ -1166,6 +1319,26 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
||||
.pattern = mirror_pattern
|
||||
};
|
||||
|
||||
static struct nand_bbt_descr bbt_main_no_bbt_descr = {
|
||||
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
|
||||
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
|
||||
| NAND_BBT_NO_OOB,
|
||||
.len = 4,
|
||||
.veroffs = 4,
|
||||
.maxblocks = 4,
|
||||
.pattern = bbt_pattern
|
||||
};
|
||||
|
||||
static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
|
||||
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
|
||||
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
|
||||
| NAND_BBT_NO_OOB,
|
||||
.len = 4,
|
||||
.veroffs = 4,
|
||||
.maxblocks = 4,
|
||||
.pattern = mirror_pattern
|
||||
};
|
||||
|
||||
#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \
|
||||
NAND_BBT_SCANBYTE1AND6)
|
||||
/**
|
||||
@ -1236,8 +1409,13 @@ int nand_default_bbt(struct mtd_info *mtd)
|
||||
if (this->options & NAND_USE_FLASH_BBT) {
|
||||
/* Use the default pattern descriptors */
|
||||
if (!this->bbt_td) {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
if (this->options & NAND_USE_FLASH_BBT_NO_OOB) {
|
||||
this->bbt_td = &bbt_main_no_bbt_descr;
|
||||
this->bbt_md = &bbt_mirror_no_bbt_descr;
|
||||
} else {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
}
|
||||
if (!this->badblock_pattern) {
|
||||
this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
|
||||
|
@ -75,9 +75,13 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||
|
||||
/*512 Megabit */
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16},
|
||||
|
||||
/* 1 Gigabit */
|
||||
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
|
||||
@ -112,7 +116,34 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
|
||||
|
||||
/* 32 Gigabit */
|
||||
{"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS},
|
||||
{"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS},
|
||||
{"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16},
|
||||
{"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16},
|
||||
|
||||
/* 64 Gigabit */
|
||||
{"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS},
|
||||
{"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS},
|
||||
{"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16},
|
||||
{"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16},
|
||||
|
||||
/* 128 Gigabit */
|
||||
{"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS},
|
||||
{"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS},
|
||||
{"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16},
|
||||
{"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16},
|
||||
|
||||
/* 256 Gigabit */
|
||||
{"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS},
|
||||
{"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS},
|
||||
{"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16},
|
||||
{"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16},
|
||||
|
||||
/* 512 Gigabit */
|
||||
{"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS},
|
||||
{"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS},
|
||||
{"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16},
|
||||
{"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16},
|
||||
|
||||
/*
|
||||
* Renesas AND 1 Gigabit. Those chips do not support extended id and
|
||||
|
@ -107,6 +107,7 @@ static char *gravepages = NULL;
|
||||
static unsigned int rptwear = 0;
|
||||
static unsigned int overridesize = 0;
|
||||
static char *cache_file = NULL;
|
||||
static unsigned int bbt;
|
||||
|
||||
module_param(first_id_byte, uint, 0400);
|
||||
module_param(second_id_byte, uint, 0400);
|
||||
@ -130,6 +131,7 @@ module_param(gravepages, charp, 0400);
|
||||
module_param(rptwear, uint, 0400);
|
||||
module_param(overridesize, uint, 0400);
|
||||
module_param(cache_file, charp, 0400);
|
||||
module_param(bbt, uint, 0400);
|
||||
|
||||
MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
|
||||
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
|
||||
@ -162,6 +164,7 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I
|
||||
"The size is specified in erase blocks and as the exponent of a power of two"
|
||||
" e.g. 5 means a size of 32 erase blocks");
|
||||
MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
|
||||
MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
|
||||
|
||||
/* The largest possible page size */
|
||||
#define NS_LARGEST_PAGE_SIZE 4096
|
||||
@ -2264,6 +2267,18 @@ static int __init ns_init_module(void)
|
||||
/* and 'badblocks' parameters to work */
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
switch (bbt) {
|
||||
case 2:
|
||||
chip->options |= NAND_USE_FLASH_BBT_NO_OOB;
|
||||
case 1:
|
||||
chip->options |= NAND_USE_FLASH_BBT;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
NS_ERR("bbt has to be 0..2\n");
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
* Perform minimum nandsim structure initialization to handle
|
||||
* the initial ID read command correctly
|
||||
@ -2321,10 +2336,10 @@ static int __init ns_init_module(void)
|
||||
if ((retval = init_nandsim(nsmtd)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
if ((retval = parse_badblocks(nand, nsmtd)) != 0)
|
||||
if ((retval = nand_default_bbt(nsmtd)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
if ((retval = nand_default_bbt(nsmtd)) != 0)
|
||||
if ((retval = parse_badblocks(nand, nsmtd)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
/* Register NAND partitions */
|
||||
|
@ -229,7 +229,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
const u32 *reg;
|
||||
const __be32 *reg;
|
||||
u32 ccr;
|
||||
int err, len;
|
||||
|
||||
@ -244,7 +244,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
|
||||
dev_err(&ofdev->dev, "unable read reg property (%d)\n", len);
|
||||
return -ENOENT;
|
||||
}
|
||||
ndfc->chip_select = reg[0];
|
||||
ndfc->chip_select = be32_to_cpu(reg[0]);
|
||||
|
||||
ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0);
|
||||
if (!ndfc->ndfcbase) {
|
||||
@ -257,7 +257,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
|
||||
/* It is ok if ccr does not exist - just default to 0 */
|
||||
reg = of_get_property(ofdev->dev.of_node, "ccr", NULL);
|
||||
if (reg)
|
||||
ccr |= *reg;
|
||||
ccr |= be32_to_cpup(reg);
|
||||
|
||||
out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
|
||||
|
||||
@ -265,7 +265,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
|
||||
reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL);
|
||||
if (reg) {
|
||||
int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
|
||||
out_be32(ndfc->ndfcbase + offset, *reg);
|
||||
out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg));
|
||||
}
|
||||
|
||||
err = ndfc_chip_init(ndfc, ofdev->dev.of_node);
|
||||
|
@ -111,11 +111,11 @@ static int use_dma = 1;
|
||||
module_param(use_dma, bool, 0);
|
||||
MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
|
||||
#else
|
||||
const int use_dma;
|
||||
static const int use_dma;
|
||||
#endif
|
||||
#else
|
||||
const int use_prefetch;
|
||||
const int use_dma;
|
||||
static const int use_dma;
|
||||
#endif
|
||||
|
||||
struct omap_nand_info {
|
||||
|
@ -117,7 +117,7 @@ struct pxa3xx_nand_info {
|
||||
struct nand_chip nand_chip;
|
||||
|
||||
struct platform_device *pdev;
|
||||
const struct pxa3xx_nand_flash *flash_info;
|
||||
struct pxa3xx_nand_cmdset *cmdset;
|
||||
|
||||
struct clk *clk;
|
||||
void __iomem *mmio_base;
|
||||
@ -131,6 +131,7 @@ struct pxa3xx_nand_info {
|
||||
int drcmr_cmd;
|
||||
|
||||
unsigned char *data_buff;
|
||||
unsigned char *oob_buff;
|
||||
dma_addr_t data_buff_phys;
|
||||
size_t data_buff_size;
|
||||
int data_dma_ch;
|
||||
@ -149,7 +150,8 @@ struct pxa3xx_nand_info {
|
||||
int use_ecc; /* use HW ECC ? */
|
||||
int use_dma; /* use DMA ? */
|
||||
|
||||
size_t data_size; /* data size in FIFO */
|
||||
unsigned int page_size; /* page size of attached chip */
|
||||
unsigned int data_size; /* data size in FIFO */
|
||||
int retcode;
|
||||
struct completion cmd_complete;
|
||||
|
||||
@ -158,6 +160,10 @@ struct pxa3xx_nand_info {
|
||||
uint32_t ndcb1;
|
||||
uint32_t ndcb2;
|
||||
|
||||
/* timing calcuted from setting */
|
||||
uint32_t ndtr0cs0;
|
||||
uint32_t ndtr1cs0;
|
||||
|
||||
/* calculated from pxa3xx_nand_flash data */
|
||||
size_t oob_size;
|
||||
size_t read_id_bytes;
|
||||
@ -174,23 +180,7 @@ MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
|
||||
* Default NAND flash controller configuration setup by the
|
||||
* bootloader. This configuration is used only when pdata->keep_config is set
|
||||
*/
|
||||
static struct pxa3xx_nand_timing default_timing;
|
||||
static struct pxa3xx_nand_flash default_flash;
|
||||
|
||||
static struct pxa3xx_nand_cmdset smallpage_cmdset = {
|
||||
.read1 = 0x0000,
|
||||
.read2 = 0x0050,
|
||||
.program = 0x1080,
|
||||
.read_status = 0x0070,
|
||||
.read_id = 0x0090,
|
||||
.erase = 0xD060,
|
||||
.reset = 0x00FF,
|
||||
.lock = 0x002A,
|
||||
.unlock = 0x2423,
|
||||
.lock_status = 0x007A,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_cmdset largepage_cmdset = {
|
||||
static struct pxa3xx_nand_cmdset default_cmdset = {
|
||||
.read1 = 0x3000,
|
||||
.read2 = 0x0050,
|
||||
.program = 0x1080,
|
||||
@ -203,142 +193,27 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = {
|
||||
.lock_status = 0x007A,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
|
||||
static struct pxa3xx_nand_timing samsung512MbX16_timing = {
|
||||
.tCH = 10,
|
||||
.tCS = 0,
|
||||
.tWH = 20,
|
||||
.tWP = 40,
|
||||
.tRH = 30,
|
||||
.tRP = 40,
|
||||
.tR = 11123,
|
||||
.tWHR = 110,
|
||||
.tAR = 10,
|
||||
static struct pxa3xx_nand_timing timing[] = {
|
||||
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
||||
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
||||
{ 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
|
||||
{ 10, 35, 15, 25, 15, 25, 25000, 60, 10, },
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash samsung512MbX16 = {
|
||||
.timing = &samsung512MbX16_timing,
|
||||
.cmdset = &smallpage_cmdset,
|
||||
.page_per_block = 32,
|
||||
.page_size = 512,
|
||||
.flash_width = 16,
|
||||
.dfc_width = 16,
|
||||
.num_blocks = 4096,
|
||||
.chip_id = 0x46ec,
|
||||
static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
||||
{ 0, 0, 2048, 8, 8, 0, &default_cmdset, &timing[0] },
|
||||
{ 0x46ec, 32, 512, 16, 16, 4096, &default_cmdset, &timing[1] },
|
||||
{ 0xdaec, 64, 2048, 8, 8, 2048, &default_cmdset, &timing[1] },
|
||||
{ 0xd7ec, 128, 4096, 8, 8, 8192, &default_cmdset, &timing[1] },
|
||||
{ 0xa12c, 64, 2048, 8, 8, 1024, &default_cmdset, &timing[2] },
|
||||
{ 0xb12c, 64, 2048, 16, 16, 1024, &default_cmdset, &timing[2] },
|
||||
{ 0xdc2c, 64, 2048, 8, 8, 4096, &default_cmdset, &timing[2] },
|
||||
{ 0xcc2c, 64, 2048, 16, 16, 4096, &default_cmdset, &timing[2] },
|
||||
{ 0xba20, 64, 2048, 16, 16, 2048, &default_cmdset, &timing[3] },
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash samsung2GbX8 = {
|
||||
.timing = &samsung512MbX16_timing,
|
||||
.cmdset = &smallpage_cmdset,
|
||||
.page_per_block = 64,
|
||||
.page_size = 2048,
|
||||
.flash_width = 8,
|
||||
.dfc_width = 8,
|
||||
.num_blocks = 2048,
|
||||
.chip_id = 0xdaec,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash samsung32GbX8 = {
|
||||
.timing = &samsung512MbX16_timing,
|
||||
.cmdset = &smallpage_cmdset,
|
||||
.page_per_block = 128,
|
||||
.page_size = 4096,
|
||||
.flash_width = 8,
|
||||
.dfc_width = 8,
|
||||
.num_blocks = 8192,
|
||||
.chip_id = 0xd7ec,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_timing micron_timing = {
|
||||
.tCH = 10,
|
||||
.tCS = 25,
|
||||
.tWH = 15,
|
||||
.tWP = 25,
|
||||
.tRH = 15,
|
||||
.tRP = 30,
|
||||
.tR = 25000,
|
||||
.tWHR = 60,
|
||||
.tAR = 10,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash micron1GbX8 = {
|
||||
.timing = µn_timing,
|
||||
.cmdset = &largepage_cmdset,
|
||||
.page_per_block = 64,
|
||||
.page_size = 2048,
|
||||
.flash_width = 8,
|
||||
.dfc_width = 8,
|
||||
.num_blocks = 1024,
|
||||
.chip_id = 0xa12c,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash micron1GbX16 = {
|
||||
.timing = µn_timing,
|
||||
.cmdset = &largepage_cmdset,
|
||||
.page_per_block = 64,
|
||||
.page_size = 2048,
|
||||
.flash_width = 16,
|
||||
.dfc_width = 16,
|
||||
.num_blocks = 1024,
|
||||
.chip_id = 0xb12c,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash micron4GbX8 = {
|
||||
.timing = µn_timing,
|
||||
.cmdset = &largepage_cmdset,
|
||||
.page_per_block = 64,
|
||||
.page_size = 2048,
|
||||
.flash_width = 8,
|
||||
.dfc_width = 8,
|
||||
.num_blocks = 4096,
|
||||
.chip_id = 0xdc2c,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash micron4GbX16 = {
|
||||
.timing = µn_timing,
|
||||
.cmdset = &largepage_cmdset,
|
||||
.page_per_block = 64,
|
||||
.page_size = 2048,
|
||||
.flash_width = 16,
|
||||
.dfc_width = 16,
|
||||
.num_blocks = 4096,
|
||||
.chip_id = 0xcc2c,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_timing stm2GbX16_timing = {
|
||||
.tCH = 10,
|
||||
.tCS = 35,
|
||||
.tWH = 15,
|
||||
.tWP = 25,
|
||||
.tRH = 15,
|
||||
.tRP = 25,
|
||||
.tR = 25000,
|
||||
.tWHR = 60,
|
||||
.tAR = 10,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash stm2GbX16 = {
|
||||
.timing = &stm2GbX16_timing,
|
||||
.cmdset = &largepage_cmdset,
|
||||
.page_per_block = 64,
|
||||
.page_size = 2048,
|
||||
.flash_width = 16,
|
||||
.dfc_width = 16,
|
||||
.num_blocks = 2048,
|
||||
.chip_id = 0xba20,
|
||||
};
|
||||
|
||||
static struct pxa3xx_nand_flash *builtin_flash_types[] = {
|
||||
&samsung512MbX16,
|
||||
&samsung2GbX8,
|
||||
&samsung32GbX8,
|
||||
µn1GbX8,
|
||||
µn1GbX16,
|
||||
µn4GbX8,
|
||||
µn4GbX16,
|
||||
&stm2GbX16,
|
||||
};
|
||||
#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */
|
||||
/* Define a default flash type setting serve as flash detecting only */
|
||||
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
|
||||
|
||||
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
||||
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
||||
@ -351,23 +226,9 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = {
|
||||
#define NDTR1_tWHR(c) (min((c), 15) << 4)
|
||||
#define NDTR1_tAR(c) (min((c), 15) << 0)
|
||||
|
||||
#define tCH_NDTR0(r) (((r) >> 19) & 0x7)
|
||||
#define tCS_NDTR0(r) (((r) >> 16) & 0x7)
|
||||
#define tWH_NDTR0(r) (((r) >> 11) & 0x7)
|
||||
#define tWP_NDTR0(r) (((r) >> 8) & 0x7)
|
||||
#define tRH_NDTR0(r) (((r) >> 3) & 0x7)
|
||||
#define tRP_NDTR0(r) (((r) >> 0) & 0x7)
|
||||
|
||||
#define tR_NDTR1(r) (((r) >> 16) & 0xffff)
|
||||
#define tWHR_NDTR1(r) (((r) >> 4) & 0xf)
|
||||
#define tAR_NDTR1(r) (((r) >> 0) & 0xf)
|
||||
|
||||
/* convert nano-seconds to nand flash controller clock cycles */
|
||||
#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000)
|
||||
|
||||
/* convert nand flash controller clock cycles to nano-seconds */
|
||||
#define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000))
|
||||
|
||||
static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
|
||||
const struct pxa3xx_nand_timing *t)
|
||||
{
|
||||
@ -385,6 +246,8 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
|
||||
NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
|
||||
NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
|
||||
|
||||
info->ndtr0cs0 = ndtr0;
|
||||
info->ndtr1cs0 = ndtr1;
|
||||
nand_writel(info, NDTR0CS0, ndtr0);
|
||||
nand_writel(info, NDTR1CS0, ndtr1);
|
||||
}
|
||||
@ -408,23 +271,31 @@ static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
|
||||
uint16_t cmd, int column, int page_addr)
|
||||
static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
|
||||
{
|
||||
const struct pxa3xx_nand_flash *f = info->flash_info;
|
||||
const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
|
||||
int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
|
||||
|
||||
/* calculate data size */
|
||||
switch (f->page_size) {
|
||||
info->data_size = info->page_size;
|
||||
if (!oob_enable) {
|
||||
info->oob_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (info->page_size) {
|
||||
case 2048:
|
||||
info->data_size = (info->use_ecc) ? 2088 : 2112;
|
||||
info->oob_size = (info->use_ecc) ? 40 : 64;
|
||||
break;
|
||||
case 512:
|
||||
info->data_size = (info->use_ecc) ? 520 : 528;
|
||||
info->oob_size = (info->use_ecc) ? 8 : 16;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
|
||||
uint16_t cmd, int column, int page_addr)
|
||||
{
|
||||
const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
|
||||
pxa3xx_set_datasize(info);
|
||||
|
||||
/* generate values for NDCBx registers */
|
||||
info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
|
||||
@ -463,12 +334,13 @@ static int prepare_erase_cmd(struct pxa3xx_nand_info *info,
|
||||
|
||||
static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd)
|
||||
{
|
||||
const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset;
|
||||
const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
|
||||
|
||||
info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
|
||||
info->ndcb1 = 0;
|
||||
info->ndcb2 = 0;
|
||||
|
||||
info->oob_size = 0;
|
||||
if (cmd == cmdset->read_id) {
|
||||
info->ndcb0 |= NDCB0_CMD_TYPE(3);
|
||||
info->data_size = 8;
|
||||
@ -537,6 +409,9 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
|
||||
case STATE_PIO_WRITING:
|
||||
__raw_writesl(info->mmio_base + NDDB, info->data_buff,
|
||||
DIV_ROUND_UP(info->data_size, 4));
|
||||
if (info->oob_size > 0)
|
||||
__raw_writesl(info->mmio_base + NDDB, info->oob_buff,
|
||||
DIV_ROUND_UP(info->oob_size, 4));
|
||||
|
||||
enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
|
||||
|
||||
@ -549,6 +424,9 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
|
||||
case STATE_PIO_READING:
|
||||
__raw_readsl(info->mmio_base + NDDB, info->data_buff,
|
||||
DIV_ROUND_UP(info->data_size, 4));
|
||||
if (info->oob_size > 0)
|
||||
__raw_readsl(info->mmio_base + NDDB, info->oob_buff,
|
||||
DIV_ROUND_UP(info->oob_size, 4));
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: invalid state %d\n", __func__,
|
||||
@ -563,7 +441,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
|
||||
static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out)
|
||||
{
|
||||
struct pxa_dma_desc *desc = info->data_desc;
|
||||
int dma_len = ALIGN(info->data_size, 32);
|
||||
int dma_len = ALIGN(info->data_size + info->oob_size, 32);
|
||||
|
||||
desc->ddadr = DDADR_STOP;
|
||||
desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
|
||||
@ -700,8 +578,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
||||
int column, int page_addr)
|
||||
{
|
||||
struct pxa3xx_nand_info *info = mtd->priv;
|
||||
const struct pxa3xx_nand_flash *flash_info = info->flash_info;
|
||||
const struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset;
|
||||
const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
|
||||
int ret;
|
||||
|
||||
info->use_dma = (use_dma) ? 1 : 0;
|
||||
@ -925,8 +802,7 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd,
|
||||
|
||||
static int __readid(struct pxa3xx_nand_info *info, uint32_t *id)
|
||||
{
|
||||
const struct pxa3xx_nand_flash *f = info->flash_info;
|
||||
const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
|
||||
const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
|
||||
uint32_t ndcr;
|
||||
uint8_t id_buff[8];
|
||||
|
||||
@ -968,7 +844,9 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
|
||||
return -EINVAL;
|
||||
|
||||
/* calculate flash information */
|
||||
info->oob_size = (f->page_size == 2048) ? 64 : 16;
|
||||
info->cmdset = f->cmdset;
|
||||
info->page_size = f->page_size;
|
||||
info->oob_buff = info->data_buff + f->page_size;
|
||||
info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
|
||||
|
||||
/* calculate addressing information */
|
||||
@ -992,49 +870,20 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
|
||||
info->reg_ndcr = ndcr;
|
||||
|
||||
pxa3xx_nand_set_timing(info, f->timing);
|
||||
info->flash_info = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
|
||||
struct pxa3xx_nand_timing *t)
|
||||
{
|
||||
unsigned long nand_clk = clk_get_rate(info->clk);
|
||||
uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
|
||||
uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
|
||||
|
||||
t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
|
||||
t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
|
||||
t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
|
||||
t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
|
||||
t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
|
||||
t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
|
||||
|
||||
t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
|
||||
t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
|
||||
t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
|
||||
}
|
||||
|
||||
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
||||
{
|
||||
uint32_t ndcr = nand_readl(info, NDCR);
|
||||
struct nand_flash_dev *type = NULL;
|
||||
uint32_t id = -1;
|
||||
uint32_t id = -1, page_per_block, num_blocks;
|
||||
int i;
|
||||
|
||||
default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
|
||||
default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
|
||||
default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
|
||||
default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
|
||||
|
||||
if (default_flash.page_size == 2048)
|
||||
default_flash.cmdset = &largepage_cmdset;
|
||||
else
|
||||
default_flash.cmdset = &smallpage_cmdset;
|
||||
|
||||
page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
|
||||
info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
|
||||
/* set info fields needed to __readid */
|
||||
info->flash_info = &default_flash;
|
||||
info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
|
||||
info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
|
||||
info->reg_ndcr = ndcr;
|
||||
|
||||
if (__readid(info, &id))
|
||||
@ -1053,21 +902,20 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
||||
return -ENODEV;
|
||||
|
||||
/* fill the missing flash information */
|
||||
i = __ffs(default_flash.page_per_block * default_flash.page_size);
|
||||
default_flash.num_blocks = type->chipsize << (20 - i);
|
||||
|
||||
info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
|
||||
i = __ffs(page_per_block * info->page_size);
|
||||
num_blocks = type->chipsize << (20 - i);
|
||||
|
||||
/* calculate addressing information */
|
||||
info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1;
|
||||
info->col_addr_cycles = (info->page_size == 2048) ? 2 : 1;
|
||||
|
||||
if (default_flash.num_blocks * default_flash.page_per_block > 65536)
|
||||
if (num_blocks * page_per_block > 65536)
|
||||
info->row_addr_cycles = 3;
|
||||
else
|
||||
info->row_addr_cycles = 2;
|
||||
|
||||
pxa3xx_nand_detect_timing(info, &default_timing);
|
||||
default_flash.timing = &default_timing;
|
||||
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
||||
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
||||
info->cmdset = &default_cmdset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1083,38 +931,29 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
|
||||
if (pxa3xx_nand_detect_config(info) == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i<pdata->num_flash; ++i) {
|
||||
f = pdata->flash + i;
|
||||
/* we use default timing to detect id */
|
||||
f = DEFAULT_FLASH_TYPE;
|
||||
pxa3xx_nand_config_flash(info, f);
|
||||
if (__readid(info, &id))
|
||||
goto fail_detect;
|
||||
|
||||
if (pxa3xx_nand_config_flash(info, f))
|
||||
continue;
|
||||
|
||||
if (__readid(info, &id))
|
||||
continue;
|
||||
|
||||
if (id == f->chip_id)
|
||||
for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) {
|
||||
/* we first choose the flash definition from platfrom */
|
||||
if (i < pdata->num_flash)
|
||||
f = pdata->flash + i;
|
||||
else
|
||||
f = &builtin_flash_types[i - pdata->num_flash + 1];
|
||||
if (f->chip_id == id) {
|
||||
dev_info(&info->pdev->dev, "detect chip id: 0x%x\n", id);
|
||||
pxa3xx_nand_config_flash(info, f);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
|
||||
for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) {
|
||||
|
||||
f = builtin_flash_types[i];
|
||||
|
||||
if (pxa3xx_nand_config_flash(info, f))
|
||||
continue;
|
||||
|
||||
if (__readid(info, &id))
|
||||
continue;
|
||||
|
||||
if (id == f->chip_id)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
dev_warn(&info->pdev->dev,
|
||||
"failed to detect configured nand flash; found %04x instead of\n",
|
||||
id);
|
||||
fail_detect:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1177,10 +1016,9 @@ static struct nand_ecclayout hw_largepage_ecclayout = {
|
||||
static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
|
||||
struct pxa3xx_nand_info *info)
|
||||
{
|
||||
const struct pxa3xx_nand_flash *f = info->flash_info;
|
||||
struct nand_chip *this = &info->nand_chip;
|
||||
|
||||
this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0;
|
||||
this->options = (info->reg_ndcr & NDCR_DWIDTH_C) ? NAND_BUSWIDTH_16: 0;
|
||||
|
||||
this->waitfunc = pxa3xx_nand_waitfunc;
|
||||
this->select_chip = pxa3xx_nand_select_chip;
|
||||
@ -1196,9 +1034,9 @@ static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
|
||||
this->ecc.hwctl = pxa3xx_nand_ecc_hwctl;
|
||||
this->ecc.calculate = pxa3xx_nand_ecc_calculate;
|
||||
this->ecc.correct = pxa3xx_nand_ecc_correct;
|
||||
this->ecc.size = f->page_size;
|
||||
this->ecc.size = info->page_size;
|
||||
|
||||
if (f->page_size == 2048)
|
||||
if (info->page_size == 2048)
|
||||
this->ecc.layout = &hw_largepage_ecclayout;
|
||||
else
|
||||
this->ecc.layout = &hw_smallpage_ecclayout;
|
||||
@ -1411,9 +1249,11 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
|
||||
struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev);
|
||||
struct pxa3xx_nand_info *info = mtd->priv;
|
||||
|
||||
nand_writel(info, NDTR0CS0, info->ndtr0cs0);
|
||||
nand_writel(info, NDTR1CS0, info->ndtr1cs0);
|
||||
clk_enable(info->clk);
|
||||
|
||||
return pxa3xx_nand_config_flash(info, info->flash_info);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pxa3xx_nand_suspend NULL
|
||||
|
@ -757,11 +757,6 @@ static irqreturn_t r852_irq(int irq, void *data)
|
||||
|
||||
spin_lock_irqsave(&dev->irqlock, flags);
|
||||
|
||||
/* We can recieve shared interrupt while pci is suspended
|
||||
in that case reads will return 0xFFFFFFFF.... */
|
||||
if (dev->insuspend)
|
||||
goto out;
|
||||
|
||||
/* handle card detection interrupts first */
|
||||
card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
|
||||
r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
|
||||
@ -1035,7 +1030,6 @@ void r852_shutdown(struct pci_dev *pci_dev)
|
||||
int r852_suspend(struct device *device)
|
||||
{
|
||||
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
|
||||
unsigned long flags;
|
||||
|
||||
if (dev->ctlreg & R852_CTL_CARDENABLE)
|
||||
return -EBUSY;
|
||||
@ -1047,43 +1041,22 @@ int r852_suspend(struct device *device)
|
||||
r852_disable_irqs(dev);
|
||||
r852_engine_disable(dev);
|
||||
|
||||
spin_lock_irqsave(&dev->irqlock, flags);
|
||||
dev->insuspend = 1;
|
||||
spin_unlock_irqrestore(&dev->irqlock, flags);
|
||||
|
||||
/* At that point, even if interrupt handler is running, it will quit */
|
||||
/* So wait for this to happen explictly */
|
||||
synchronize_irq(dev->irq);
|
||||
|
||||
/* If card was pulled off just during the suspend, which is very
|
||||
unlikely, we will remove it on resume, it too late now
|
||||
anyway... */
|
||||
dev->card_unstable = 0;
|
||||
|
||||
pci_save_state(to_pci_dev(device));
|
||||
return pci_prepare_to_sleep(to_pci_dev(device));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int r852_resume(struct device *device)
|
||||
{
|
||||
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
|
||||
unsigned long flags;
|
||||
|
||||
/* Turn on the hardware */
|
||||
pci_back_from_sleep(to_pci_dev(device));
|
||||
pci_restore_state(to_pci_dev(device));
|
||||
|
||||
r852_disable_irqs(dev);
|
||||
r852_card_update_present(dev);
|
||||
r852_engine_disable(dev);
|
||||
|
||||
|
||||
/* Now its safe for IRQ to run */
|
||||
spin_lock_irqsave(&dev->irqlock, flags);
|
||||
dev->insuspend = 0;
|
||||
spin_unlock_irqrestore(&dev->irqlock, flags);
|
||||
|
||||
|
||||
/* If card status changed, just do the work */
|
||||
if (dev->card_detected != dev->card_registred) {
|
||||
dbg("card was %s during low power state",
|
||||
@ -1121,7 +1094,6 @@ MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
|
||||
|
||||
SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
|
||||
|
||||
|
||||
static struct pci_driver r852_pci_driver = {
|
||||
.name = DRV_NAME,
|
||||
.id_table = r852_pci_id_tbl,
|
||||
|
@ -140,8 +140,6 @@ struct r852_device {
|
||||
/* interrupt handling */
|
||||
spinlock_t irqlock; /* IRQ protecting lock */
|
||||
int irq; /* irq num */
|
||||
int insuspend; /* device is suspended */
|
||||
|
||||
/* misc */
|
||||
void *tmp_buffer; /* temporary buffer */
|
||||
uint8_t ctlreg; /* cached contents of control reg */
|
||||
|
@ -44,7 +44,7 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
|
||||
pp = NULL;
|
||||
i = 0;
|
||||
while ((pp = of_get_next_child(node, pp))) {
|
||||
const u32 *reg;
|
||||
const __be32 *reg;
|
||||
int len;
|
||||
|
||||
reg = of_get_property(pp, "reg", &len);
|
||||
|
@ -32,10 +32,11 @@ config MTD_ONENAND_OMAP2
|
||||
|
||||
config MTD_ONENAND_SAMSUNG
|
||||
tristate "OneNAND on Samsung SOC controller support"
|
||||
depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210
|
||||
depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310
|
||||
help
|
||||
Support for a OneNAND flash device connected to an Samsung SOC
|
||||
S3C64XX/S5PC1XX controller.
|
||||
Support for a OneNAND flash device connected to an Samsung SOC.
|
||||
S3C64XX/S5PC100 use command mapping method.
|
||||
S5PC110/S5PC210 use generic OneNAND method.
|
||||
|
||||
config MTD_ONENAND_OTP
|
||||
bool "OneNAND OTP Support"
|
||||
|
@ -3365,18 +3365,19 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
static void onenand_check_features(struct mtd_info *mtd)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
unsigned int density, process;
|
||||
unsigned int density, process, numbufs;
|
||||
|
||||
/* Lock scheme depends on density and process */
|
||||
density = onenand_get_density(this->device_id);
|
||||
process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
|
||||
numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8;
|
||||
|
||||
/* Lock scheme */
|
||||
switch (density) {
|
||||
case ONENAND_DEVICE_DENSITY_4Gb:
|
||||
if (ONENAND_IS_DDP(this))
|
||||
this->options |= ONENAND_HAS_2PLANE;
|
||||
else
|
||||
else if (numbufs == 1)
|
||||
this->options |= ONENAND_HAS_4KB_PAGE;
|
||||
|
||||
case ONENAND_DEVICE_DENSITY_2Gb:
|
||||
@ -4027,7 +4028,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
||||
mtd->ecclayout = this->ecclayout;
|
||||
|
||||
/* Fill in remaining MTD driver data */
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
mtd->erase = onenand_erase;
|
||||
mtd->point = NULL;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/mtd/onenand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/mach/flash.h>
|
||||
#include <plat/regs-onenand.h>
|
||||
@ -58,7 +59,7 @@ enum soc_type {
|
||||
#define MAP_11 (0x3)
|
||||
|
||||
#define S3C64XX_CMD_MAP_SHIFT 24
|
||||
#define S5PC1XX_CMD_MAP_SHIFT 26
|
||||
#define S5PC100_CMD_MAP_SHIFT 26
|
||||
|
||||
#define S3C6400_FBA_SHIFT 10
|
||||
#define S3C6400_FPA_SHIFT 4
|
||||
@ -81,6 +82,17 @@ enum soc_type {
|
||||
#define S5PC110_DMA_TRANS_CMD 0x418
|
||||
#define S5PC110_DMA_TRANS_STATUS 0x41C
|
||||
#define S5PC110_DMA_TRANS_DIR 0x420
|
||||
#define S5PC110_INTC_DMA_CLR 0x1004
|
||||
#define S5PC110_INTC_ONENAND_CLR 0x1008
|
||||
#define S5PC110_INTC_DMA_MASK 0x1024
|
||||
#define S5PC110_INTC_ONENAND_MASK 0x1028
|
||||
#define S5PC110_INTC_DMA_PEND 0x1044
|
||||
#define S5PC110_INTC_ONENAND_PEND 0x1048
|
||||
#define S5PC110_INTC_DMA_STATUS 0x1064
|
||||
#define S5PC110_INTC_ONENAND_STATUS 0x1068
|
||||
|
||||
#define S5PC110_INTC_DMA_TD (1 << 24)
|
||||
#define S5PC110_INTC_DMA_TE (1 << 16)
|
||||
|
||||
#define S5PC110_DMA_CFG_SINGLE (0x0 << 16)
|
||||
#define S5PC110_DMA_CFG_4BURST (0x2 << 16)
|
||||
@ -134,6 +146,7 @@ struct s3c_onenand {
|
||||
void __iomem *dma_addr;
|
||||
struct resource *dma_res;
|
||||
unsigned long phys_base;
|
||||
struct completion complete;
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
struct mtd_partition *parts;
|
||||
#endif
|
||||
@ -191,7 +204,7 @@ static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val)
|
||||
|
||||
static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val)
|
||||
{
|
||||
return (type << S5PC1XX_CMD_MAP_SHIFT) | val;
|
||||
return (type << S5PC100_CMD_MAP_SHIFT) | val;
|
||||
}
|
||||
|
||||
static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa)
|
||||
@ -531,10 +544,13 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
|
||||
static int (*s5pc110_dma_ops)(void *dst, void *src, size_t count, int direction);
|
||||
|
||||
static int s5pc110_dma_poll(void *dst, void *src, size_t count, int direction)
|
||||
{
|
||||
void __iomem *base = onenand->dma_addr;
|
||||
int status;
|
||||
unsigned long timeout;
|
||||
|
||||
writel(src, base + S5PC110_DMA_SRC_ADDR);
|
||||
writel(dst, base + S5PC110_DMA_DST_ADDR);
|
||||
@ -552,6 +568,13 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
|
||||
|
||||
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
|
||||
|
||||
/*
|
||||
* There's no exact timeout values at Spec.
|
||||
* In real case it takes under 1 msec.
|
||||
* So 20 msecs are enough.
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(20);
|
||||
|
||||
do {
|
||||
status = readl(base + S5PC110_DMA_TRANS_STATUS);
|
||||
if (status & S5PC110_DMA_TRANS_STATUS_TE) {
|
||||
@ -559,13 +582,68 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
|
||||
base + S5PC110_DMA_TRANS_CMD);
|
||||
return -EIO;
|
||||
}
|
||||
} while (!(status & S5PC110_DMA_TRANS_STATUS_TD));
|
||||
} while (!(status & S5PC110_DMA_TRANS_STATUS_TD) &&
|
||||
time_before(jiffies, timeout));
|
||||
|
||||
writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t s5pc110_onenand_irq(int irq, void *data)
|
||||
{
|
||||
void __iomem *base = onenand->dma_addr;
|
||||
int status, cmd = 0;
|
||||
|
||||
status = readl(base + S5PC110_INTC_DMA_STATUS);
|
||||
|
||||
if (likely(status & S5PC110_INTC_DMA_TD))
|
||||
cmd = S5PC110_DMA_TRANS_CMD_TDC;
|
||||
|
||||
if (unlikely(status & S5PC110_INTC_DMA_TE))
|
||||
cmd = S5PC110_DMA_TRANS_CMD_TEC;
|
||||
|
||||
writel(cmd, base + S5PC110_DMA_TRANS_CMD);
|
||||
writel(status, base + S5PC110_INTC_DMA_CLR);
|
||||
|
||||
if (!onenand->complete.done)
|
||||
complete(&onenand->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int s5pc110_dma_irq(void *dst, void *src, size_t count, int direction)
|
||||
{
|
||||
void __iomem *base = onenand->dma_addr;
|
||||
int status;
|
||||
|
||||
status = readl(base + S5PC110_INTC_DMA_MASK);
|
||||
if (status) {
|
||||
status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE);
|
||||
writel(status, base + S5PC110_INTC_DMA_MASK);
|
||||
}
|
||||
|
||||
writel(src, base + S5PC110_DMA_SRC_ADDR);
|
||||
writel(dst, base + S5PC110_DMA_DST_ADDR);
|
||||
|
||||
if (direction == S5PC110_DMA_DIR_READ) {
|
||||
writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG);
|
||||
writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG);
|
||||
} else {
|
||||
writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG);
|
||||
writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG);
|
||||
}
|
||||
|
||||
writel(count, base + S5PC110_DMA_TRANS_SIZE);
|
||||
writel(direction, base + S5PC110_DMA_TRANS_DIR);
|
||||
|
||||
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
|
||||
|
||||
wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
|
||||
unsigned char *buffer, int offset, size_t count)
|
||||
{
|
||||
@ -573,7 +651,8 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
|
||||
void __iomem *p;
|
||||
void *buf = (void *) buffer;
|
||||
dma_addr_t dma_src, dma_dst;
|
||||
int err;
|
||||
int err, page_dma = 0;
|
||||
struct device *dev = &onenand->pdev->dev;
|
||||
|
||||
p = this->base + area;
|
||||
if (ONENAND_CURRENT_BUFFERRAM(this)) {
|
||||
@ -597,21 +676,27 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
|
||||
page = vmalloc_to_page(buf);
|
||||
if (!page)
|
||||
goto normal;
|
||||
buf = page_address(page) + ((size_t) buf & ~PAGE_MASK);
|
||||
}
|
||||
|
||||
/* DMA routine */
|
||||
dma_src = onenand->phys_base + (p - this->base);
|
||||
dma_dst = dma_map_single(&onenand->pdev->dev,
|
||||
buf, count, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) {
|
||||
dev_err(&onenand->pdev->dev,
|
||||
"Couldn't map a %d byte buffer for DMA\n", count);
|
||||
page_dma = 1;
|
||||
/* DMA routine */
|
||||
dma_src = onenand->phys_base + (p - this->base);
|
||||
dma_dst = dma_map_page(dev, page, 0, count, DMA_FROM_DEVICE);
|
||||
} else {
|
||||
/* DMA routine */
|
||||
dma_src = onenand->phys_base + (p - this->base);
|
||||
dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE);
|
||||
}
|
||||
if (dma_mapping_error(dev, dma_dst)) {
|
||||
dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count);
|
||||
goto normal;
|
||||
}
|
||||
err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src,
|
||||
count, S5PC110_DMA_DIR_READ);
|
||||
dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
|
||||
|
||||
if (page_dma)
|
||||
dma_unmap_page(dev, dma_dst, count, DMA_FROM_DEVICE);
|
||||
else
|
||||
dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE);
|
||||
|
||||
if (!err)
|
||||
return 0;
|
||||
@ -759,7 +844,6 @@ static void s3c_onenand_setup(struct mtd_info *mtd)
|
||||
onenand->cmd_map = s5pc1xx_cmd_map;
|
||||
} else if (onenand->type == TYPE_S5PC110) {
|
||||
/* Use generic onenand functions */
|
||||
onenand->cmd_map = s5pc1xx_cmd_map;
|
||||
this->read_bufferram = s5pc110_read_bufferram;
|
||||
this->chip_probe = s5pc110_chip_probe;
|
||||
return;
|
||||
@ -904,6 +988,20 @@ static int s3c_onenand_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
onenand->phys_base = onenand->base_res->start;
|
||||
|
||||
s5pc110_dma_ops = s5pc110_dma_poll;
|
||||
/* Interrupt support */
|
||||
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (r) {
|
||||
init_completion(&onenand->complete);
|
||||
s5pc110_dma_ops = s5pc110_dma_irq;
|
||||
err = request_irq(r->start, s5pc110_onenand_irq,
|
||||
IRQF_SHARED, "onenand", &onenand);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
goto scan_failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onenand_scan(mtd, 1)) {
|
||||
@ -1000,7 +1098,7 @@ static int s3c_pm_ops_suspend(struct device *dev)
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
|
||||
this->wait(mtd, FL_PM_SUSPENDED);
|
||||
return mtd->suspend(mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_pm_ops_resume(struct device *dev)
|
||||
@ -1009,7 +1107,6 @@ static int s3c_pm_ops_resume(struct device *dev)
|
||||
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
|
||||
mtd->resume(mtd);
|
||||
this->unlock_all(mtd);
|
||||
return 0;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
struct ftl_zone {
|
||||
int initialized;
|
||||
bool initialized;
|
||||
int16_t *lba_to_phys_table; /* LBA to physical table */
|
||||
struct kfifo free_sectors; /* queue of free sectors */
|
||||
};
|
||||
@ -37,8 +37,8 @@ struct sm_ftl {
|
||||
int zone_count; /* number of zones */
|
||||
int max_lba; /* maximum lba in a zone */
|
||||
int smallpagenand; /* 256 bytes/page nand */
|
||||
int readonly; /* is FS readonly */
|
||||
int unstable;
|
||||
bool readonly; /* is FS readonly */
|
||||
bool unstable;
|
||||
int cis_block; /* CIS block location */
|
||||
int cis_boffset; /* CIS offset in the block */
|
||||
int cis_page_offset; /* CIS offset in the page */
|
||||
@ -49,7 +49,7 @@ struct sm_ftl {
|
||||
int cache_zone; /* zone of cached block */
|
||||
unsigned char *cache_data; /* cached block data */
|
||||
long unsigned int cache_data_invalid_bitmap;
|
||||
int cache_clean;
|
||||
bool cache_clean;
|
||||
struct work_struct flush_work;
|
||||
struct timer_list timer;
|
||||
|
||||
|
@ -23,7 +23,7 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *,
|
||||
static inline struct jffs2_inode_cache *
|
||||
first_inode_chain(int *i, struct jffs2_sb_info *c)
|
||||
{
|
||||
for (; *i < INOCACHE_HASHSIZE; (*i)++) {
|
||||
for (; *i < c->inocache_hashsize; (*i)++) {
|
||||
if (c->inocache_list[*i])
|
||||
return c->inocache_list[*i];
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
*datalen = orig_slen;
|
||||
*cdatalen = orig_dlen;
|
||||
compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
|
||||
compr_ret = this->compress(data_in, output_buf, datalen, cdatalen);
|
||||
spin_lock(&jffs2_compressor_list_lock);
|
||||
this->usecount--;
|
||||
if (!compr_ret) {
|
||||
@ -152,7 +152,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
*datalen = orig_slen;
|
||||
*cdatalen = orig_dlen;
|
||||
compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
|
||||
compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen);
|
||||
spin_lock(&jffs2_compressor_list_lock);
|
||||
this->usecount--;
|
||||
if (!compr_ret) {
|
||||
@ -220,7 +220,7 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
if (comprtype == this->compr) {
|
||||
this->usecount++;
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
|
||||
ret = this->decompress(cdata_in, data_out, cdatalen, datalen);
|
||||
spin_lock(&jffs2_compressor_list_lock);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
|
||||
|
@ -49,9 +49,9 @@ struct jffs2_compressor {
|
||||
char *name;
|
||||
char compr; /* JFFS2_COMPR_XXX */
|
||||
int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *srclen, uint32_t *destlen, void *model);
|
||||
uint32_t *srclen, uint32_t *destlen);
|
||||
int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
|
||||
uint32_t cdatalen, uint32_t datalen, void *model);
|
||||
uint32_t cdatalen, uint32_t datalen);
|
||||
int usecount;
|
||||
int disabled; /* if set the compressor won't compress */
|
||||
unsigned char *compr_buf; /* used by size compr. mode */
|
||||
|
@ -42,7 +42,7 @@ static int __init alloc_workspace(void)
|
||||
}
|
||||
|
||||
static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen, void *model)
|
||||
uint32_t *sourcelen, uint32_t *dstlen)
|
||||
{
|
||||
size_t compress_size;
|
||||
int ret;
|
||||
@ -67,7 +67,7 @@ static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
}
|
||||
|
||||
static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t srclen, uint32_t destlen, void *model)
|
||||
uint32_t srclen, uint32_t destlen)
|
||||
{
|
||||
size_t dl = destlen;
|
||||
int ret;
|
||||
|
@ -31,8 +31,7 @@
|
||||
/* _compress returns the compressed size, -1 if bigger */
|
||||
static int jffs2_rtime_compress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen,
|
||||
void *model)
|
||||
uint32_t *sourcelen, uint32_t *dstlen)
|
||||
{
|
||||
short positions[256];
|
||||
int outpos = 0;
|
||||
@ -73,8 +72,7 @@ static int jffs2_rtime_compress(unsigned char *data_in,
|
||||
|
||||
static int jffs2_rtime_decompress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t srclen, uint32_t destlen,
|
||||
void *model)
|
||||
uint32_t srclen, uint32_t destlen)
|
||||
{
|
||||
short positions[256];
|
||||
int outpos = 0;
|
||||
|
@ -298,7 +298,7 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
|
||||
#if 0
|
||||
/* _compress returns the compressed size, -1 if bigger */
|
||||
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen, void *model)
|
||||
uint32_t *sourcelen, uint32_t *dstlen)
|
||||
{
|
||||
return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in,
|
||||
cpage_out, sourcelen, dstlen);
|
||||
@ -306,8 +306,7 @@ int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
#endif
|
||||
static int jffs2_dynrubin_compress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen,
|
||||
void *model)
|
||||
uint32_t *sourcelen, uint32_t *dstlen)
|
||||
{
|
||||
int bits[8];
|
||||
unsigned char histo[256];
|
||||
@ -387,8 +386,7 @@ static void rubin_do_decompress(int bit_divider, int *bits,
|
||||
|
||||
static int jffs2_rubinmips_decompress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t sourcelen, uint32_t dstlen,
|
||||
void *model)
|
||||
uint32_t sourcelen, uint32_t dstlen)
|
||||
{
|
||||
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in,
|
||||
cpage_out, sourcelen, dstlen);
|
||||
@ -397,8 +395,7 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in,
|
||||
|
||||
static int jffs2_dynrubin_decompress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t sourcelen, uint32_t dstlen,
|
||||
void *model)
|
||||
uint32_t sourcelen, uint32_t dstlen)
|
||||
{
|
||||
int bits[8];
|
||||
int c;
|
||||
|
@ -68,8 +68,7 @@ static void free_workspaces(void)
|
||||
|
||||
static int jffs2_zlib_compress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen,
|
||||
void *model)
|
||||
uint32_t *sourcelen, uint32_t *dstlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -136,8 +135,7 @@ static int jffs2_zlib_compress(unsigned char *data_in,
|
||||
|
||||
static int jffs2_zlib_decompress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t srclen, uint32_t destlen,
|
||||
void *model)
|
||||
uint32_t srclen, uint32_t destlen)
|
||||
{
|
||||
int ret;
|
||||
int wbits = MAX_WBITS;
|
||||
|
@ -367,7 +367,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
||||
}
|
||||
|
||||
/* We use f->target field to store the target path. */
|
||||
f->target = kmalloc(targetlen + 1, GFP_KERNEL);
|
||||
f->target = kmemdup(target, targetlen + 1, GFP_KERNEL);
|
||||
if (!f->target) {
|
||||
printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
|
||||
mutex_unlock(&f->sem);
|
||||
@ -376,7 +376,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(f->target, target, targetlen + 1);
|
||||
D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target));
|
||||
|
||||
/* No data here. Only a metadata node, which will be
|
||||
|
@ -151,7 +151,7 @@ int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
|
||||
}
|
||||
|
||||
/* Be nice */
|
||||
yield();
|
||||
cond_resched();
|
||||
mutex_lock(&c->erase_free_sem);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
@ -474,6 +474,25 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
|
||||
return inode;
|
||||
}
|
||||
|
||||
static int calculate_inocache_hashsize(uint32_t flash_size)
|
||||
{
|
||||
/*
|
||||
* Pick a inocache hash size based on the size of the medium.
|
||||
* Count how many megabytes we're dealing with, apply a hashsize twice
|
||||
* that size, but rounding down to the usual big powers of 2. And keep
|
||||
* to sensible bounds.
|
||||
*/
|
||||
|
||||
int size_mb = flash_size / 1024 / 1024;
|
||||
int hashsize = (size_mb * 2) & ~0x3f;
|
||||
|
||||
if (hashsize < INOCACHE_HASHSIZE_MIN)
|
||||
return INOCACHE_HASHSIZE_MIN;
|
||||
if (hashsize > INOCACHE_HASHSIZE_MAX)
|
||||
return INOCACHE_HASHSIZE_MAX;
|
||||
|
||||
return hashsize;
|
||||
}
|
||||
|
||||
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
@ -520,7 +539,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c->inocache_list = kcalloc(INOCACHE_HASHSIZE, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
|
||||
c->inocache_hashsize = calculate_inocache_hashsize(c->flash_size);
|
||||
c->inocache_list = kcalloc(c->inocache_hashsize, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
|
||||
if (!c->inocache_list) {
|
||||
ret = -ENOMEM;
|
||||
goto out_wbuf;
|
||||
|
@ -219,13 +219,14 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
if (!list_empty(&c->erase_complete_list) ||
|
||||
!list_empty(&c->erase_pending_list)) {
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
mutex_unlock(&c->alloc_sem);
|
||||
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n"));
|
||||
if (jffs2_erase_pending_blocks(c, 1)) {
|
||||
mutex_unlock(&c->alloc_sem);
|
||||
if (jffs2_erase_pending_blocks(c, 1))
|
||||
return 0;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n"));
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
mutex_lock(&c->alloc_sem);
|
||||
}
|
||||
|
||||
/* First, work out which block we're garbage-collecting */
|
||||
|
@ -100,6 +100,7 @@ struct jffs2_sb_info {
|
||||
wait_queue_head_t erase_wait; /* For waiting for erases to complete */
|
||||
|
||||
wait_queue_head_t inocache_wq;
|
||||
int inocache_hashsize;
|
||||
struct jffs2_inode_cache **inocache_list;
|
||||
spinlock_t inocache_lock;
|
||||
|
||||
|
@ -420,7 +420,7 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t
|
||||
{
|
||||
struct jffs2_inode_cache *ret;
|
||||
|
||||
ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
|
||||
ret = c->inocache_list[ino % c->inocache_hashsize];
|
||||
while (ret && ret->ino < ino) {
|
||||
ret = ret->next;
|
||||
}
|
||||
@ -441,7 +441,7 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new
|
||||
|
||||
dbg_inocache("add %p (ino #%u)\n", new, new->ino);
|
||||
|
||||
prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
|
||||
prev = &c->inocache_list[new->ino % c->inocache_hashsize];
|
||||
|
||||
while ((*prev) && (*prev)->ino < new->ino) {
|
||||
prev = &(*prev)->next;
|
||||
@ -462,7 +462,7 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
|
||||
dbg_inocache("del %p (ino #%u)\n", old, old->ino);
|
||||
spin_lock(&c->inocache_lock);
|
||||
|
||||
prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
|
||||
prev = &c->inocache_list[old->ino % c->inocache_hashsize];
|
||||
|
||||
while ((*prev) && (*prev)->ino < old->ino) {
|
||||
prev = &(*prev)->next;
|
||||
@ -487,7 +487,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
|
||||
int i;
|
||||
struct jffs2_inode_cache *this, *next;
|
||||
|
||||
for (i=0; i<INOCACHE_HASHSIZE; i++) {
|
||||
for (i=0; i < c->inocache_hashsize; i++) {
|
||||
this = c->inocache_list[i];
|
||||
while (this) {
|
||||
next = this->next;
|
||||
|
@ -199,7 +199,8 @@ struct jffs2_inode_cache {
|
||||
#define RAWNODE_CLASS_XATTR_DATUM 1
|
||||
#define RAWNODE_CLASS_XATTR_REF 2
|
||||
|
||||
#define INOCACHE_HASHSIZE 128
|
||||
#define INOCACHE_HASHSIZE_MIN 128
|
||||
#define INOCACHE_HASHSIZE_MAX 1024
|
||||
|
||||
#define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size)
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "summary.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define DEFAULT_EMPTY_SCAN_SIZE 1024
|
||||
#define DEFAULT_EMPTY_SCAN_SIZE 256
|
||||
|
||||
#define noisy_printk(noise, args...) do { \
|
||||
if (*(noise)) { \
|
||||
@ -435,7 +435,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
||||
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
|
||||
struct jffs2_unknown_node *node;
|
||||
struct jffs2_unknown_node crcnode;
|
||||
uint32_t ofs, prevofs;
|
||||
uint32_t ofs, prevofs, max_ofs;
|
||||
uint32_t hdr_crc, buf_ofs, buf_len;
|
||||
int err;
|
||||
int noise = 0;
|
||||
@ -550,12 +550,12 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
||||
|
||||
/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
|
||||
ofs = 0;
|
||||
|
||||
/* Scan only 4KiB of 0xFF before declaring it's empty */
|
||||
while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
|
||||
max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
|
||||
/* Scan only EMPTY_SCAN_SIZE of 0xFF before declaring it's empty */
|
||||
while(ofs < max_ofs && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
|
||||
ofs += 4;
|
||||
|
||||
if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
|
||||
if (ofs == max_ofs) {
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
/* scan oob, take care of cleanmarker */
|
||||
|
@ -84,7 +84,7 @@ struct nand_bbt_descr {
|
||||
#define NAND_BBT_PERCHIP 0x00000080
|
||||
/* bbt has a version counter at offset veroffs */
|
||||
#define NAND_BBT_VERSION 0x00000100
|
||||
/* Create a bbt if none axists */
|
||||
/* Create a bbt if none exists */
|
||||
#define NAND_BBT_CREATE 0x00000200
|
||||
/* Search good / bad pattern through all pages of a block */
|
||||
#define NAND_BBT_SCANALLPAGES 0x00000400
|
||||
@ -102,6 +102,8 @@ struct nand_bbt_descr {
|
||||
#define NAND_BBT_SCANBYTE1AND6 0x00100000
|
||||
/* The nand_bbt_descr was created dynamicaly and must be freed */
|
||||
#define NAND_BBT_DYNAMICSTRUCT 0x00200000
|
||||
/* The bad block table does not OOB for marker */
|
||||
#define NAND_BBT_NO_OOB 0x00400000
|
||||
|
||||
/* The maximum number of blocks to scan for a bbt */
|
||||
#define NAND_BBT_SCAN_MAXBLOCKS 4
|
||||
|
@ -289,6 +289,7 @@ struct cfi_private {
|
||||
must be of the same type. */
|
||||
int mfr, id;
|
||||
int numchips;
|
||||
map_word sector_erase_cmd;
|
||||
unsigned long chipshift; /* Because they're of the same type */
|
||||
const char *im_name; /* inter_module name for cmdset_setup */
|
||||
struct flchip chips[0]; /* per-chip data structure for each chip */
|
||||
|
181
include/linux/mtd/fsmc.h
Normal file
181
include/linux/mtd/fsmc.h
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* incude/mtd/fsmc.h
|
||||
*
|
||||
* ST Microelectronics
|
||||
* Flexible Static Memory Controller (FSMC)
|
||||
* platform data interface and header file
|
||||
*
|
||||
* Copyright © 2010 ST Microelectronics
|
||||
* Vipin Kumar <vipin.kumar@st.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __MTD_FSMC_H
|
||||
#define __MTD_FSMC_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/param.h>
|
||||
|
||||
#define FSMC_NAND_BW8 1
|
||||
#define FSMC_NAND_BW16 2
|
||||
|
||||
/*
|
||||
* The placement of the Command Latch Enable (CLE) and
|
||||
* Address Latch Enable (ALE) is twised around in the
|
||||
* SPEAR310 implementation.
|
||||
*/
|
||||
#if defined(CONFIG_MACH_SPEAR310)
|
||||
#define PLAT_NAND_CLE (1 << 17)
|
||||
#define PLAT_NAND_ALE (1 << 16)
|
||||
#else
|
||||
#define PLAT_NAND_CLE (1 << 16)
|
||||
#define PLAT_NAND_ALE (1 << 17)
|
||||
#endif
|
||||
|
||||
#define FSMC_MAX_NOR_BANKS 4
|
||||
#define FSMC_MAX_NAND_BANKS 4
|
||||
|
||||
#define FSMC_FLASH_WIDTH8 1
|
||||
#define FSMC_FLASH_WIDTH16 2
|
||||
|
||||
struct fsmc_nor_bank_regs {
|
||||
uint32_t ctrl;
|
||||
uint32_t ctrl_tim;
|
||||
};
|
||||
|
||||
/* ctrl register definitions */
|
||||
#define BANK_ENABLE (1 << 0)
|
||||
#define MUXED (1 << 1)
|
||||
#define NOR_DEV (2 << 2)
|
||||
#define WIDTH_8 (0 << 4)
|
||||
#define WIDTH_16 (1 << 4)
|
||||
#define RSTPWRDWN (1 << 6)
|
||||
#define WPROT (1 << 7)
|
||||
#define WRT_ENABLE (1 << 12)
|
||||
#define WAIT_ENB (1 << 13)
|
||||
|
||||
/* ctrl_tim register definitions */
|
||||
|
||||
struct fsms_nand_bank_regs {
|
||||
uint32_t pc;
|
||||
uint32_t sts;
|
||||
uint32_t comm;
|
||||
uint32_t attrib;
|
||||
uint32_t ioata;
|
||||
uint32_t ecc1;
|
||||
uint32_t ecc2;
|
||||
uint32_t ecc3;
|
||||
};
|
||||
|
||||
#define FSMC_NOR_REG_SIZE 0x40
|
||||
|
||||
struct fsmc_regs {
|
||||
struct fsmc_nor_bank_regs nor_bank_regs[FSMC_MAX_NOR_BANKS];
|
||||
uint8_t reserved_1[0x40 - 0x20];
|
||||
struct fsms_nand_bank_regs bank_regs[FSMC_MAX_NAND_BANKS];
|
||||
uint8_t reserved_2[0xfe0 - 0xc0];
|
||||
uint32_t peripid0; /* 0xfe0 */
|
||||
uint32_t peripid1; /* 0xfe4 */
|
||||
uint32_t peripid2; /* 0xfe8 */
|
||||
uint32_t peripid3; /* 0xfec */
|
||||
uint32_t pcellid0; /* 0xff0 */
|
||||
uint32_t pcellid1; /* 0xff4 */
|
||||
uint32_t pcellid2; /* 0xff8 */
|
||||
uint32_t pcellid3; /* 0xffc */
|
||||
};
|
||||
|
||||
#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
|
||||
|
||||
/* pc register definitions */
|
||||
#define FSMC_RESET (1 << 0)
|
||||
#define FSMC_WAITON (1 << 1)
|
||||
#define FSMC_ENABLE (1 << 2)
|
||||
#define FSMC_DEVTYPE_NAND (1 << 3)
|
||||
#define FSMC_DEVWID_8 (0 << 4)
|
||||
#define FSMC_DEVWID_16 (1 << 4)
|
||||
#define FSMC_ECCEN (1 << 6)
|
||||
#define FSMC_ECCPLEN_512 (0 << 7)
|
||||
#define FSMC_ECCPLEN_256 (1 << 7)
|
||||
#define FSMC_TCLR_1 (1 << 9)
|
||||
#define FSMC_TAR_1 (1 << 13)
|
||||
|
||||
/* sts register definitions */
|
||||
#define FSMC_CODE_RDY (1 << 15)
|
||||
|
||||
/* comm register definitions */
|
||||
#define FSMC_TSET_0 (0 << 0)
|
||||
#define FSMC_TWAIT_6 (6 << 8)
|
||||
#define FSMC_THOLD_4 (4 << 16)
|
||||
#define FSMC_THIZ_1 (1 << 24)
|
||||
|
||||
/* peripid2 register definitions */
|
||||
#define FSMC_REVISION_MSK (0xf)
|
||||
#define FSMC_REVISION_SHFT (0x4)
|
||||
|
||||
#define FSMC_VER1 1
|
||||
#define FSMC_VER2 2
|
||||
#define FSMC_VER3 3
|
||||
#define FSMC_VER4 4
|
||||
#define FSMC_VER5 5
|
||||
#define FSMC_VER6 6
|
||||
#define FSMC_VER7 7
|
||||
#define FSMC_VER8 8
|
||||
|
||||
static inline uint32_t get_fsmc_version(struct fsmc_regs *regs)
|
||||
{
|
||||
return (readl(®s->peripid2) >> FSMC_REVISION_SHFT) &
|
||||
FSMC_REVISION_MSK;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 13 bytes of ecc for every 512 byte block in FSMC version 8
|
||||
* and it has to be read consecutively and immediately after the 512
|
||||
* byte data block for hardware to generate the error bit offsets
|
||||
* Managing the ecc bytes in the following way is easier. This way is
|
||||
* similar to oobfree structure maintained already in u-boot nand driver
|
||||
*/
|
||||
#define MAX_ECCPLACE_ENTRIES 32
|
||||
|
||||
struct fsmc_nand_eccplace {
|
||||
uint8_t offset;
|
||||
uint8_t length;
|
||||
};
|
||||
|
||||
struct fsmc_eccplace {
|
||||
struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES];
|
||||
};
|
||||
|
||||
/**
|
||||
* fsmc_nand_platform_data - platform specific NAND controller config
|
||||
* @partitions: partition table for the platform, use a default fallback
|
||||
* if this is NULL
|
||||
* @nr_partitions: the number of partitions in the previous entry
|
||||
* @options: different options for the driver
|
||||
* @width: bus width
|
||||
* @bank: default bank
|
||||
* @select_bank: callback to select a certain bank, this is
|
||||
* platform-specific. If the controller only supports one bank
|
||||
* this may be set to NULL
|
||||
*/
|
||||
struct fsmc_nand_platform_data {
|
||||
struct mtd_partition *partitions;
|
||||
unsigned int nr_partitions;
|
||||
unsigned int options;
|
||||
unsigned int width;
|
||||
unsigned int bank;
|
||||
void (*select_bank)(uint32_t bank, uint32_t busw);
|
||||
};
|
||||
|
||||
extern int __init fsmc_nor_init(struct platform_device *pdev,
|
||||
unsigned long base, uint32_t bank, uint32_t width);
|
||||
extern void __init fsmc_init_board_info(struct platform_device *pdev,
|
||||
struct mtd_partition *partitions, unsigned int nr_partitions,
|
||||
unsigned int width);
|
||||
|
||||
#endif /* __MTD_FSMC_H */
|
@ -37,14 +37,14 @@ struct INFTLrecord {
|
||||
__u16 firstEUN;
|
||||
__u16 lastEUN;
|
||||
__u16 numfreeEUNs;
|
||||
__u16 LastFreeEUN; /* To speed up finding a free EUN */
|
||||
__u16 LastFreeEUN; /* To speed up finding a free EUN */
|
||||
int head,sect,cyl;
|
||||
__u16 *PUtable; /* Physical Unit Table */
|
||||
__u16 *VUtable; /* Virtual Unit Table */
|
||||
unsigned int nb_blocks; /* number of physical blocks */
|
||||
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
|
||||
struct erase_info instr;
|
||||
struct nand_ecclayout oobinfo;
|
||||
__u16 *PUtable; /* Physical Unit Table */
|
||||
__u16 *VUtable; /* Virtual Unit Table */
|
||||
unsigned int nb_blocks; /* number of physical blocks */
|
||||
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
|
||||
struct erase_info instr;
|
||||
struct nand_ecclayout oobinfo;
|
||||
};
|
||||
|
||||
int INFTL_mount(struct INFTLrecord *s);
|
||||
|
@ -110,6 +110,21 @@ struct mtd_oob_ops {
|
||||
uint8_t *oobbuf;
|
||||
};
|
||||
|
||||
#define MTD_MAX_OOBFREE_ENTRIES_LARGE 32
|
||||
#define MTD_MAX_ECCPOS_ENTRIES_LARGE 448
|
||||
/*
|
||||
* Internal ECC layout control structure. For historical reasons, there is a
|
||||
* similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
|
||||
* for export to user-space via the ECCGETLAYOUT ioctl.
|
||||
* nand_ecclayout should be expandable in the future simply by the above macros.
|
||||
*/
|
||||
struct nand_ecclayout {
|
||||
__u32 eccbytes;
|
||||
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
|
||||
__u32 oobavail;
|
||||
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
|
||||
};
|
||||
|
||||
struct mtd_info {
|
||||
u_char type;
|
||||
uint32_t flags;
|
||||
|
@ -27,15 +27,17 @@
|
||||
struct mtd_info;
|
||||
struct nand_flash_dev;
|
||||
/* Scan and identify a NAND device */
|
||||
extern int nand_scan (struct mtd_info *mtd, int max_chips);
|
||||
/* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type */
|
||||
extern int nand_scan(struct mtd_info *mtd, int max_chips);
|
||||
/*
|
||||
* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type.
|
||||
*/
|
||||
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
|
||||
struct nand_flash_dev *table);
|
||||
extern int nand_scan_tail(struct mtd_info *mtd);
|
||||
|
||||
/* Free resources held by the NAND device */
|
||||
extern void nand_release (struct mtd_info *mtd);
|
||||
extern void nand_release(struct mtd_info *mtd);
|
||||
|
||||
/* Internal helper for board drivers which need to override command function */
|
||||
extern void nand_wait_ready(struct mtd_info *mtd);
|
||||
@ -49,12 +51,13 @@ 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
|
||||
/*
|
||||
* 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 256
|
||||
#define NAND_MAX_PAGESIZE 4096
|
||||
#define NAND_MAX_OOBSIZE 576
|
||||
#define NAND_MAX_PAGESIZE 8192
|
||||
|
||||
/*
|
||||
* Constants for hardware specific CLE/ALE/NCE function
|
||||
@ -88,6 +91,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
#define NAND_CMD_RNDIN 0x85
|
||||
#define NAND_CMD_READID 0x90
|
||||
#define NAND_CMD_ERASE2 0xd0
|
||||
#define NAND_CMD_PARAM 0xec
|
||||
#define NAND_CMD_RESET 0xff
|
||||
|
||||
#define NAND_CMD_LOCK 0x2a
|
||||
@ -152,9 +156,10 @@ typedef enum {
|
||||
#define NAND_GET_DEVICE 0x80
|
||||
|
||||
|
||||
/* Option constants for bizarre disfunctionality and real
|
||||
* features
|
||||
*/
|
||||
/*
|
||||
* Option constants for bizarre disfunctionality and real
|
||||
* features.
|
||||
*/
|
||||
/* Chip can not auto increment pages */
|
||||
#define NAND_NO_AUTOINCR 0x00000001
|
||||
/* Buswitdh is 16 bit */
|
||||
@ -165,19 +170,27 @@ typedef enum {
|
||||
#define NAND_CACHEPRG 0x00000008
|
||||
/* Chip has copy back function */
|
||||
#define NAND_COPYBACK 0x00000010
|
||||
/* AND Chip which has 4 banks and a confusing page / block
|
||||
* assignment. See Renesas datasheet for further information */
|
||||
/*
|
||||
* AND Chip which has 4 banks and a confusing page / block
|
||||
* assignment. See Renesas datasheet for further information.
|
||||
*/
|
||||
#define NAND_IS_AND 0x00000020
|
||||
/* Chip has a array of 4 pages which can be read without
|
||||
* additional ready /busy waits */
|
||||
/*
|
||||
* Chip has a array of 4 pages which can be read without
|
||||
* additional ready /busy waits.
|
||||
*/
|
||||
#define NAND_4PAGE_ARRAY 0x00000040
|
||||
/* Chip requires that BBT is periodically rewritten to prevent
|
||||
/*
|
||||
* Chip requires that BBT is periodically rewritten to prevent
|
||||
* bits from adjacent blocks from 'leaking' in altering data.
|
||||
* This happens with the Renesas AG-AND chips, possibly others. */
|
||||
* This happens with the Renesas AG-AND chips, possibly others.
|
||||
*/
|
||||
#define BBT_AUTO_REFRESH 0x00000080
|
||||
/* Chip does not require ready check on read. True
|
||||
/*
|
||||
* Chip does not require ready check on read. True
|
||||
* for all large page devices, as they do not support
|
||||
* autoincrement.*/
|
||||
* autoincrement.
|
||||
*/
|
||||
#define NAND_NO_READRDY 0x00000100
|
||||
/* Chip does not allow subpage writes */
|
||||
#define NAND_NO_SUBPAGE_WRITE 0x00000200
|
||||
@ -205,16 +218,27 @@ typedef enum {
|
||||
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
|
||||
|
||||
/* Non chip related options */
|
||||
/* Use a flash based bad block table. This option is passed to the
|
||||
* default bad block table function. */
|
||||
/*
|
||||
* Use a flash based bad block table. OOB identifier is saved in OOB area.
|
||||
* This option is passed to the default bad block table function.
|
||||
*/
|
||||
#define NAND_USE_FLASH_BBT 0x00010000
|
||||
/* This option skips the bbt scan during initialization. */
|
||||
#define NAND_SKIP_BBTSCAN 0x00020000
|
||||
/* This option is defined if the board driver allocates its own buffers
|
||||
(e.g. because it needs them DMA-coherent */
|
||||
/*
|
||||
* This option is defined if the board driver allocates its own buffers
|
||||
* (e.g. because it needs them DMA-coherent).
|
||||
*/
|
||||
#define NAND_OWN_BUFFERS 0x00040000
|
||||
/* Chip may not exist, so silence any errors in scan */
|
||||
#define NAND_SCAN_SILENT_NODEV 0x00080000
|
||||
/*
|
||||
* If passed additionally to NAND_USE_FLASH_BBT then BBT code will not touch
|
||||
* the OOB area.
|
||||
*/
|
||||
#define NAND_USE_FLASH_BBT_NO_OOB 0x00100000
|
||||
/* Create an empty BBT with no vendor information if the BBT is available */
|
||||
#define NAND_CREATE_EMPTY_BBT 0x00200000
|
||||
|
||||
/* Options set by nand scan */
|
||||
/* Nand scan has allocated controller struct */
|
||||
@ -227,15 +251,80 @@ typedef enum {
|
||||
/* Keep gcc happy */
|
||||
struct nand_chip;
|
||||
|
||||
struct nand_onfi_params {
|
||||
/* rev info and features block */
|
||||
/* 'O' 'N' 'F' 'I' */
|
||||
u8 sig[4];
|
||||
__le16 revision;
|
||||
__le16 features;
|
||||
__le16 opt_cmd;
|
||||
u8 reserved[22];
|
||||
|
||||
/* manufacturer information block */
|
||||
char manufacturer[12];
|
||||
char model[20];
|
||||
u8 jedec_id;
|
||||
__le16 date_code;
|
||||
u8 reserved2[13];
|
||||
|
||||
/* memory organization block */
|
||||
__le32 byte_per_page;
|
||||
__le16 spare_bytes_per_page;
|
||||
__le32 data_bytes_per_ppage;
|
||||
__le16 spare_bytes_per_ppage;
|
||||
__le32 pages_per_block;
|
||||
__le32 blocks_per_lun;
|
||||
u8 lun_count;
|
||||
u8 addr_cycles;
|
||||
u8 bits_per_cell;
|
||||
__le16 bb_per_lun;
|
||||
__le16 block_endurance;
|
||||
u8 guaranteed_good_blocks;
|
||||
__le16 guaranteed_block_endurance;
|
||||
u8 programs_per_page;
|
||||
u8 ppage_attr;
|
||||
u8 ecc_bits;
|
||||
u8 interleaved_bits;
|
||||
u8 interleaved_ops;
|
||||
u8 reserved3[13];
|
||||
|
||||
/* electrical parameter block */
|
||||
u8 io_pin_capacitance_max;
|
||||
__le16 async_timing_mode;
|
||||
__le16 program_cache_timing_mode;
|
||||
__le16 t_prog;
|
||||
__le16 t_bers;
|
||||
__le16 t_r;
|
||||
__le16 t_ccs;
|
||||
__le16 src_sync_timing_mode;
|
||||
__le16 src_ssync_features;
|
||||
__le16 clk_pin_capacitance_typ;
|
||||
__le16 io_pin_capacitance_typ;
|
||||
__le16 input_pin_capacitance_typ;
|
||||
u8 input_pin_capacitance_max;
|
||||
u8 driver_strenght_support;
|
||||
__le16 t_int_r;
|
||||
__le16 t_ald;
|
||||
u8 reserved4[7];
|
||||
|
||||
/* vendor */
|
||||
u8 reserved5[90];
|
||||
|
||||
__le16 crc;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define ONFI_CRC_BASE 0x4F4E
|
||||
|
||||
/**
|
||||
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
|
||||
* @lock: protection lock
|
||||
* @active: the mtd device which holds the controller currently
|
||||
* @wq: wait queue to sleep on if a NAND operation is in progress
|
||||
* used instead of the per chip wait queue when a hw controller is available
|
||||
* @wq: wait queue to sleep on if a NAND operation is in
|
||||
* progress used instead of the per chip wait queue
|
||||
* when a hw controller is available.
|
||||
*/
|
||||
struct nand_hw_control {
|
||||
spinlock_t lock;
|
||||
spinlock_t lock;
|
||||
struct nand_chip *active;
|
||||
wait_queue_head_t wq;
|
||||
};
|
||||
@ -256,51 +345,42 @@ struct nand_hw_control {
|
||||
* @correct: function for ecc correction, matching to ecc generator (sw/hw)
|
||||
* @read_page_raw: function to read a raw page without ECC
|
||||
* @write_page_raw: function to write a raw page without ECC
|
||||
* @read_page: function to read a page according to the ecc generator requirements
|
||||
* @read_page: function to read a page according to the ecc generator
|
||||
* requirements.
|
||||
* @read_subpage: function to read parts of the page covered by ECC.
|
||||
* @write_page: function to write a page according to the ecc generator requirements
|
||||
* @write_page: function to write a page according to the ecc generator
|
||||
* requirements.
|
||||
* @read_oob: function to read chip OOB data
|
||||
* @write_oob: function to write chip OOB data
|
||||
*/
|
||||
struct nand_ecc_ctrl {
|
||||
nand_ecc_modes_t mode;
|
||||
int steps;
|
||||
int size;
|
||||
int bytes;
|
||||
int total;
|
||||
int prepad;
|
||||
int postpad;
|
||||
nand_ecc_modes_t mode;
|
||||
int steps;
|
||||
int size;
|
||||
int bytes;
|
||||
int total;
|
||||
int prepad;
|
||||
int postpad;
|
||||
struct nand_ecclayout *layout;
|
||||
void (*hwctl)(struct mtd_info *mtd, int mode);
|
||||
int (*calculate)(struct mtd_info *mtd,
|
||||
const uint8_t *dat,
|
||||
uint8_t *ecc_code);
|
||||
int (*correct)(struct mtd_info *mtd, uint8_t *dat,
|
||||
uint8_t *read_ecc,
|
||||
uint8_t *calc_ecc);
|
||||
int (*read_page_raw)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf, int page);
|
||||
void (*write_page_raw)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
int (*read_page)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf, int page);
|
||||
int (*read_subpage)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len,
|
||||
uint8_t *buf);
|
||||
void (*write_page)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
int (*read_oob)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
int page,
|
||||
int sndcmd);
|
||||
int (*write_oob)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
int page);
|
||||
void (*hwctl)(struct mtd_info *mtd, int mode);
|
||||
int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
|
||||
uint8_t *ecc_code);
|
||||
int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
|
||||
uint8_t *calc_ecc);
|
||||
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page);
|
||||
void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page);
|
||||
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len, uint8_t *buf);
|
||||
void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page,
|
||||
int sndcmd);
|
||||
int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -320,102 +400,132 @@ struct nand_buffers {
|
||||
|
||||
/**
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
|
||||
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
|
||||
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
|
||||
* flash device
|
||||
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
|
||||
* flash device.
|
||||
* @read_byte: [REPLACEABLE] read one byte from the chip
|
||||
* @read_word: [REPLACEABLE] read one word from the chip
|
||||
* @write_buf: [REPLACEABLE] write data from the buffer to the chip
|
||||
* @read_buf: [REPLACEABLE] read data from the chip into the buffer
|
||||
* @verify_buf: [REPLACEABLE] verify buffer contents against the chip data
|
||||
* @verify_buf: [REPLACEABLE] verify buffer contents against the chip
|
||||
* data.
|
||||
* @select_chip: [REPLACEABLE] select chip nr
|
||||
* @block_bad: [REPLACEABLE] check, if the block is bad
|
||||
* @block_markbad: [REPLACEABLE] mark the block bad
|
||||
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific funtion for controlling
|
||||
* ALE/CLE/nCE. Also used to write command and address
|
||||
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
|
||||
* If set to NULL no access to ready/busy is available and the ready/busy information
|
||||
* is read from the chip status register
|
||||
* @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip
|
||||
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready
|
||||
* @init_size: [BOARDSPECIFIC] hardwarespecific funtion for setting
|
||||
* mtd->oobsize, mtd->writesize and so on.
|
||||
* @id_data contains the 8 bytes values of NAND_CMD_READID.
|
||||
* Return with the bus width.
|
||||
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing
|
||||
* device ready/busy line. If set to NULL no access to
|
||||
* ready/busy is available and the ready/busy information
|
||||
* is read from the chip status register.
|
||||
* @cmdfunc: [REPLACEABLE] hardwarespecific function for writing
|
||||
* commands to the chip.
|
||||
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on
|
||||
* ready.
|
||||
* @ecc: [BOARDSPECIFIC] ecc control ctructure
|
||||
* @buffers: buffer structure for read/write
|
||||
* @hwcontrol: platform-specific hardware control structure
|
||||
* @ops: oob operation operands
|
||||
* @erase_cmd: [INTERN] erase command write function, selectable due to AND support
|
||||
* @erase_cmd: [INTERN] erase command write function, selectable due
|
||||
* to AND support.
|
||||
* @scan_bbt: [REPLACEABLE] function to scan bad block table
|
||||
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
|
||||
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering
|
||||
* data from array to read regs (tR).
|
||||
* @state: [INTERN] the current state of the NAND device
|
||||
* @oob_poi: poison value buffer
|
||||
* @page_shift: [INTERN] number of address bits in a page (column address bits)
|
||||
* @page_shift: [INTERN] number of address bits in a page (column
|
||||
* address bits).
|
||||
* @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
|
||||
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
|
||||
* @chip_shift: [INTERN] number of address bits in one chip
|
||||
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
|
||||
* special functionality. See the defines for further explanation
|
||||
* @badblockpos: [INTERN] position of the bad block marker in the oob area
|
||||
* @options: [BOARDSPECIFIC] various chip options. They can partly
|
||||
* be set to inform nand_scan about special functionality.
|
||||
* See the defines for further explanation.
|
||||
* @badblockpos: [INTERN] position of the bad block marker in the oob
|
||||
* area.
|
||||
* @cellinfo: [INTERN] MLC/multichip data from chip ident
|
||||
* @numchips: [INTERN] number of physical chips
|
||||
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
||||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||
* @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
|
||||
* @pagebuf: [INTERN] holds the pagenumber which is currently in
|
||||
* data_buf.
|
||||
* @subpagesize: [INTERN] holds the subpagesize
|
||||
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
|
||||
* non 0 if ONFI supported.
|
||||
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
|
||||
* supported, 0 otherwise.
|
||||
* @ecclayout: [REPLACEABLE] the default ecc placement scheme
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
||||
* lookup.
|
||||
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
|
||||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
|
||||
* @controller: [REPLACEABLE] a pointer to a hardware controller structure
|
||||
* which is shared among multiple independend devices
|
||||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
|
||||
* bad block scan.
|
||||
* @controller: [REPLACEABLE] a pointer to a hardware controller
|
||||
* structure which is shared among multiple independend
|
||||
* devices.
|
||||
* @priv: [OPTIONAL] pointer to private chip date
|
||||
* @errstat: [OPTIONAL] hardware specific function to perform additional error status checks
|
||||
* (determine if errors are correctable)
|
||||
* @errstat: [OPTIONAL] hardware specific function to perform
|
||||
* additional error status checks (determine if errors are
|
||||
* correctable).
|
||||
* @write_page: [REPLACEABLE] High-level page write function
|
||||
*/
|
||||
|
||||
struct nand_chip {
|
||||
void __iomem *IO_ADDR_R;
|
||||
void __iomem *IO_ADDR_W;
|
||||
void __iomem *IO_ADDR_R;
|
||||
void __iomem *IO_ADDR_W;
|
||||
|
||||
uint8_t (*read_byte)(struct mtd_info *mtd);
|
||||
u16 (*read_word)(struct mtd_info *mtd);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
|
||||
unsigned int ctrl);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
|
||||
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
|
||||
void (*erase_cmd)(struct mtd_info *mtd, int page);
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached, int raw);
|
||||
uint8_t (*read_byte)(struct mtd_info *mtd);
|
||||
u16 (*read_word)(struct mtd_info *mtd);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
|
||||
u8 *id_data);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
|
||||
int page_addr);
|
||||
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
|
||||
void (*erase_cmd)(struct mtd_info *mtd, int page);
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||
int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached, int raw);
|
||||
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
|
||||
int page_shift;
|
||||
int phys_erase_shift;
|
||||
int bbt_erase_shift;
|
||||
int chip_shift;
|
||||
int numchips;
|
||||
uint64_t chipsize;
|
||||
int pagemask;
|
||||
int pagebuf;
|
||||
int subpagesize;
|
||||
uint8_t cellinfo;
|
||||
int badblockpos;
|
||||
int badblockbits;
|
||||
int page_shift;
|
||||
int phys_erase_shift;
|
||||
int bbt_erase_shift;
|
||||
int chip_shift;
|
||||
int numchips;
|
||||
uint64_t chipsize;
|
||||
int pagemask;
|
||||
int pagebuf;
|
||||
int subpagesize;
|
||||
uint8_t cellinfo;
|
||||
int badblockpos;
|
||||
int badblockbits;
|
||||
|
||||
flstate_t state;
|
||||
int onfi_version;
|
||||
struct nand_onfi_params onfi_params;
|
||||
|
||||
uint8_t *oob_poi;
|
||||
struct nand_hw_control *controller;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
flstate_t state;
|
||||
|
||||
uint8_t *oob_poi;
|
||||
struct nand_hw_control *controller;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
|
||||
struct nand_ecc_ctrl ecc;
|
||||
struct nand_buffers *buffers;
|
||||
@ -423,13 +533,13 @@ struct nand_chip {
|
||||
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
uint8_t *bbt;
|
||||
struct nand_bbt_descr *bbt_td;
|
||||
struct nand_bbt_descr *bbt_md;
|
||||
uint8_t *bbt;
|
||||
struct nand_bbt_descr *bbt_td;
|
||||
struct nand_bbt_descr *bbt_md;
|
||||
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
|
||||
void *priv;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -473,7 +583,7 @@ struct nand_flash_dev {
|
||||
*/
|
||||
struct nand_manufacturers {
|
||||
int id;
|
||||
char * name;
|
||||
char *name;
|
||||
};
|
||||
|
||||
extern struct nand_flash_dev nand_flash_ids[];
|
||||
@ -486,7 +596,7 @@ extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
|
||||
extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, uint8_t * buf);
|
||||
size_t *retlen, uint8_t *buf);
|
||||
|
||||
/**
|
||||
* struct platform_nand_chip - chip level device structure
|
||||
@ -502,17 +612,16 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
* @priv: hardware controller specific settings
|
||||
*/
|
||||
struct platform_nand_chip {
|
||||
int nr_chips;
|
||||
int chip_offset;
|
||||
int nr_partitions;
|
||||
struct mtd_partition *partitions;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
const char **part_probe_types;
|
||||
void (*set_parts)(uint64_t size,
|
||||
struct platform_nand_chip *chip);
|
||||
void *priv;
|
||||
int nr_chips;
|
||||
int chip_offset;
|
||||
int nr_partitions;
|
||||
struct mtd_partition *partitions;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
const char **part_probe_types;
|
||||
void (*set_parts)(uint64_t size, struct platform_nand_chip *chip);
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* Keep gcc happy */
|
||||
@ -534,18 +643,15 @@ struct platform_device;
|
||||
* All fields are optional and depend on the hardware driver requirements
|
||||
*/
|
||||
struct platform_nand_ctrl {
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
void (*remove)(struct platform_device *pdev);
|
||||
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
|
||||
unsigned int ctrl);
|
||||
void (*write_buf)(struct mtd_info *mtd,
|
||||
const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd,
|
||||
uint8_t *buf, int len);
|
||||
void *priv;
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
void (*remove)(struct platform_device *pdev);
|
||||
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -554,8 +660,8 @@ struct platform_nand_ctrl {
|
||||
* @ctrl: controller level device structure
|
||||
*/
|
||||
struct platform_nand_data {
|
||||
struct platform_nand_chip chip;
|
||||
struct platform_nand_ctrl ctrl;
|
||||
struct platform_nand_chip chip;
|
||||
struct platform_nand_ctrl ctrl;
|
||||
};
|
||||
|
||||
/* Some helpers to access the data structures */
|
||||
|
@ -39,7 +39,7 @@ struct mtd_partition {
|
||||
uint64_t size; /* partition size */
|
||||
uint64_t offset; /* offset within the master MTD space */
|
||||
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
|
||||
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
|
||||
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */
|
||||
};
|
||||
|
||||
#define MTDPART_OFS_NXTBLK (-2)
|
||||
@ -89,4 +89,9 @@ static inline int mtd_has_cmdlinepart(void) { return 1; }
|
||||
static inline int mtd_has_cmdlinepart(void) { return 0; }
|
||||
#endif
|
||||
|
||||
int mtd_is_master(struct mtd_info *mtd);
|
||||
int mtd_add_partition(struct mtd_info *master, char *name,
|
||||
long long offset, long long length);
|
||||
int mtd_del_partition(struct mtd_info *master, int partno);
|
||||
|
||||
#endif
|
||||
|
@ -52,6 +52,7 @@ struct mtd_oob_buf64 {
|
||||
#define MTD_NANDFLASH 4
|
||||
#define MTD_DATAFLASH 6
|
||||
#define MTD_UBIVOLUME 7
|
||||
#define MTD_MLCNANDFLASH 8
|
||||
|
||||
#define MTD_WRITEABLE 0x400 /* Device is writeable */
|
||||
#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
|
||||
@ -119,7 +120,7 @@ struct otp_info {
|
||||
#define OTPGETREGIONCOUNT _IOW('M', 14, int)
|
||||
#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
|
||||
#define OTPLOCK _IOR('M', 16, struct otp_info)
|
||||
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
|
||||
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout_user)
|
||||
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
|
||||
#define MTDFILEMODE _IO('M', 19)
|
||||
#define MEMERASE64 _IOW('M', 20, struct erase_info_user64)
|
||||
@ -144,13 +145,18 @@ struct nand_oobfree {
|
||||
};
|
||||
|
||||
#define MTD_MAX_OOBFREE_ENTRIES 8
|
||||
#define MTD_MAX_ECCPOS_ENTRIES 64
|
||||
/*
|
||||
* ECC layout control structure. Exported to userspace for
|
||||
* diagnosis and to allow creation of raw images
|
||||
* OBSOLETE: ECC layout control structure. Exported to user-space via ioctl
|
||||
* ECCGETLAYOUT for backwards compatbility and should not be mistaken as a
|
||||
* complete set of ECC information. The ioctl truncates the larger internal
|
||||
* structure to retain binary compatibility with the static declaration of the
|
||||
* ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of
|
||||
* the user struct, not the MAX size of the internal struct nand_ecclayout.
|
||||
*/
|
||||
struct nand_ecclayout {
|
||||
struct nand_ecclayout_user {
|
||||
__u32 eccbytes;
|
||||
__u32 eccpos[64];
|
||||
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES];
|
||||
__u32 oobavail;
|
||||
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
|
||||
};
|
||||
|
@ -29,6 +29,6 @@ typedef struct mtd_info_user mtd_info_t;
|
||||
typedef struct erase_info_user erase_info_t;
|
||||
typedef struct region_info_user region_info_t;
|
||||
typedef struct nand_oobinfo nand_oobinfo_t;
|
||||
typedef struct nand_ecclayout nand_ecclayout_t;
|
||||
typedef struct nand_ecclayout_user nand_ecclayout_t;
|
||||
|
||||
#endif /* __MTD_USER_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user